Fix the orientation of a PDF in order to scale it

て烟熏妆下的殇ゞ 提交于 2019-12-22 11:37:09

问题


I am having some issues with a PDF containing a rotation property created by a Xerox scanner. The below function was originally created to scale the height of an input PDF by the amount given by the variable scaleHeight. This works fine for input documents without rotation.

When testing a document with a 270 degree rotation, I found that the rotation property which would have made the document appear in a portrait orientation was ignored. Instead, the document appeared in a landscape orientation in the output PDF. So I updated the function below to apply the scaling only when there is no rotation, and used another example I found online to try to fix the rotation. This did not work, and resulted in a mirror image of the original document in portrait format.

So now I have two problems: 1. How to properly rotate the document contents. 2. How to scale the rotated contents.

If I can solve item 1, I can simply call the function again (with the rotation property removed) to fix item 2.

Thank you for any and all help, the function is below. The commented out lines referring to the rotationEvent did not help here either.

public String resizePDF (String pdfIn, float x, float y, float scaleHeight) throws Exception {
    String pdfOut = pdfIn.substring(0, pdfIn.length() - 4) + "_resize.pdf";
    PdfReader reader = new PdfReader(pdfIn);
    int rotation = reader.getPageRotation(1);
    com.itextpdf.text.Document doc = new com.itextpdf.text.Document(reader.getPageSizeWithRotation(1), 0, 0, 0, 0);
    PdfWriter writer = PdfWriter.getInstance(doc, new FileOutputStream(pdfOut));
    doc.open();
    PdfContentByte cb = writer.getDirectContent();
    Rotate rotationEvent = new Rotate();
    writer.setPageEvent(rotationEvent);
    for(int i=1; i<=reader.getNumberOfPages(); 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);
            //rotationEvent.setRotation(PdfPage.PORTRAIT);
        } else if (rotation == 90) {
            cb.addTemplate(page, 0, -1f, 1f, 0, 0, pageHeight);
            //rotationEvent.setRotation(PdfPage.LANDSCAPE);
        } else if (rotation == 180) {
            cb.addTemplate(page, 1f, 0, 0, -1f, pageWidth, pageHeight);
            //rotationEvent.setRotation(PdfPage.INVERTEDPORTRAIT);
        } else if (rotation == 270) {
            cb.addTemplate(page, 0, -1f, 1f, 0, 0, pageHeight);
            //cb.addTemplate(page, 0, 1f, -1f, 0, pageWidth, 0);
            //rotationEvent.setRotation(PdfPage.SEASCAPE);
        }
    }
    doc.close();
    return pdfOut;
}

回答1:


I don't understand your code, but when I tell people "please throw away your code and start anew", many people feel offended (although it was certainly not my intention to offend them).

I understand that you want to scale the contents of an existing PDF and either keep the rotation or remove it (that part isn't entirely clear).

Hence I have written you an example called ScaleDown that can be used to scale down, keeping the orientation. It's sufficient to remove a single line to remove the orientation.

This example uses a page event (I named it ScaleEvent):

public class ScaleEvent extends PdfPageEventHelper {

    protected float scale = 1;
    protected PdfDictionary pageDict;

    public ScaleEvent(float scale) {
        this.scale = scale;
    }

    public void setPageDict(PdfDictionary pageDict) {
        this.pageDict = pageDict;
    }

    @Override
    public void onStartPage(PdfWriter writer, Document document) {
        writer.addPageDictEntry(PdfName.ROTATE, pageDict.getAsNumber(PdfName.ROTATE));
        writer.addPageDictEntry(PdfName.MEDIABOX, scaleDown(pageDict.getAsArray(PdfName.MEDIABOX), scale));
        writer.addPageDictEntry(PdfName.CROPBOX, scaleDown(pageDict.getAsArray(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.

The information about page size and rotation is stored in the page dictionary. Obviously the ScaleEvent 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:

public PdfArray scaleDown(PdfArray original, float scale) {
    if (original == null)
        return null;
    float width = original.getAsNumber(2).floatValue()
            - original.getAsNumber(0).floatValue();
    float height = original.getAsNumber(3).floatValue()
            - original.getAsNumber(1).floatValue();
    return new PdfRectangle(width * scale, height * scale);
}

Suppose that I want to reduce the page width and height to 50% of the original width and height, then I create the event like this:

PdfReader reader = new PdfReader(src);
float scale = 0.5f;
ScaleEvent event = new ScaleEvent(scale);
event.setPageDict(reader.getPageN(1));

I can define a Document with any page size I want as the size will be changed in the ScaleEvent anyway. Obviously, for this to work I need to declare the event to the PdfWriter instance:

Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));
writer.setPageEvent(event);
document.open();

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

int n = reader.getNumberOfPages();
Image page;
for (int p = 1; p <= n; p++) {
    page = Image.getInstance(writer.getImportedPage(reader, p));
    page.setAbsolutePosition(0, 0);
    page.scalePercent(scale * 100);
    document.add(page);
    if (p < n) {
        event.setPageDict(reader.getPageN(p + 1));
    }
    document.newPage();
}
document.close();

I am wrapping the imported page inside an Image because I personally think that the methods available for the Image class are easier to use than defining the parameters of the addTemplate() method. If you want to use addTemplate() instead of Image, feel free to do so; the result will be identical (contrary to what you wrote in a comment, wrapping a page inside an image will not cause any loss of "resolution" as all the text remains available as vector data).

Note that I update the pageDict for every new page.

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.

If you want all the pages to be in portrait, just remove the following line:

writer.addPageDictEntry(PdfName.ROTATE, pageDict.getAsNumber(PdfName.ROTATE));


来源:https://stackoverflow.com/questions/29152313/fix-the-orientation-of-a-pdf-in-order-to-scale-it

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!