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());
}
}
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.
I was having trouble following the above code (some of the methods appear to have changed in the current itextpdf version). Here's my solution:
import com.itextpdf.awt.PdfGraphics2D;
import com.itextpdf.text.Document;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.WindowEvent;
import java.io.FileOutputStream;
import java.io.OutputStream;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout;
public class PanelToPDF {
private static JFrame frame= new JFrame();
private static JPanel view= new JPanel();
private static float pageWidth= PageSize.A4.getWidth();
private static float pageHeight= PageSize.A4.getHeight();
public static void main(String[] args) throws Exception {
System.out.println("Page width = " + pageWidth + ", height = " + pageHeight);
initPane();
createMultipagePDF();
frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
}
private static void initPane() {
view.setLayout(new MigLayout());
view.setBackground(Color.WHITE);
for (int i= 1; i <= 160; ++i) {
JLabel label= new JLabel("This is a test! " + i);
label.setForeground(Color.BLACK);
view.add(label, "wrap");
JPanel subPanel= new JPanel();
subPanel.setBackground(Color.RED);
view.add(subPanel);
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(new Dimension(Math.round(pageWidth), Math.round(pageHeight)));
frame.add(view);
frame.setVisible(true);
}
private static void createMultipagePDF() throws Exception {
// Calculate the number of pages required. Use the preferred size to get
// the entire panel height, rather than the panel height within the JFrame
int numPages= (int) Math.ceil(view.getPreferredSize().height / pageHeight); // int divided by float
// Output to PDF
OutputStream os= new FileOutputStream("test.pdf");
Document doc= new Document();
PdfWriter writer= PdfWriter.getInstance(doc, os);
doc.open();
PdfContentByte cb= writer.getDirectContent();
// Iterate over pages here
for (int currentPage= 0; currentPage < numPages; ++currentPage) {
doc.newPage(); // not needed for page 1, needed for >1
PdfTemplate template= cb.createTemplate(pageWidth, pageHeight);
Graphics2D g2d= new PdfGraphics2D(template, pageWidth, pageHeight * (currentPage + 1));
view.printAll(g2d);
g2d.dispose();
cb.addTemplate(template, 0, 0);
}
doc.close();
}