Java iText scale document to A4

Tags: , , , ,

I have the following method that “resizes” all the pages of a document to A4 page dimensions:

  for (PdfDocument doc : pdfDocuments) {
        int n = doc.getNumberOfPages();
        for (int i = 1; i <= n; i++) {
            PdfPage page = doc.getPage(i);
            Rectangle media = page.getCropBox();
            if (media == null) {
                media = page.getMediaBox();
            Rectangle crop = new Rectangle(0, 0, 210, 297);

            // The content, placed on a content stream before, will be rendered before the other content
            // and, therefore, could be understood as a background (bottom "layer")
            new PdfCanvas(page.newContentStreamBefore(),
                    page.getResources(), doc).writeLiteral("nq 0.5 0 0 0.5 0 0 cmnqn");

            // The content, placed on a content stream after, will be rendered after the other content
            // and, therefore, could be understood as a foreground (top "layer")
            new PdfCanvas(page.newContentStreamAfter(),
                    page.getResources(), doc).writeLiteral("nQnQn");

However , this is not working as expected , the pages are being transformed to A4 (297×210) BUT the content is not being fitted inside (scaled) , the content appears cutted because the original pages are larger than 297X210 . How can I fix this ?


In a comment you clarified

I want the bounding box of the former content be scaled and a margin be added in the target

So, we first have to determine the bounding box of the original page content. This can be done using the MarginFinder class from this answer. Beware: That class determines the bounding box of all content, even if it is merely a white rectangle visually not distinct from no content or something formerly outside the crop box… If your use case requires it, you may have to extend that class to take such circumstances into consideration, too.

With the content bounding box determined all that remains to do is a bit of calculation.

The following method determines the bounding box using the class above, transforms the content accordingly, and changes the result crop box.

void scale(PdfDocument pdfDocument, Rectangle pageSize, Rectangle pageBodySize) {
    int n = pdfDocument.getNumberOfPages();

    for (int i = 1; i <= n; i++) {
        PdfPage page = pdfDocument.getPage(i);

        MarginFinder marginFinder = new MarginFinder();
        PdfCanvasProcessor pdfCanvasProcessor = new PdfCanvasProcessor(marginFinder);
        Rectangle boundingBox = marginFinder.getBoundingBox();
        if (boundingBox == null || boundingBox.getWidth() == 0 || boundingBox.getHeight() == 0) {
            System.err.printf("Cannot scale page %d contents with bounding box %sn", i , boundingBox);
        } else {
            // Scale and move content into A4 with margin
            double scale = 0, xDiff= 0, yDiff = 0;
            double xScale = pageBodySize.getWidth()/boundingBox.getWidth();
            double yScale = pageBodySize.getHeight()/boundingBox.getHeight();
            if (xScale < yScale) {
                yDiff = boundingBox.getHeight() * (yScale / xScale - 1) / 2;
                scale = xScale;
            } else {
                xDiff = boundingBox.getWidth() * (xScale / yScale - 1) / 2;
                scale = yScale;

            AffineTransform transform = AffineTransform.getTranslateInstance(pageBodySize.getLeft() + xDiff, pageBodySize.getBottom() + yDiff);
            transform.scale(scale, scale);
            transform.translate(-boundingBox.getLeft(), -boundingBox.getBottom());
            new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDocument)

(ScaleToA4 method scale)

For an A4 result page size with an inch of margin on each side you can call it like this for a PdfDocument pdfDocument:

Rectangle pageSize = PageSize.A4;
Rectangle pageBodySize = pageSize.clone().applyMargins(72, 72, 72, 72, false);
scale(pdfDocument, pageSize, pageBodySize);

(excerpt from ScaleToA4 test testFdaRequiresUseOfEctdFormatAndStandardizedStudyDataInFutureRegulatorySubmissionsSept)

Source: stackoverflow