PDF页面大小,图像更宽

时间:2014-02-12 17:54:54

标签: java pdf pdfbox

我正在使用PDF Box处理PDF文件,并根据页面上给定的坐标插入文本对象。我得到的坐标是左上角的,我找到页面的媒体框,然后计算文本的位置。然而,有一些PDF图像(它们被扫描),我插入的文本没有站在正确的位置,就像页面的大小比媒体盒大得多。

// getX-Y returns the coordinates that the text should be inserted
// getSize returns the text height
void write(PDDocument doc, PDPage page, PDPageContentStream cs) {
    PDRectangle rect = page.findMediaBox();
    cs.moveTextPositionByAmount(this.getX(), height-this.getY()-getSize());
}

从媒体框中返回的尺寸为595.2 x 841.92。对于给定的文本位置300x420,我希望将此文本插入页面中间。但是它的插入方式太低而且页面左侧。当我用Acrobat Reader打开文档并将页面复制为图像时(因为它已被扫描),我看到图像尺寸为2480 x 3508。如果页面尺寸为该尺寸,则插入文本的位置将有意义。

我觉得pdf页面大小会根据其内容进行更改,但为什么我不将这些尺寸作为页面大小进行更改,而是仍然得到类似595.2 x 841.92的内容?我应该处理页面上的每个图像并找到真正的尺寸吗?我在这里缺少什么?

编辑: Sample PDF Document

编辑: 这是我得到的代码部分PDPageContentStream

PDDocument doc = null;
doc = PDDocument.load(inputFile);
List <?> allPages = doc.getDocumentCatalog().getAllPages();
for (int i = 0; i < list.size(); i++) {
    PDFObject obj = (PDFObject) list.get(i);
    for (int j = 0; j < allPages.size(); j++) {
        PDPage page = (PDPage) allPages.get(j);
        PDPageContentStream contentStream = new PDPageContentStream(doc, page, true, true);
        obj.write(doc, page, contentStream);
        if ("F".equalsIgnoreCase(obj.getPageType())) {
            break;
        }
    }
}

1 个答案:

答案 0 :(得分:1)

不幸的是,OP没有发布所有相关代码。因此,这个答案部分基于假设,特别是他创建了他的PDPageContentStream 而没有确保默认用户空间坐标系仍然在他添加新操作的位置使用。

样本文件

第一页的内容流开头如下:

0.24000 0 0 0.24000 0 0 cm
q
2480 0 0 3508 0 0 cm
/Im5 Do
Q

因此,它首先按.24缩放用户空间坐标系,按下图形状态,将坐标系缩放2480(x方向)和3508(y方向),绘制图像,最终恢复图形状态。

因此,此后用户空间坐标系仍然由.24缩放。因此,以下操作中给出的坐标受该因素的影响。

紧接着是文本对象,例如这样:

BT
1 0 0 rg
/F0 25 Tf
400 794.9199829102 Td
(JFE14006) Tj
ET 

我认为这是OP添加的对象之一,没有考虑非默认用户空间坐标系,因为坐标和字体大小似乎足以用于默认的用户空间坐标系。

(顺便说一句,所引用的字体未在页面的资源字典中定义。)

解决方案1 ​​

当插入点的用户空间坐标系缩放.24时,您可以反缩放自己的坐标和大小(即将它们除以.24)。

E.g。要使用大小为10的字体在给定的给定文本位置300x420(原点在左上角)绘制文本“MIDDLE”,您可以这样做:

PDDocument document = PDDocument.load("0006-sun1-4.pdf");
List<PDPage> allPages = document.getDocumentCatalog().getAllPages();
PDPage firstPage = allPages.get(0);
PDRectangle pageSize = firstPage.findMediaBox();

PDPageContentStream contentStream = new PDPageContentStream(document, firstPage, true, true);
contentStream.setStrokingColor(Color.red);
contentStream.beginText();
contentStream.moveTextPositionByAmount(300/.24f, (pageSize.getUpperRightY() - 420 - 10)/.24f);
contentStream.setFont(PDType1Font.HELVETICA_BOLD, 10/.24f);
contentStream.drawString("MIDDLE");
contentStream.endText();
contentStream.close();

document.save("0006-sun1-4-scaledAdd.pdf");
document.close();

此解决方案并非最佳,但是:

  • 只要您有其他源文档(例如更新的表单),插入点处的内容流可能会有不同缩放的坐标系;
  • 图形绘制引擎的其他状态也可能未处于您期望的默认状态。

因此:

解决方案2

您可以通过使用 q (保存图形状态)和 Q (恢复图形状态)运算符对封装现有内容流来恢复对图形状态的所有更改。

E.g。如上所述,使用大小为10的字体在给定的给定文本位置300x420(来自左上角)绘制文本“MIDDLE”,您可以这样做:

PDDocument document = PDDocument.load("0006-sun1-4.pdf");
List<PDPage> allPages = document.getDocumentCatalog().getAllPages();
PDPage firstPage = allPages.get(0);
PDRectangle pageSize = firstPage.findMediaBox();

PDStream contents = firstPage.getContents();  
PDFStreamParser parser = new PDFStreamParser(contents.getStream()); 
parser.parse();
List<Object> tokens = parser.getTokens();
tokens.add(0, PDFOperator.getOperator("q"));
tokens.add(PDFOperator.getOperator("Q"));
PDStream updatedStream = new PDStream(document);  
OutputStream out = updatedStream.createOutputStream();  
ContentStreamWriter tokenWriter = new ContentStreamWriter(out);  
tokenWriter.writeTokens(tokens);  
firstPage.setContents(updatedStream);

PDPageContentStream contentStream = new PDPageContentStream(document, firstPage, true, true);
contentStream.setStrokingColor(Color.red);
contentStream.beginText();
contentStream.moveTextPositionByAmount(300, pageSize.getUpperRightY() - 420 - 10);
contentStream.setFont(PDType1Font.HELVETICA_BOLD, 10);
contentStream.drawString("MIDDLE");
contentStream.endText();
contentStream.close();

document.save("0006-sun1-4-restoredAdd.pdf");
document.close();

(在基本上只绘制图像的页面的情况下,解析和重写现有流不是完全合适的样式资源,但不是真正的问题。)

Screenshot of result