Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a PDF with multiple pages from a Graphics object with Java and itext

Tags:

java

pdf

itext

I have an abstract class with an abstract method draw(Graphics2D g2), and the methods print(), showPreview(), printPDF(). For each document in my Java program, I implement draw(), so I can print, show preview and create a PDF file for each document. My problem is how to create a PDF with multiple pages from that Graphics object. I solved it by creating a PDF file for each page, and then merge the files into one new file. But there must be a better way. I have following code to create PDF with one page:

public void printPDF1(){
    JFileChooser dialog = new JFileChooser();
    String filePath = "";
    int dialogResult = dialog.showSaveDialog(null);
    if (dialogResult==JFileChooser.APPROVE_OPTION){
        filePath = dialog.getSelectedFile().getPath();
    }
    else return;
    try {
        Document document = new Document(new Rectangle(_pageWidth, _pageHeight));
        PdfWriter writer = PdfWriter.getInstance(document,
                new FileOutputStream(filePath));
        document.open();

        PdfContentByte cb = writer.getDirectContent();
        g2 = cb.createGraphics(_pageWidth, _height);
        g2.translate(0, (_numberOfPages - _pageNumber) * _pageHeight);
        draw(g2);
        g2.dispose();
        document.close();
    } 
    catch (Exception e2) {
        System.out.println(e2.getMessage());
    }
}
like image 666
Jan Avatar asked Mar 13 '11 15:03

Jan


People also ask

What is PdfWriter Java?

The PdfWriter class represents the Doc Writer for a PDF. This class belongs to the package com. itextpdf. kernel.


1 Answers

    document.open();

    // the same contentByte is returned, it's just flushed & reset during
    // new page events.
    PdfContentByte cb = writer.getDirectContent();

    for (int _pageNumber = 0; _pageNumber < _numberofPages; ++_numberOfPages) {
      /*******************/
      //harmless in first pass, *necessary* in others
      document.newPage(); 
      /*******************/

      g2 = cb.createGraphics(_pageWidth, _height);
      g2.translate(0, (_numberOfPages - _pageNumber) * _pageHeight);
      draw(g2);
      g2.dispose();
    }

    document.close();

So you're rendering your entire interface N times, and only showing a page-sized slice of it in different locations. That's called "striping" in print-world IIRC. Clever, but it could be more efficient in PDF.

Render your entire interface into one huge PdfTemplate (with g2d), once. Then draw that template into all your pages such that the portion you want is visible inside the current page's margins ("media box").

PdfContentByte cb = writer.getDirectContent();
float entireHeight = _numberOfPages * _pageHeight;
PdfTemplate hugeTempl = cb.createTemplate( 0, -entireHeight, pageWidth, _pageHeight );
g2 = hugeTempl.createGraphics(0, -entireHeight, _pageWidth, _pageHeight ); 
draw(g2);
g2.dispose();

for (int curPg = 0; curPg < _numberOfPages; ++curPg) {
  cb.addTemplateSimple( hugeTempl, 0, -_pageHeight * curPg );

  document.newPage();
}

PDF's coordinate space sets 0,0 in the lower left corner, and those values increase as you go up and to the right. PdfGraphis2D does a fair amount of magic to hide that difference from you, but we still have to deal with it a bit here... thus the negative coordinates in the bounding box and drawing locations.

This is all "back of the napkin" coding, and it's entirely possible I've made a mistake or two in there... but that's the idea.

like image 60
Mark Storer Avatar answered Sep 23 '22 19:09

Mark Storer