如何将文本对象添加到现有pdf

时间:2013-03-01 05:24:55

标签: pdf annotations adobe pdf-generation adobe-reader

我有一个源代码pdf,我通过添加文本对象进行修改。我正在使用PDF规范中提到的“增量更新”。但是在使用这种方法添加文本对象时,由于pdf无法在Adobe Reader 11中正确呈现,我会犯一些错误。当打开pdf并双击它时,添加的文本对象将被删除。我发现这是由于文本注释。

现在我想知道如何使用增量更新添加新的文本对象?如何维护自由文本注释的内容和RC?

是否可以禁用或删除注释,以便轻松避免我的问题?因为我想要一个简单的pdf,我不想要注释选项。

我使用的来源pdf是here

添加文本对象后修改后的pdf为here

根据pdf规范,我不确定源pdf本身是否合适。

1 个答案:

答案 0 :(得分:6)

首先,让我告诉你,如果你能使用一个体面的PDF库,那将是多么容易。我使用iTextSharp作为示例,但也可以与PDFBox或PDFNet等其他人一起完成(@Ika在他的回答中已经提到过):

PdfReader reader = new PdfReader(sourcePdf);
using (PdfStamper stamper = new PdfStamper(reader, targetPdfStream)) {
  Font FONT = new Font(Font.FontFamily.HELVETICA, 12, Font.BOLD, new GrayColor(0.75f));
  PdfContentByte canvas = stamper.GetOverContent(1);
  ColumnText.ShowTextAligned(
    canvas,
    Element.ALIGN_LEFT, 
    new Phrase("Hello people!", FONT), 
    36, 540, 0
  );
}

(源自Webified iTextSharp Example StampText.cs中解释的chapter 6 iText in Action — 2nd Edition。)

(您选择哪个PDF库,取决于您的一般要求和可用的许可证模型。)

如果尽管易于使用此类PDF库,您仍然坚持手动操作,这里有一些评论:

首先,您必须找到要添加内容的页面的页面词典。根据PDF的类型,这可能需要对对象流等进行解压缩,但在样本modified1.pdf中则不需要:

7 0 obj
  <</Rotate 90
    /Type /Page
    /TrimBox [ 9.54 6.12 585.68 835.88 ]
    /Resources 8 0 R
    /CropBox [ 0 0 595.22 842 ]
    /ArtBox [ 9.54 18.36 585.68 842 ]
    /Contents [ 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R ]
    /Parent 6 0 R
    /MediaBox [ 0 0 595.22 842 ]
    /Annots 17 0 R
    /BleedBox [ 9.54 6.12 585.68 835.88 ]
  >>
endobj 

您会看到对内容流的引用数组。这是您必须添加新页面内容的位置。您可以操作现有流或创建新流并将其添加到该阵列。

(大多数PDF都会对其内容流进行压缩。因此,对于一般情况,您必须在对其进行处理之前对其进行解压缩。因此,在我看来,更简单的方法是启动新流。)

您选择操作PDF中未压缩的最后引用的流16 0:

16 0 obj
<</Length 37 0 R>>
stream
  S 1 0 0 1 13.183 0 cm 0 0 m
  [...]
  0 10 -10 -0 506.238 342.629 Tm
  .13333 .11765 .12157 scn
  -.0002 Tc
  .0006 Tw
  (the Bank and branch on which cheque is drawn\).)Tj

  /F1 2 Tf
  -15.1279 10.9462 Td
  (abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~!@#$%^&*aaaaaaaaaaaaa)Tj

  /F2 1 Tf
  015.1279 01.9462 Td
  (ANAabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789)Tj

  ET
endstream
endobj 

我收集的是你的附加内容,它们是底部的两个3行,首先选择一种字体,然后定位插入点,最后打印一组字母。

现在你说你添加了文本abc..z和ABC ... Z仅用于测试。但是字母b j k q v等没有出现在pdf中。第二次添加字母时问题变得更加明显;这里只有首都&#39; A&#39;和&#39; N&#39;显示。

The added letter groups

这是因为有问题的字体嵌入到PDF中---字体嵌入到PDF中,以允许不具有相关字体的系统上的PDF查看器显示PDF - - 但它们并非完全嵌入,只是该字体所需的字符子集。

让我们查找仅有&#39; N&#39;和&#39; A&#39;出现:

根据页面对象,页面资源可以在对象8 0中找到:

8 0 obj
  <</Font <</F1 45 0 R /TT2 46 0 R /F2 47 0 R>>
    /ExtGState <</GS2 48 0 R>>
    /ProcSet [ /PDF /Text ]
    /ColorSpace <</Cs6 49 0 R>>
  >>
endobj 

所以F2在47 0中定义:

47 0 obj
  <</Subtype /Type1
    /Type /Font
    /Widths [ 722 250 250 250 250 250 250 250 250 250 250 250 250 722 ]
    /Encoding 52 0 R
    /FirstChar 65
    /FontDescriptor 53 0 R
    /ToUnicode 54 0 R
    /BaseFont /ILBPOB+TimesNewRomanPSMT-Bold
    /LastChar 78
  >>
endobj 

在引用的ToUnicode地图54 0中,您会看到

54 0 obj
<</Length 55 0 R>>stream
  /CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo <<
  /Registry (AAAAAA+F2+0) /Ordering (T1UV) /Supplement 0 >> def
  /CMapName /AAAAAA+F2+0 def
  /CMapType 2 def
  1 begincodespacerange <41> <4e> endcodespacerange
  2 beginbfchar
  <41> <0041>
  <4e> <004E>
  endbfchar
  endcmap CMapName currentdict /CMap defineresource pop end end
endstream
endobj 

在此映射中,您会看到只有字符代码0x41&#39; A&#39;和0x4e&#39; N&#39;映射

在您的文档中,字体仅用于打印&#34; NA&#34;在表格单元格中,没有别的。因此,只有那两个字母“N&#39; N&#39;和&#39; A&#39;嵌入,这导致您添加该字体只输出这些字母。

因此,要成功地向页面添加文本,您必须检查与页面关联的字体资源以及它们提供的字形(并限制您对这些字形的添加),或者您必须添加自己的字体资源。

由于编码中字符的存在通常不像在这里那样容易看到(ToUnicode是可选的),我建议你添加自己的字体资源。 PDF规范ISO 32000-1解释了如何做到这一点。

此外,您说明文本的 x和y轴位置未正确显示在pdf中。虽然您没有说出具体含义,但您应该注意内容流可以将仿射变换应用于页面的坐标系,即拉伸,倾斜,旋转和移动轴。

如果您想使用原始坐标系并且不依赖于您添加的坐标,则应将初始内容流添加到包含 q 运算符的页面(到将当前图形状态保存在图形状态堆栈上,并使用 Q 运算符在新的最终内容流中开始添加(通过删除最多来恢复图形状态最近从堆栈中保存状态并使其成为当前状态

编辑作为示例,我在您的modified1.pdf顶部应用了Java等效的C#代码并激活了追加模式。结果更改或添加了以下对象:

页面对象7 0已更新:

7 0 obj
  <</CropBox[0 0 595.22 842]
    /Parent 6 0 R
    /Contents[69 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 70 0 R]
    /Type/Page
    /Resources<<
      /ExtGState<</GS2 48 0 R>>
      /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
      /ColorSpace<</Cs6 49 0 R>>
      /Font<</F1 45 0 R/F2 47 0 R/TT2 46 0 R/Xi0 68 0 R>>
    >>
    /MediaBox[0 0 595.22 842]
    /TrimBox[9.54 6.12 585.68 835.88]
    /BleedBox[9.54 6.12 585.68 835.88]
    /Annots 17 0 R
    /ArtBox[9.54 18.36 585.68 842]
    /Rotate 90
  >>
endobj 

如果您与之前的版本进行比较,则会看到

  • 添加了两个新的内容流,开始时为69 0,最后为70 0;
  • 资源不再是间接对象,而是直接包含在这里;
  • 资源在68 0处包含新的字体资源Xi0。

现在让我们看一下添加的对象。

这是Helvetica-Bold的字体资源,命名为Xi0 at 68 0:

68 0 obj
  <</BaseFont/Helvetica-Bold
    /Type/Font
    /Encoding/WinAnsiEncoding
    /Subtype/Type1
  >>
endobj 

非嵌入式标准14种字体资源根本不复杂......

现在还有其他内容流。 iText会压缩它们,但我会在这里以未压缩的状态显示它们:

69 0 obj
<</Length 1>>stream
  q
endstream
endobj
70 0 obj
<</Length 106>>stream 
  Q
  q
  0 1 -1 0 595.22 0 cm
  q
  BT
  1 0 0 1 36 540 Tm
  /Xi0 12 Tf
  0.75 g
  (Hello people!)Tj
  0 g
  ET
  Q
  Q
endstream
endobj 

因此,开始时的新内容流存储当前图形状态,最后的新内容流检索存储状态,更改坐标系,文本插入位置,选择字体,字体大小和填充颜色,最后打印一个字符串。