I am using PDFBox version 2.0.23 to create one output PDF that is comprised of multiple input PDFs. Sometimes, multiple input PDFs need to be placed on the same page of the output PDF, so I can't just add the pages from the input to the output.
The problem that I am encountering is that some input PDFs are being distorted rather inexplicably: they seem to be stretched out in the Y axis.
This is the minimum reproducible example:
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 of two input files (Automania.pdf and Colonel.pdf) and the output (out.pdf); all are in the public domain. Automania.pdf is stretched badly, while colonel.pdf is unharmed. What difference between the two files is causing the distortion, and how can I fix it?
Thanks to @mkl for working this one out!
The issue is that the call to importPageAsForm distorts everything when it encounters rotations of 90 and 270 such that the original, unrotated aspect ratio is kept. The quick fix suggested by @mkl is to set page rotation to 0 before importing and then manually applying rotation, which works great.
The code for this solution is below, with the slight change of generating our own input documents rather than loading them from a file.
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.");
}
}