为什么将某些 PDF 包含到输出 PDF 而不是其他 PDF 时出现失真?

Why am I getting distortion when including some PDFs into an output PDF, but not others?

我正在使用 PDFBox 版本 2.0.23 创建一个由多个输入 PDF 组成的输出 PDF。有时,多个输入 PDF 需要放在输出 PDF 的同一页上,所以我不能只将输入的页面添加到输出。

我遇到的问题是一些输入的 PDF 莫名其妙地扭曲了:它们似乎在 Y 轴上被拉伸了。

这是最小的可重现示例:


import java.io.File;
import java.io.IOException;

import org.apache.pdfbox.multipdf.LayerUtility;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;

public class Main {

    public static void main(String[] args) {

        PDDocument d = new PDDocument();

        LayerUtility layerUtility = new LayerUtility(d);

        String[] paths = { "Automania.pdf", "Colonel.pdf" };

        for (String p : paths) {
            PDPage page = new PDPage(PDRectangle.LETTER);
            d.addPage(page);

            try (PDPageContentStream contents = new PDPageContentStream(d, page, PDPageContentStream.AppendMode.APPEND,
                    true)) {
                PDDocument source = null;
                source = PDDocument.load(new File(p));
                PDFormXObject form = layerUtility.importPageAsForm(source, 0);
                source.close();

                contents.drawForm(form);

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        try {
            d.save(new File("out.pdf"));
            d.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("Complete.");
    }

}

Here is a sample 两个输入文件(Automania.pdf 和 Colonel.pdf)和输出(out.pdf);都在 public 域中。 Automania.pdf 被严重拉伤,而 colonel.pdf 没有受伤。这两个文件之间有什么不同导致失真,我该如何解决?

感谢@mkl 解决了这个问题!

问题是调用 importPageAsForm 在遇到 90 度和 270 度旋转时会扭曲所有内容,从而保持原始的、未旋转的纵横比。 @mkl 建议的快速修复是在导入之前将页面旋转设置为 0,然后手动应用旋转,效果很好。

此解决方案的代码如下,稍微更改为生成我们自己的输入文档而不是从文件加载它们。

package pdftest;

import java.io.File;
import java.io.IOException;

import org.apache.pdfbox.multipdf.LayerUtility;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.util.Matrix;

public class Main {

    public static void main(String[] args) {

        PDDocument d = new PDDocument();

        LayerUtility layerUtility = new LayerUtility(d);

        for (int i = 0; i < 360; i += 90) {
            PDPage page = new PDPage(PDRectangle.LETTER);
            d.addPage(page);

            try (PDPageContentStream contents = new PDPageContentStream(d, page, PDPageContentStream.AppendMode.APPEND,
                    true)) {
                
                /* Generate Test Input Document */
                PDDocument source = new PDDocument();
                PDPage sourcePage = new PDPage(new PDRectangle(500, 200));
                PDPageContentStream sourceStream = new PDPageContentStream(source, sourcePage);
                
                // Draw text
                sourceStream.beginText();
                sourceStream.newLineAtOffset(sourcePage.getBBox().getUpperRightX() / 2, sourcePage.getBBox().getUpperRightY() / 2);
                sourceStream.setFont(PDType1Font.TIMES_ROMAN, 12);
                sourceStream.showText("This Way Up! " + i);
                sourceStream.endText();
                sourceStream.addRect(0, 0, sourcePage.getBBox().getUpperRightX(), sourcePage.getBBox().getUpperRightY());
                sourceStream.setLineWidth(10);
                sourceStream.stroke();
                
                sourceStream.close();
                source.addPage(sourcePage);
                sourcePage.setRotation(i);
                
                /* Place the Test Input Document into the Output Document */
                
                // Rotations of 90 and 270 are distorted by importPageAsForm, so we remove them here 
                int rotation = sourcePage.getRotation();
                switch (rotation) {
                case 90:
                case 270:
                    sourcePage.setRotation(0);
                    break;
                default:
                    break;
                }
                
                
                PDFormXObject form = layerUtility.importPageAsForm(source, 0);
                source.close();
                
                // Re-add rotations of 90 and 270 now that we're past importPageAsForm.
                PDRectangle viewBox = sourcePage.getBBox();
                switch (rotation) {
                case 90:
                    contents.transform(Matrix.getRotateInstance(Math.toRadians(-rotation), 0, viewBox.getWidth()));
                    break;
                case 270:
                    contents.transform(Matrix.getRotateInstance(Math.toRadians(-rotation), viewBox.getHeight(), 0));
                    break;
                default:
                    break;
                }
                
                contents.drawForm(form);

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        try {
            d.save(new File("out.pdf"));
            d.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("Complete.");
    }

}