如何使用Java和itext从Graphics对象创建包含多个页面的PDF

8

我有一个抽象类,其中包含一个抽象方法draw(Graphics2D g2),以及print()、showPreview()、printPDF()方法。对于我Java程序中的每个文档,我都会实现draw()方法,这样我就可以为每个文档打印、预览和创建PDF文件。 我的问题是如何从该Graphics对象创建具有多个页面的PDF。 我通过为每个页面创建一个PDF文件,然后将这些文件合并成一个新文件来解决它。但肯定有更好的方法。 以下是我用来创建单页PDF的代码:

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());
    }
}
2个回答

9
    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();

所以你正在将整个界面渲染N次,并在不同位置显示页面大小的切片。如果我没记错的话,在印刷世界中这被称为“条纹”。聪明,但在PDF中可以更有效率。

使用g2d将整个界面渲染成一个巨大的PdfTemplate,只需一次即可。然后将该模板绘制到所有页面上,使所需部分在当前页面的边距(“媒体框”)内可见。

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的坐标空间将0,0设置在左下角,当你向上和向右移动时,这些值会增加。PdfGraphics2D做了很多魔法来隐藏这种差异,但我们还是需要在这里处理一下...因此在边界框和绘图位置中出现了负坐标。

这都是“草稿纸”编码,我可能犯了一两个错误......但这就是思路。


哪些更改?PS:这是您单击正确答案旁边的空心复选标记的部分,让其他人知道谁是正确的(并给予该人一些声望)。在这种情况下,“我”是正确的。此外,如果您提出多个问题并从未将其标记为已回答,则其他人将看到您的低回答百分比,并且不太可能打扰您,因为他们知道即使他们是正确的,您也不会给他们声望。 - Mark Storer
抱歉,马克,我试图理解这个网站的工作原理。 我没有意识到PdfContentByte可以在每一页上重复使用。 我做出了以下更改: PdfTemplate template = cb.createTemplate(_pageWidth, entireHeight); g2 = template.createGraphics(_pageWidth, entireHeight); ..... 对于每一页(从第1页到第_numberOfPages页): cb.addTemplate(hugeTempl, 0, -_pageHeight * (_numberOfPages - curPg)); document.newPage(); - Jan
没关系。我会把你的“代码注释”归类为同类问题。欢迎来到SO。 “无知可治,愚蠢永存。”总是乐意帮助消除一些无知。 - Mark Storer
@MarkStorer 我正在使用您在此答案中提供的第二个代码片段。不知何故,我只得到了最后一页的内容。有什么解决办法吗? - MalTec
你只看到了最后一页反复出现,还是只有最后一页有内容? - Mark Storer

0

我在跟随上面的代码时遇到了一些问题(当前的itextpdf版本中似乎有一些方法已经更改)。这是我的解决方案:

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();
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接