# Drucken von Jtextpane mit Seitenangabe



## prince (10. Feb 2010)

Hallo,

ich habe ein JTextpane, das HTML enthält. Es kann mehrere A4-Seiten lang sein und auch bilder und Tabellen etc. enthalten.

Nun habe ich schon ein Beispiel ausprobiert, welches ich per Google fand:

```
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.font.LineMetrics;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.JEditorPane;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;
import javax.swing.text.View;
import javax.swing.text.html.HTMLDocument;

public class DocumentRenderer implements Printable 
{

    protected int currentPage = -1; // Used to keep track of when the page to print changes.

    protected JEditorPane jeditorPane; // Container to hold the
    int pwidth;
    int pheigth;
    double scale;
    // Document. This object will
    // be used to lay out the
    // Document for printing.

    protected double pageEndY = 0; // Location of the current page

    // end.

    protected double pageStartY = 0; // Location of the current page

    // start.

    protected boolean scaleWidthToFit = true; // boolean to allow control over

    // whether pages too wide to fit
    // on a page will be scaled.

    /*
     * The DocumentRenderer class uses pFormat and pJob in its methods. Note
     * that pFormat is not the variable name used by the print method of the
     * DocumentRenderer. Although it would always be expected to reference the
     * pFormat object, the print method gets its PageFormat as an argument.
     */
    protected PageFormat pFormat;

    protected PrinterJob pJob;
    private int totalNumPages;

    /*
     * The constructor initializes the pFormat and PJob variables.
     */
    public DocumentRenderer(String jobName)
    {
        pJob = PrinterJob.getPrinterJob();
        pJob.setJobName(jobName);
        pFormat=new FooterFormat();//pJob.defaultPage();
    }
    public boolean setupPageFormat()
    {
      PageFormat defaultPageFormat = pJob.defaultPage();
      pFormat = pJob.pageDialog(defaultPageFormat); //SeiteneinstellungsDialog
      pJob.setPrintable(this, pFormat);             //das zu Druckende und das zugehörige Format
      return (pFormat != defaultPageFormat);        //wenn sie ungleich sind, dann hat der Benutzer OK gedrückt!
    }

    /*
     * Method to get the current Document
     */
    public Document getDocument() 
    {
        if (jeditorPane != null)
            return jeditorPane.getDocument();
        else
            return null;
    }

    /*
     * Method to get the current choice the width scaling option.
     */
    public boolean getScaleWidthToFit()
    {
        return scaleWidthToFit;
    }

    /*
     * pageDialog() displays a page setup dialog.
     */
    public void pageDialog()
    {
        pFormat = pJob.pageDialog(pFormat);
    }

    /*
     * The print method implements the Printable interface. Although Printables
     * may be called to render a page more than once, each page is painted in
     * order. We may, therefore, keep track of changes in the page being
     * rendered by setting the currentPage variable to equal the pageIndex, and
     * then comparing these variables on subsequent calls to this method. When
     * the two variables match, it means that the page is being rendered for the
     * second or third time. When the currentPage differs from the pageIndex, a
     * new page is being requested.
     *
     * The highlights of the process used print a page are as follows:
     *
     * I. The Graphics object is cast to a Graphics2D object to allow for
     * scaling.
     II. The JEditorPane is laid out using the width of a printable
     * page. This will handle line breaks. If the JEditorPane cannot be sized at
     * the width of the graphics clip, scaling will be allowed.
     III. The root
     * view of the JEditorPane is obtained. By examining this root view and all
     * of its children, printView will be able to determine the location of each
     * printable element of the document.
     IV. If the scaleWidthToFit option is
     * chosen, a scaling ratio is determined, and the graphics2D object is
     * scaled.
     V. The Graphics2D object is clipped to the size of the printable
     * page.
     VI. currentPage is checked to see if this is a new page to render.
     * If so, pageStartY and pageEndY are reset.
     VII. To match the coordinates
     * of the printable clip of graphics2D and the allocation rectangle which
     * will be used to lay out the views, graphics2D is translated to begin at
     * the printable X and Y coordinates of the graphics clip.
     VIII. An
     * allocation Rectangle is created to represent the layout of the Views.
     *
     * The Printable Interface always prints the area indexed by reference to
     * the Graphics object. For instance, with a standard 8.5 x 11 inch page
     * with 1 inch margins the rectangle X = 72, Y = 72, Width = 468, and Height =
     * 648, the area 72, 72, 468, 648 will be painted regardless of which page
     * is actually being printed.
     *
     * To align the allocation Rectangle with the graphics2D object two things
     * are done. The first step is to translate the X and Y coordinates of the
     * graphics2D object to begin at the X and Y coordinates of the printable
     * clip, see step VII. Next, when printing other than the first page, the
     * allocation rectangle must start laying out in coordinates represented by
     * negative numbers. After page one, the beginning of the allocation is
     * started at minus the page end of the prior page. This moves the part
     * which has already been rendered to before the printable clip of the
     * graphics2D object.
     *
     * X. The printView method is called to paint the page. Its return value
     * will indicate if a page has been rendered.
     *
     * Although public, print should not ordinarily be called by programs other
     * than PrinterJob.
     */
    public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) 
    {
      scale=1.0;
      

        Graphics2D graphics2D;
        View rootView;
        FooterFormat pf=(FooterFormat)pageFormat;
        // I
        
        graphics2D = (Graphics2D) graphics;
        // II
        graphics2D.fill( new Rectangle2D.Float(0,0,800,600) );
        jeditorPane.setSize((int) pf.getImageableWidth(),Integer.MAX_VALUE);
        jeditorPane.validate();
        // III
        
        rootView = jeditorPane.getUI().getRootView(jeditorPane);
        // IV
        if ((scaleWidthToFit) && (jeditorPane.getMinimumSize().getWidth() > pf.getImageableWidth()))
        {
            scale = pf.getImageableWidth()/ (jeditorPane.getMinimumSize().getWidth());
            totalNumPages = (int) Math.ceil(scale * pheigth / pf.getImageableHeight()); //Seitenzahlen
            graphics.drawString("Seite "+ (pageIndex + 1)+" von "+totalNumPages, 280, 771);
            graphics2D.scale(scale, scale);
        }

        // V
        
        
        graphics2D.setClip((int) (pf.getImageableX() / scale),
                           (int) (pf.getImageableY() / scale),
                           (int) (pf.getImageableWidth() / scale),
                           (int) ((pf.getImageableHeight()+10) / scale));
        
        Shape s=graphics2D.getClip();
        Area a=new Area(s);
        
        // VI
        if (pageIndex > currentPage) 
        {
            currentPage = pageIndex;
            pageStartY += pageEndY;
            pageEndY = graphics2D.getClipBounds().getHeight();
        }
        // VII
        graphics2D.translate(graphics2D.getClipBounds().getX(),graphics2D.getClipBounds().getY());

        // VIII
        Rectangle allocation = new Rectangle(0, 
                                             (int) -pageStartY,
                                             (int) (jeditorPane.getMinimumSize().getWidth()),
                                             (int) (jeditorPane.getPreferredSize().getHeight()));
        // X

        if (printView(graphics2D, allocation, rootView)) 
        {
          try
          {
            FooterFormat formatPainter = pf;
            formatPainter.print(graphics, pf, pageIndex+1);
          }
          catch (ClassCastException exception)
          {}
         
          return Printable.PAGE_EXISTS;
        } 
        else 
        {
          pageStartY = 0;
          pageEndY = 0;
          currentPage = -1;
          return Printable.NO_SUCH_PAGE;
        }
    }

    /*
     * print(HTMLDocument) is called to set an HTMLDocument for printing.
     */
    public boolean print(HTMLDocument htmlDocument)
    {
      setDocument(htmlDocument);
      return printDialog();
    }

    /*
     * print(JEditorPane) prints a Document contained within a JEDitorPane.
     */
    public boolean print(JEditorPane jedPane)
    {
      setDocument(jedPane);
      return printDialog();
    }

    /*
     * print(PlainDocument) is called to set a PlainDocument for printing.
     */
    public boolean print(PlainDocument plainDocument)
    {
      setDocument(plainDocument);
      return printDialog();
    }

    /*
     * A protected method, printDialog(), displays the print dialog and
     * initiates printing in response to user input.
     */
    protected boolean printDialog()
    {
      if (pJob.printDialog()) 
      {
        pJob.setPrintable(this, pFormat);
        try 
        {
          pJob.print();
          return true;
        } 
        catch (PrinterException printerException) 
        {
          pageStartY = 0;
          pageEndY = 0;
          currentPage = -1;
          System.out.println("Error Printing Document");
          return false;
        }
      }
      else
        return false;
    }

    /*
     * printView is a recursive method which iterates through the tree structure
     * of the view sent to it. If the view sent to printView is a branch view,
     * that is one with children, the method calls itself on each of these
     * children. If the view is a leaf view, that is a view without children
     * which represents an actual piece of text to be painted, printView
     * attempts to render the view to the Graphics2D object.
     *
     * I. When any view starts after the beginning of the current printable
     * page, this means that there are pages to print and the method sets
     * pageExists to true. II. When a leaf view is taller than the printable
     * area of a page, it cannot, of course, be broken down to fit a single
     * page. Such a View will be printed whenever it intersects with the
     * Graphics2D clip. III. If a leaf view intersects the printable area of the
     * graphics clip and fits vertically within the printable area, it will be
     * rendered. IV. If a leaf view does not exceed the printable area of a page
     * but does not fit vertically within the Graphics2D clip of the current
     * page, the method records that this page should end at the start of the
     * view. This information is stored in pageEndY.
     */
    protected boolean printView(Graphics2D graphics2D, Shape allocation,View view)
    {
        boolean pageExists = false;
        Rectangle clipRectangle = graphics2D.getClipBounds();
        Shape childAllocation;
        View childView;

        if (view.getViewCount() > 0) {
            for (int i = 0; i < view.getViewCount(); i++) {
                childAllocation = view.getChildAllocation(i, allocation);
                if (childAllocation != null) {
                    childView = view.getView(i);
                    if (printView(graphics2D, childAllocation, childView)) {
                        pageExists = true;
                    }
                }
            }
        } else {
            // I
            if (allocation.getBounds().getMaxY() >= clipRectangle.getY()) {
                pageExists = true;
                // II
                if ((allocation.getBounds().getHeight() > clipRectangle
                        .getHeight())
                        && (allocation.intersects(clipRectangle))) {
                    view.paint(graphics2D, allocation);
                } else {
                    // III
                    if (allocation.getBounds().getY() >= clipRectangle.getY()) {
                        if (allocation.getBounds().getMaxY() <= clipRectangle.getMaxY()) {
                            view.paint(graphics2D, allocation);
                        } else {
                            // IV
                            if (allocation.getBounds().getY() < pageEndY) {
                                pageEndY = allocation.getBounds().getY();
                            }
                        }
                    }
                }
            }
        }
        return pageExists;
    }

    /*
     * Method to set the content type the JEditorPane.
     */
    protected void setContentType(String type)
    {
        jeditorPane.setContentType(type);
    }

    /*
     * Method to set an HTMLDocument as the Document to print.
     */
    public void setDocument(HTMLDocument htmlDocument)
    {
        jeditorPane = new JEditorPane();
        setDocument("text/html", htmlDocument);
    }

    /*
     * Method to set the Document to print as the one contained in a
     * JEditorPane. This method is useful when Java does not provide direct
     * access to a particular Document type, such as a Rich Text Format
     * document. With this method such a document can be sent to the
     * DocumentRenderer class enclosed in a JEditorPane.
     */
    public void setDocument(JEditorPane jedPane)
    {
        jeditorPane = new JEditorPane();
        pwidth=jedPane.getSize().width;
        pheigth=jedPane.getSize().height;
        setDocument(jedPane.getContentType(), jedPane.getDocument());
    }

    /*
     * Method to set a PlainDocument as the Document to print.
     */
    public void setDocument(PlainDocument plainDocument)
    {
        jeditorPane = new JEditorPane();
        setDocument("text/plain", plainDocument);
    }

    /*
     * Method to set the content type and document of the JEditorPane.
     */
    protected void setDocument(String type, Document document)
    {
        setContentType(type);
        jeditorPane.setDocument(document);
    }

    /*
     * Method to set the current choice of the width scaling option.
     */
    public void setScaleWidthToFit(boolean scaleWidth)
    {
        scaleWidthToFit = scaleWidth;
    }
public class FooterFormat extends PageFormat implements Printable
{
   /**
     * The font we use for the footer.
     */
    private final Font mFooterFont = new Font("Serif", Font.ITALIC, 15);
    /**
     * The amount of space at the bottom of the imageable area that we
     * reserve for the footer.
     */
    private static final float mFooterHeight = (float) (0.5 * 72);
    /**
     * Tell the caller that the imageable area of the paper is shorter
     * than it actually is. We use the extra room at the bottom of the
     * page for our footer text.
     */
    public double getImageableHeight()
    {
      double imageableHeight = super.getImageableHeight() - mFooterHeight;
      if (imageableHeight < 0)
        imageableHeight = 0;
      return imageableHeight;

    }
    /**
     * Draws the footer text which has the following format:
     * <date>
     */
    public int print(Graphics g, PageFormat format, int pageIndex) {

        /* Make a copy of the passed in Graphics instance so
         * that we do not upset the caller's current Graphics
         * settings such as the current color and font.
         */
        String sites="Seite " + pageIndex + " von " + totalNumPages;
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setPaint(Color.black);
        g2d.setFont(mFooterFont);
        LineMetrics metrics = mFooterFont.getLineMetrics(sites, g2d.getFontRenderContext());
        /* We will draw the footer at the bottom of the imageable
         * area. We subtract off the font's descent so that the bottoms
         * of descenders remain visable.
         */
        float y = (float) (super.getImageableY() + super.getImageableHeight()- metrics.getDescent() - metrics.getLeading());
        // Cast to an int because of printing bug in drawString(String, float, float)!

        g2d.drawString(sites, (int) super.getImageableX(), (int)(y*(1+scale-0.06)));
        g2d.dispose();
        
        return Printable.PAGE_EXISTS;


    }

}

}
```

mit der Klasse FooterFormat will ich die Seitenzahl hinzufügen, was soweit auch funktioniert. Das Problem ist nun, dass, alles was in JTextpane angezeigt wird ja einfach aufgeteilt wird auf die Seiten. Nun ist am unteren Rand kein Platz fü eine Seitennummerierung. (am Papier wäre schon platz nur ist das schon außerhalb vom printable Area)...

Wie schaffe ich es nun zur Graphics2d einen String hinzuzufügen, der auch gedruckt wird, wenn er außerhalb liegt. Oder gibt es eine möglichkeit diese Graphics2D pixelmäßig zu vergrößern?

Also das Ziel ist es eine Seitennummerierung einzufügen, die nahc dem Text steht und nicht im Text.

Habe schon sehr lange herumprobiert aber bin noch zu keiner Lösung gekommen.

Was hier Positiv ist, das der Seitenumbruch gut funktioniert und keine Tabellen oder Texte abgeschnitten werden, welche im JTextPane vorkommen. 

Danke für eure Antworten im Vorhinein!
mfg 
Prince


----------



## klein-odd (15. Mrz 2010)

Hey !

Mein Vorschlag :

Du generierst Dir im Programm eine int - Variable, die die nötige Seitennummer akkurat und aktuell beinhaltet,
beispielsweise SeitNr,initialisierst Du sie (zum Spass mit -57658)


```
int SeitNr=-57658
```

Die Variable wird beim Drucken jeder Seite neu berechnet.

Und dann bringst Du sie auf die aktuell gedruckte Seite mit drawString dorthin, wo sie hingehört.

Leider habe ich momentan Mühe, den Programmcode
im Ganzen zu sehen.

Mag sein, dass Du mir den Link zu den Internetquellen mit dem Code gibst ?
Ich habe wenig Ahnung, woher der Beispielcode stammt.

Auch wenn ich den ganzen Code haben kann, wird sich meine Idee nicht ändern,
nur ich kann einen konkreten Vorschlag  leichter erarbeiten

Wie man eine integer Zahl mit drawSring zeichnet, ist es schon für Dich leicht, oder ?


Viel Erfolg, klein-odd


----------

