使用PDFBox编辑pdf内容会从pdf中删除最后一行

时间:2015-03-23 20:54:05

标签: java pdfbox

我正在尝试使用Java中的PDFBox编辑pdf的一些内容。问题是,每当我编辑pdf中的任何字符串,并尝试使用Adobe Reader打开它时,最后一行不会出现在新呈现的pdf中。

当我尝试从浏览器直接打开渲染的pdf时,我能够看到最后一行。但是,它以不同的格式编码。我正在使用以下代码编辑pdf的内容:

PDDocument doc = PDDocument.load(FileName);
PDPage page = (PDPage) doc.getDocumentCatalog().getAllPages().get(0);
PDStream contents = page.getContents();
PDFStreamParser parser = new PDFStreamParser(contents.getStream());
parser.parse();
List<Object> tokens = parser.getTokens();
for (int j = 0; j < tokens.size(); j++) {
    Object next = tokens.get(j);
    if (next instanceof PDFOperator) {
        PDFOperator op = (PDFOperator) next;
        if (op.getOperation().equals("Tj")) {
            COSString previous = (COSString) tokens.get(j - 1);
            String string = previous.getString();

            string = string.replace("@ordnum&", (null != data.getOrderNumber()?data.getOrderNumber():""));
            string = string.replace("@shipid&", (null != data.getShipmentId()?data.getShipmentId():""));
            string = string.replace("@customer&", (null != data.getCustomerNumber()?data.getCustomerNumber():""));
            string = string.replace("@fromname&", (null != data.getFromName()?data.getFromName():""));

            tokens.set(j - 1, new COSString(string.trim()));
        }
    }
}

编辑pdf会删除“有疑问?...”的行。这里有什么问题?我做错了吗?

感谢。

1 个答案:

答案 0 :(得分:4)

为什么最后一行变为无效

首先,你必须要知道PDF中的字符串存在两种根本不同的情况

  • 外部内容流,例如文档属性的作者和关键字,以及
  • 内部内容流,表示要绘制的某些字体的字形序列。

前一种类型使用 PDFDocEncoding (类似于Latin1) UTF-16BE 以前导字节顺序编码标记。方法COSString.getString和构造函数COSString(String)是为这种字符串设计的。

后一种类型使用为此字符串呈现的PDF字体定义的编码进行编码。这可能是一些标准化编码,如 WinAnsiEncoding (类似于Latin1)或 UniGB-UTF16-H (用于Adobe-GB1字符集的Unicode(UTF-16BE)编码)。但它也可能是一些自定义的单字节或多字节编码。标准化和自定义多字节编码都没有字节顺序标记。

在PDF的页面内容流中,大多数字符串使用 WinAnsiEncoding (因为这是其字体的编码)。由于 WinAnsiEncoding PDFDocEncoding 非常相似,因此您使用的 PDFDocEncoding COSString方法和构造函数对他们来说非常合适。

但是,最后一行是使用 Identity-H 进行编码的,它是 2字节CID的水平身份映射,即直接引用a的双字节编码字体程序中的字符ID没有任何意义,没有该字体程序。

由于此字符串不以字节顺序标记开头,COSString.getString假定它使用单字节编码 PDFDocEncoding ,因此为每个原始双字节创建两个Java字符串字符PDF字符串字符。由于某些字符的字符值超出实际有效的 PDFDocEncoding 范围,构造函数COSString(String)会创建一个PDF字符串,其中每个中间Java字符使用一个双字节表示 UTF-16BE 字符;此外,还添加了一个字节顺序标记。

因此,原始PDF字符串(以十六进制书写)

002b004400590048000300540058004800560057004c0052005100560022000300260052
005100570044004600570003005800560003004400570003004b0057005700530056001d
00120012005a005a005a005600110046004c0057005500580056004f0044005100480011
004600520050001200460052005100570044004600570010005800560012
编辑后

FEFF002B0000004400000059000000480000000300000054000000580000004800000056
000000570000004C00000052000000510000005600000022000000030000002600000052
000000510000005700000044000000460000005700000003000000580000005600000003
0000004400000057000000030000004B00000057000000570000005300000056000002DB
00000012000000120000005A0000005A0000005A0000005600000011000000460000004C
000000570000005500000058000000560000004F00000044000000510000004800000011
000000460000005200000050000000120000004600000052000000510000005700000044
0000004600000057000000100000005800000056

根据PDF查看器,这可能会产生不同的效果。你原来的一行

original line

e.g。可能会变得非常广泛:

line spread across page

或完全消失

line vanished

因此,简而言之,如果您需要编辑这样的PDF,请确保您只编辑类似Latin1的PDF字符串。

如果您还需要编辑不同编码的PDF字符串,请使用byte[]方法COSString将其提取为getBytes,以适用于相关编码的方式编辑此数组,以及使用构造函数COSString从编辑的字节创建新的COSString(byte[])

但即便如此也不是一个好主意。

编辑像一般

的流的问题

在编辑像

这样的流时,还有很多其他的陷阱在等着你
  • 而不是像。

    (@customer&) Tj
    

    您的信息流可能包含

    (@cust) Tj
    (omer&) Tj
    

    [(@cust) -6 (omer&) ] TJ 
    

    甚至

    (omer&) Tj
    -62 0 Td
    (@cust) Tj
    

    因此,如果新模板使用略有不同的表示,则突然替换可能不起作用。

  • 字体可能只是部分嵌入。如果未包含替换字符的字形,则会将它们绘制为间隙。

  • 您编辑的文字绘图操作可能依赖于前者使用特定宽度。然后,您的替换可以破坏以前的布局。

  • ...

实质上,在通用文档中正确编辑流是非常困难的。

你还能做什么

您可以使用AcroForm表单字段代替内容持有者,例如您的@customer&

表单字段具有名称,可以被它们识别。填写它们不会改变内容中的任何内容。

如果您之后不想让人们编辑您的PDF表单字段,您可以将它们标记为只读,甚至将它们展平为内容。