Java iText将文档缩放至A4。

huangapple go评论62阅读模式
英文:

Java iText scale document to A4

问题

我有以下方法,可以将文档的所有页面调整为A4页面尺寸:

  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);
            page.setMediaBox(crop);
            page.setCropBox(crop);

            // 在之前放置在内容流上的内容将在其他内容之前呈现,
            // 因此可以理解为背景(底部“图层”)
            new PdfCanvas(page.newContentStreamBefore(),
                    page.getResources(), doc).writeLiteral("\nq 0.5 0 0 0.5 0 0 cm\nq\n");

            // 在之后放置在内容流上的内容将在其他内容之后呈现,
            // 因此可以理解为前景(顶部“图层”)
            new PdfCanvas(page.newContentStreamAfter(),
                    page.getResources(), doc).writeLiteral("\nQ\nQ\n");
        }
    }

然而,目前这不按预期工作,页面虽然被调整为A4(297x210),但内容没有被适当地缩放以适应其中,内容似乎被裁剪,因为原始页面大于297x210。您应该如何修复这个问题?

英文:

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 &lt;= 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);
            page.setMediaBox(crop);
            page.setCropBox(crop);

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

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

However , this is not working as expected , the pages are being transformed to A4 (297x210) 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 ?

答案1

得分: 1

你在评论中进行了澄清:

> 我想要前一内容的边界框被缩放,并在目标中添加边距

因此,我们首先必须确定原始页面内容的边界框。这可以通过使用来自此答案MarginFinder类来完成。注意:该类确定所有内容的边界框,即使它只是一个白色矩形,在视觉上与没有内容或先前在裁剪框外的内容在外观上没有区别... 如果您的用例需要,您可能还需要扩展该类以考虑这些情况。

有了内容边界框的确定,剩下要做的就是一些计算。

以下方法使用上述类来确定边界框,相应地转换内容,并更改结果的裁剪框。

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);
        pdfCanvasProcessor.processPageContent(page);
        Rectangle boundingBox = marginFinder.getBoundingBox();
        if (boundingBox == null || boundingBox.getWidth() == 0 || boundingBox.getHeight() == 0) {
            System.err.printf("无法缩放第 %d 页的内容,边界框为 %s\n", i , boundingBox);
            continue;
        } else {
            // 缩放并将内容移入带边距的 A4 页面
            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)
                    .concatMatrix(transform);
        }
        page.setMediaBox(pageSize);
        page.setCropBox(pageSize);
    }
}

(来自 ScaleToA4 方法 scale

对于带有每边一英寸边距的 A4 结果页面大小,您可以为 PdfDocument pdfDocument 这样调用它:

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

(摘自 ScaleToA4 测试 testFdaRequiresUseOfEctdFormatAndStandardizedStudyDataInFutureRegulatorySubmissionsSept

英文:

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 &lt;= n; i++) {
PdfPage page = pdfDocument.getPage(i);
MarginFinder marginFinder = new MarginFinder();
PdfCanvasProcessor pdfCanvasProcessor = new PdfCanvasProcessor(marginFinder);
pdfCanvasProcessor.processPageContent(page);
Rectangle boundingBox = marginFinder.getBoundingBox();
if (boundingBox == null || boundingBox.getWidth() == 0 || boundingBox.getHeight() == 0) {
System.err.printf(&quot;Cannot scale page %d contents with bounding box %s\n&quot;, i , boundingBox);
continue;
} 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 &lt; 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)
.concatMatrix(transform);
}
page.setMediaBox(pageSize);
page.setCropBox(pageSize);
}
}

(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)

huangapple
  • 本文由 发表于 2020年8月20日 20:25:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/63505060.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定