How to fix the orientation of a PDF page in order to scale it?

Tags: page sizepage orientationscale contentiText 7

I need to scale down the pages of an existing PDF document. This works fine if the pages aren't rotated, but I don't succeed in rotating pages correctly, for instance if they have a rotation of 270 degrees:

for(int i=1; i<=reader.getNumberOfPages(); i++){
    int rotation = reader.getPageRotation(i);
    float pageWidth = reader.getPageSizeWithRotation(i).getWidth();
    float pageHeight = reader.getPageSizeWithRotation(i).getHeight();
    doc.newPage();
    PdfImportedPage page = writer.getImportedPage(reader, i);
    if (rotation == 0) {
        cb.addTemplate(page, 1f, 0, 0, scaleHeight, x, y);
    } else if (rotation == 90) {
        cb.addTemplate(page, 0, -1f, 1f, 0, 0, pageHeight);
    } else if (rotation == 180) {
        cb.addTemplate(page, 1f, 0, 0, -1f, pageWidth, pageHeight);
    } else if (rotation == 270) {
        cb.addTemplate(page, 0, -1f, 1f, 0, 0, pageHeight);
    }
}
I have two questions:

  1. How do I properly rotate the page contents?
  2. How can I scale the rotated contents correctly?

Posted on StackOverflow on Mar 19, 2014 by user3854377

Please take a look at the ScaleDown example that can be used to scale down pages, keeping the original orientation. This example implements the IEventHandler interface:

protected class ScaleDownEventHandler implements IEventHandler {
    protected float scale = 1;
    protected PdfDictionary pageDict;
 
    public ScaleDownEventHandler(float scale) {
        this.scale = scale;
    }
 
    public void setPageDict(PdfDictionary pageDict) {
        this.pageDict = pageDict;
    }
 
    @Override
    public void handleEvent(Event event) {
        PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
        PdfPage page = docEvent.getPage();
        page.put(PdfName.Rotate, pageDict.getAsNumber(PdfName.Rotate));
 
        scaleDown(page, pageDict, PdfName.MediaBox, scale);
        scaleDown(page, pageDict, PdfName.CropBox, scale);
    }
}

When you create the event, you pass a value scale that will define the scale factor. I apply the scale to the width and the height, feel free to adapt it if you only want to scale the height or the width only.

The information about page size and rotation is stored in the page dictionary. Obviously the ScaleDownEventHandler needs the values of the original document, and that why we'll pass a pageDict for every page we copy.

Every time a new page is created, we will copy/replace:

  • the /Rotate value. Remove this line if you want to remove the rotation,

  • the /MediaBox value. This defines the full size of the page.

  • the /CropBox value. This defines the visible size of the page.

As we want to scale the page, we use the following scaleDown() method:

protected void scaleDown(PdfPage destPage, PdfDictionary pageDictSrc, PdfName box, float scale) {
    PdfArray original = pageDictSrc.getAsArray(box);
    if (original != null) {
        float width = original.getAsNumber(2).floatValue() - original.getAsNumber(0).floatValue();
        float height = original.getAsNumber(3).floatValue() - original.getAsNumber(1).floatValue();
        PdfArray result = new PdfArray();
        result.add(new PdfNumber(0));
        result.add(new PdfNumber(0));
        result.add(new PdfNumber(width * scale));
        result.add(new PdfNumber(height * scale));
        destPage.put(box, result);
    }
}

Suppose that I want to reduce the page width and height to 50% of the original width and height, then using iText 7 I create two instances of PdfDocument class (one for the source file and one for the result) and set the event handler like this:

PdfDocument srcDoc = new PdfDocument(new PdfReader(src));
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
float scale = 0.5f;
ScaleDownEventHandler eventHandler = new ScaleDownEventHandler(scale);
pdfDoc.addEventHandler(PdfDocumentEvent.START_PAGE, eventHandler);

Now it's only a matter of looping over the pages:

int n = srcDoc.getNumberOfPages();
PdfCanvas canvas;
PdfFormXObject page;
for (int p = 1; p <= n; p++) {
    eventHandler.setPageDict(srcDoc.getPage(p).getPdfObject());
    canvas = new PdfCanvas(pdfDoc.addNewPage());
    page = srcDoc.getPage(p).copyAsFormXObject(pdfDoc);
    canvas.addXObject(page, scale, 0f, 0f, scale, 0f, 0f);
}
pdfDoc.close();
srcDoc.close();

Here we update the pageDict for every new page, then define a PdfCanvas object, update a page to be copied and use addXObject() method, which takes the page from source file as the first argument and adds it to the resulting PDF.

This code takes the file orientations.pdf measuring 8.26 by 11.69 in and transforms it into the file scaled_down.pdf measuring 4.13 by 5.85 in.

Click this link if you want to see how to answer this question in iText 5.