首页 > 解决方案 > 如何使用 iText 7 将源 PDF 内容添加到目标 PDF 而不会丢失页眉和页脚?

问题描述

我正在使用 iText 7。我有两个 PDF 文件。源 PDF 有一些内容。目标 PDF 有页眉和页脚。我需要将源 PDF 中的内容添加到页面中间的目标 PDF,而不会重叠目标 PDF 的页眉和页脚。代码应该是什么?

下面是我的代码,附件是源 PDF 文件的截图,需要嵌入到final.pdf文件中:

import java.io.File;
import java.io.FileOutputStream;
import java.net.MalformedURLException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import com.itextpdf.io.font.FontProgram;
import com.itextpdf.io.font.FontProgramFactory;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.Style;
import com.itextpdf.layout.borders.Border;
import com.itextpdf.layout.borders.SolidBorder;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.element.Text;
import com.itextpdf.layout.font.FontProvider;
import com.itextpdf.layout.property.HorizontalAlignment;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.UnitValue;
import com.itextpdf.layout.property.VerticalAlignment;

public class TestPdf {

    public static void main(String[] args) {

        String uuid = UUID.randomUUID().toString();
        try {
            @SuppressWarnings("resource")
            PdfWriter writer = new PdfWriter(new FileOutputStream(new File(Paths.get("Output").toAbsolutePath()+"/final.pdf"))).setSmartMode(true);
            PdfDocument pdfDoc = new PdfDocument(writer);
            pdfDoc.setDefaultPageSize(PageSize.A4.rotate());
            String fonts[] = {Paths.get("fonts").toAbsolutePath() + "/TREBUC.TTF", Paths.get("fonts").toAbsolutePath() + "/TREBUCBD.TTF", Paths.get("fonts").toAbsolutePath() + "/TREBUCBI.TTF",Paths.get("fonts").toAbsolutePath() + "/TREBUCIT.TTF"};
            FontProvider fontProvider = new FontProvider();
            Map<String, PdfFont> pdfFontMap = new HashMap();
            for (String font : fonts) {
                FontProgram fontProgram = FontProgramFactory.createFont(font);
                if(font.endsWith("TREBUC.TTF")) {
                    pdfFontMap.put("NORMAL", PdfFontFactory.createFont(fontProgram, PdfEncodings.WINANSI, true));
                } else if(font.endsWith("TREBUCBD.TTF")) {
                    pdfFontMap.put("BOLD", PdfFontFactory.createFont(fontProgram, PdfEncodings.WINANSI, true));
                } else if(font.endsWith("TREBUCBI.TTF")) {
                    pdfFontMap.put("BOLD_ITALIC", PdfFontFactory.createFont(fontProgram, PdfEncodings.WINANSI, true));
                } else if(font.endsWith("TREBUCIT.TTF")) {
                    pdfFontMap.put("ITALIC", PdfFontFactory.createFont(fontProgram, PdfEncodings.WINANSI, true));
                }

                fontProvider.addFont(fontProgram);
            }
            TestPdf testPdf = new TestPdf();
            NormalPageHeader headerHandler = testPdf.new NormalPageHeader(Paths.get("images").toAbsolutePath() + "\\logo.png", pdfFontMap);
            pdfDoc.addEventHandler(PdfDocumentEvent.START_PAGE, headerHandler);
            PageEndEvent pageEndEvent = testPdf.new PageEndEvent(Paths.get("images").toAbsolutePath() + "\\FooterLineExternal.png" ,pdfFontMap);
            pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, pageEndEvent);
            Document doc = new Document(pdfDoc);

            doc.getPageEffectiveArea(PageSize.A4.rotate());
            Table imageTable = new Table(1);
            imageTable.setBorder(Border.NO_BORDER);
            imageTable.setWidth(UnitValue.createPercentValue(100));
            Cell cell = new Cell();
            Paragraph paragraph = new Paragraph("Title");
            paragraph.setVerticalAlignment(VerticalAlignment.TOP);
            cell.add(paragraph);
            cell.setBorder(Border.NO_BORDER);
            cell.setPaddingTop(50);
            imageTable.addCell(cell);
            doc.add(imageTable);
            doc.close();
            System.out.println("Converted to PDF Succesfully >>> convertedSvg_"+uuid+".pdf");
         }
         catch(Exception e){
             e.printStackTrace();
             System.out.println("Error Occured while converting to PDF = " + e.getMessage());
         }
    }


    class NormalPageHeader implements IEventHandler {

        String header;
        Map<String, PdfFont> font;
        public NormalPageHeader(String header, Map<String, PdfFont> font) {
            this.header = header;
            this.font = font;
        }

        @Override
        public void handleEvent(Event event) {
            //Retrieve document and
            PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
            PdfDocument pdf = docEvent.getDocument();
            PdfPage page = docEvent.getPage();
            Rectangle pageSize = page.getPageSize();
            PdfCanvas pdfCanvas = new PdfCanvas(
            page.getLastContentStream(), page.getResources(), pdf);
            Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
            canvas.setFontSize(10f);
            Table table = new Table(3);
            table.setBorder(Border.NO_BORDER);
            table.setWidth(UnitValue.createPercentValue(100));
            Cell leftCell = new Cell();
            leftCell.setFont(font.get("NORMAL"));
            leftCell.setPaddingTop(15);
            leftCell.setPaddingLeft(20);
            leftCell.setBorder(Border.NO_BORDER);
            leftCell.setBorderBottom(new SolidBorder(0.5f));
            leftCell.setWidth(UnitValue.createPercentValue(33.3f));
            Text userLabel = new Text("Username: ");
            userLabel.setBold();
            Paragraph paragraph = new Paragraph(userLabel);
            Cell middleCell = new Cell();
            middleCell.setFont(font.get("NORMAL"));
            middleCell.setPaddingTop(15);
            middleCell.setBorder(Border.NO_BORDER);
            middleCell.setBorderBottom(new SolidBorder(0.5f));
            middleCell.setWidth(UnitValue.createPercentValue(33.3f));
            paragraph = new Paragraph("Main Header");
            paragraph.setTextAlignment(TextAlignment.CENTER);
            paragraph.setBold();
            paragraph.setFontSize(12);
            middleCell.add(paragraph);
            String programString = "Sample header";
            paragraph = new Paragraph(programString);
            paragraph.setTextAlignment(TextAlignment.CENTER);
            paragraph.setBold();
            paragraph.setFontSize(10);
            middleCell.add(paragraph);

            table.addCell(middleCell);
            Cell rightCell = new Cell();
            rightCell.setFont(font.get("NORMAL"));
            rightCell.setPaddingTop(20);
            rightCell.setWidth(UnitValue.createPercentValue(33.3f));
            rightCell.setHorizontalAlignment(HorizontalAlignment.RIGHT);
            rightCell.setBorder(Border.NO_BORDER);
            rightCell.setBorderBottom(new SolidBorder(0.5f));
            rightCell.setPaddingRight(20);
            //Write text at position
            Image img;
            try {
                img = new Image(ImageDataFactory.create(header));
                img.setHorizontalAlignment(HorizontalAlignment.RIGHT);
                Style style = new Style();
                style.setWidth(91);
                style.setHeight(25);

                 img.addStyle(style);
                 rightCell.add(img);
                 table.addCell(rightCell);
                 table.setMarginLeft(15);
                 table.setMarginRight(15);
                 canvas.add(table);
            }
            catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }
    }

    class PageEndEvent implements IEventHandler {

         protected PdfFormXObject placeholder;
         protected float side = 20;
         protected float x = 300;
         protected float y = 10;
         protected float space = 4.5f;
         private String bar;
         protected float descent = 3;
         Map<String, PdfFont> font;
         public PageEndEvent(String bar, Map<String, PdfFont> font) {
             this.bar = bar;
             this.font = font;
             placeholder =new PdfFormXObject(new Rectangle(0, 0, side, side));
         }

         @Override
         public void handleEvent(Event event) {
             Table table = new Table(3);
             table.setBorder(Border.NO_BORDER);
             table.setWidth(UnitValue.createPercentValue(100));
             Cell confCell = new Cell();
             confCell.setFont(font.get("NORMAL"));
             confCell.setPaddingTop(15);
             confCell.setPaddingLeft(20);
             confCell.setBorder(Border.NO_BORDER);
             confCell.setBorderBottom(new SolidBorder(0.5f));
             confCell.setWidth(UnitValue.createPercentValue(100));
             PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
             PdfDocument pdf = docEvent.getDocument();
             PdfPage page = docEvent.getPage();
             Rectangle pageSize = page.getPageSize();
             PdfCanvas pdfCanvas = new PdfCanvas(
             page.getLastContentStream(), page.getResources(), pdf);
             Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
             Image img;
             try {
                 img = new Image(ImageDataFactory.create(bar));
                 img.setHorizontalAlignment(HorizontalAlignment.LEFT);
                 Style style = new Style();
                 style.setWidth(UnitValue.createPercentValue(100));
                 style.setHeight(50);
                 img.addStyle(style);
                 Paragraph p = new Paragraph().add("Test: Confidential");
                 p.setFont(font.get("NORMAL"));
                 p.setFontSize(8);
                 p.setFontColor(com.itextpdf.kernel.colors.ColorConstants.GRAY);
                 canvas.showTextAligned(p, x, y, TextAlignment.CENTER);
                 pdfCanvas.addXObject(placeholder, x + space, y - descent);
                 pdfCanvas.release();
             }
             catch (MalformedURLException e) {
                 e.printStackTrace();
             }
         }

         public void writeTotal(PdfDocument pdf) {

             Canvas canvas = new Canvas(placeholder, pdf);

             canvas.showTextAligned(String.valueOf(pdf.getNumberOfPages()),
                                    0, descent, TextAlignment.LEFT);
         }

     }

}

标签: pdfitext7

解决方案


首先,在您使用的一些构造背后的 iText 架构上的一些话:

当您使用Document实例将内容添加到 iText 应自动布局的文档时,假设 iText 可以布局内容的区域是整个页面减去页边距。

因此,如果您通过除 之外的其他渠道添加更多页面材料Document,例如您在 yourNormalPageHeader headerHandler和 your中所做的那样PageEndEvent pageEndEvent您有责任在上述布局区域之外,即在页边空白区域中这样做。(除非附加材料是背景材料,例如水标……)

为此,您应该将边距设置得足够大,以保证您的其他材料在边距中。默认情况下,页面每一侧的页边距设置为 36pt,通常对于单行页眉或页脚来说已经足够,但对于多行的页眉或页脚来说就不够了。

在您的代码中,您创建一个标题,该标题至少需要一些 52pt 加上一点,以防止 iText 将布局的内容接触标题行。

请记住,将给定的内容PdfPage sourcePage插入页面非常简单:

...
NormalPageHeader headerHandler = testPdf.new NormalPageHeader(Paths.get("images").toAbsolutePath() + "\\logo.png", pdfFontMap);
pdfDoc.addEventHandler(PdfDocumentEvent.START_PAGE, headerHandler);
PageEndEvent pageEndEvent = testPdf.new PageEndEvent(Paths.get("images").toAbsolutePath() + "\\FooterLineExternal.png" ,pdfFontMap);
pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, pageEndEvent);

Document doc = new Document(pdfDoc);
doc.setTopMargin(55);
PdfFormXObject xobject = sourcePage.copyAsFormXObject(pdfDoc);
Rectangle xobjectBoundaryBox = xobject.getBBox().toRectangle();
xobject.getPdfObject().put(PdfName.Matrix, new PdfArray(new float[] {1, 0, 0, 1, -xobjectBoundaryBox.getLeft(), -xobjectBoundaryBox.getBottom()}));
Image image = new Image(xobject);
image.setAutoScale(true);
doc.add(image);

doc.close();
...

(摘自InsertInSpace助手insertIntoNithinTestFile


如果您按原样使用原始源页面,上面的代码将插入它,包括所有边距空间。如果你不想要这个而是削减那个空间,你可以按照以下步骤确定页面内容的实际边界框,将页面缩小到那个框,然后转发到insertIntoNithinTestFile上面的方法,假设第 1 页PdfDocument pdfDocument应该被处理:

PdfDocumentContentParser contentParser = new PdfDocumentContentParser(pdfDocument);
MarginFinder strategy = contentParser.processContent(1, new MarginFinder());

PdfPage page = pdfDocument.getPage(1);
page.setCropBox(strategy.getBoundingBox());
page.setMediaBox(strategy.getBoundingBox());
insertIntoNithinTestFile(page, "test-InsertIntoNithinTestFile.pdf");

InsertInSpace测试testInsertSimpleTestPdf

MarginFinder是 iText5MarginFinder到 iText 7 的端口:

public class MarginFinder implements IEventListener {
    public Rectangle getBoundingBox() {
        return boundingBox != null ? boundingBox.clone() : null;
    }

    @Override
    public void eventOccurred(IEventData data, EventType type) {
        if (data instanceof ImageRenderInfo) {
            ImageRenderInfo imageData = (ImageRenderInfo) data;
            Matrix ctm = imageData.getImageCtm();
            for (Vector unitCorner : UNIT_SQUARE_CORNERS) {
                Vector corner = unitCorner.cross(ctm);
                addToBoundingBox(new Rectangle(corner.get(Vector.I1), corner.get(Vector.I2), 0, 0));
            }
        } else if (data instanceof TextRenderInfo) {
            TextRenderInfo textRenderInfo = (TextRenderInfo) data;
            addToBoundingBox(textRenderInfo.getAscentLine().getBoundingRectangle());
            addToBoundingBox(textRenderInfo.getDescentLine().getBoundingRectangle());
        } else if (data instanceof PathRenderInfo) {
            PathRenderInfo renderInfo = (PathRenderInfo) data;
            if (renderInfo.getOperation() != PathRenderInfo.NO_OP)
            {
                Matrix ctm = renderInfo.getCtm();
                Path path = renderInfo.getPath();
                for (Subpath subpath : path.getSubpaths())
                {
                    for (Point point2d : subpath.getPiecewiseLinearApproximation())
                    {
                        Vector vector = new Vector((float)point2d.getX(), (float)point2d.getY(), 1);
                        vector = vector.cross(ctm);
                        addToBoundingBox(new Rectangle(vector.get(Vector.I1), vector.get(Vector.I2), 0, 0));
                    }
                }
            }
        } else if (data != null) {
            logger.fine(String.format("Ignored %s event, class %s.", type, data.getClass().getSimpleName()));
        } else {
            logger.fine(String.format("Ignored %s event with null data.", type));
        }
    }

    @Override
    public Set<EventType> getSupportedEvents() {
        return null;
    }

    void addToBoundingBox(Rectangle rectangle) {
        if (boundingBox == null)
            boundingBox = rectangle.clone();
        else
            boundingBox = Rectangle.getCommonRectangle(boundingBox, rectangle);
    }

    Rectangle boundingBox = null;
    Logger logger = Logger.getLogger(MarginFinder.class.getName());
    static List<Vector> UNIT_SQUARE_CORNERS = Arrays.asList(new Vector(0,0,1), new Vector(1,0,1), new Vector(1,1,1), new Vector(0,1,1));
}

( MarginFinder.java )


推荐阅读