iText - 扫描的 PDF 上的 pageSize 指示错误

iText - Wrong indication of pageSize on scanned PDFs

我正在使用 iText 7 (7.1.14) 开发一个应用程序以在现有 PDF 的右上角写入文本。 除了某些文件,例如可以从 here 下载的文件,它给我的页面大小不正确。 它发生在扫描的 PDF 上。 页面大小返回 595.44 x 842.04。 但真正的是 1656.0 x 2339.0。 我尝试了所有页面大小,如 MediaBox 等...

PdfDocument pdfDoc = new PdfDocument(pdfReader,new PdfWriter(file));
pageSize = page.getPageSize();
System.out.println("W:"+page.getPageSize().getWidth()+"H:"+page.getPageSize().getHeight());
System.out.println("W:"+page.getMediaBox().getWidth()+"H:"+page.getMediaBox().getHeight());
System.out.println("W:"+page.getCropBox().getWidth()+"H:"+page.getCropBox().getHeight());
System.out.println("W:"+page.getBleedBox().getWidth()+"H:"+page.getBleedBox().getHeight());
System.out.println("W:"+page.getArtBox().getWidth()+"H:"+page.getArtBox().getHeight());

准确来说,剩下的代码是这样的:

PdfFont bf = PdfFontFactory.createFont(FontConstants.HELVETICA);
float stringWidth = bf.getWidth(stringa,fontSize);
PdfCanvas canvas=new PdfCanvas(page.newContentStreamAfter(),page.getResources(),pdfDoc);
float centeredPosition = pageSize.getWidth() - (pageSize.getWidth()/30);
float yCoord = (pageSize.getHeight()-fontSize-5);
float xCoord = centeredPosition-stringWidth;
canvas.beginText().setFontAndSize(bf, fontSize)
    .moveText(xCoord, yCoord)
    .showText(stringa)
    .setTextRenderingMode(pageN)
    .endText();
pdfDoc.close();

在链接示例 PDF 中,它不是位于右上角,而是位于 sheet 的中间。如果我取一个文本PDF文件,结果是正确的。

你误解了那些吸气剂的输出。特别是它们与像素无关。

所有这些框均以默认用户 space 单位给出。一个这样的单位是 1/72 英寸,除非它被相应页面的 UserUnit 条目重新定义(一个正数,应给出默认用户 space 单位的大小,是 1 ⁄ 72 英寸的倍数。支持值的范围应取决于实现。 - ISO 32000-2,Table 31 — 页面对象中的条目)。


关于您添加的有关向 PDF 添加文本的问题:

之所以你的文本绘制位置不是你期望的绘制位置,是因为用户 space 坐标系在原始内容流中已被转换:

0.36000 0 0 0.36000 0 0 cm
q
1656 0 0 2339 0 0 cm
/Im1 Do
Q

第一条指令(0.36000 0 0 0.36000 0 0 cm)改变了当前的变换矩阵,因此有效地将用户坐标系缩放了 .36;之后一个用户 space 单位是 1/200 inch.

以下指令包裹在q保存图形状态)... Q (恢复图形状态)包络,因此其中当前变换矩阵的变化不影响此后的任何指令。

为了完全不受现有内容流指令的此类图形状态变化的影响,您应该自己将现有的未知内容包装在另一个 q ... Q信封.

实际上有一个 PdfCanvas 构造函数为您做这件事;只需替换

PdfCanvas canvas=new PdfCanvas(page.newContentStreamAfter(),page.getResources(),pdfDoc);

来自

PdfCanvas canvas=new PdfCanvas(page, true);

另请参阅该构造函数的 JavaDocs:

/**
 * Convenience method for fast PdfCanvas creation by a certain page.
 *
 * @param page           page to create canvas from.
 * @param wrapOldContent true to wrap all old content streams into q/Q operators so that the state of old
 *                       content streams would not affect the new one
 */
public PdfCanvas(PdfPage page, boolean wrapOldContent)

但请注意:如果您向页面添加多个新内容流,请不要在同一页面上一次又一次地使用 truewrapOldContent;这会将旧内容越来越深地包裹到这样的信封中,但 PDF 处理器只需要支持有限的深度,PDF 32000-1 仍然提到了 28 层包裹的嵌套限制。