追加模式要求文档没有错误,即使可以恢复也是如此
Append mode requires a document without errors, even if recovery is possible
我用append方式签名的PDF是从Office Word 2016导出的
这是我的文件:word.pdf
我收到了这条错误信息:
com.itextpdf.kernel.PdfException: Append mode requires a document without errors, even if recovery is possible.
我正在使用 iText7 7.0.4。
您尝试在追加模式下更改的文档已损坏。很可能,交叉引用中定义的字节偏移量 table 与 PDF 对象的实际字节位置不对应。
在你的例子中,我在文件末尾看到一些奇怪的东西:
xref
0 26
0000000010 65535 f
0000000017 00000 n
0000000166 00000 n
0000000222 00000 n
0000000492 00000 n
0000000755 00000 n
0000000932 00000 n
0000001180 00000 n
0000001233 00000 n
0000001286 00000 n
0000000011 65535 f
0000000012 65535 f
0000000013 65535 f
0000000014 65535 f
0000000015 65535 f
0000000016 65535 f
0000000017 65535 f
0000000018 65535 f
0000000019 65535 f
0000000020 65535 f
0000000000 65535 f
0000001961 00000 n
0000002154 00000 n
0000044863 00000 n
0000048000 00000 n
0000048045 00000 n
trailer
<</Size 26/Root 1 0 R/Info 9 0 R/ID[<8812105F6F93284DAEF240C8C1FC4C4E><8812105F6F93284DAEF240C8C1FC4C4E>] >>
startxref
48341
%%EOF
xref
0 0
trailer
<</Size 26/Root 1 0 R/Info 9 0 R/ID[<8812105F6F93284DAEF240C8C1FC4C4E><8812105F6F93284DAEF240C8C1FC4C4E>] /Prev 48341/XRefStm 48045>>
startxref
49017
%%EOF
您有一个包含两个预告片的 PDF。一个预告片声称交叉引用 table 存储在流中:
/XRefStm 48045
同时指示交叉引用的开始 table 在字节位置 49017:
startxref
49017
另一个预告片声称有一个未压缩的交叉引用 table 并且它从字节位置 48341 开始:
startxref
48341
确实:存在未压缩的交叉引用流:
xref
0 26
0000000010 65535 f
0000000017 00000 n
您了解文件中的不一致之处吗?
当您使用附加模式时,iText 不会更改原始文档的任何内容:一个字节都没有更改;在原始文件的最后一个 %%EOF
标记之后添加新字节。但是,当原始文件损坏时,iText 拒绝这样做。我希望您理解其中的原理:如果 iText 允许您这样做,您会使情况变得更糟。
要解决这个问题,您需要先修复损坏的文件。这可以通过 "manipulating" 文档完成而不更改任何内容,但是 在正常模式下执行此操作,而不是在追加模式下执行此操作。
您是否尝试过删除多余的预告片。我扔掉了:
xref
0 0
trailer
<</Size 26/Root 1 0 R/Info 9 0 R/ID[<8812105F6F93284DAEF240C8C1FC4C4E><8812105F6F93284DAEF240C8C1FC4C4E>] /Prev 48341/XRefStm 48045>>
startxref
49017
%%EOF
Adobe Reader 删除这些字节后没有抱怨。
这实际上是 iText 7 中的一个错误,它创建了对混合引用文件的增量更新。
错误情况
不幸的是,问题中的描述没有清楚地描述重现错误的方法。因此,可以像这样重现错误:
在附加模式中标记 OP 的 sample document(它不需要是签名用例)。
此步骤不会但会产生有问题的错误。
在附加模式中再次标记步骤 1 的输出(同样不需要用于签名)。
这一步例外
com.itextpdf.kernel.PdfException: Append mode requires a document without errors, even if recovery is possible.
出现。
有问题的 PDF
OP 的 PDF 很特别,因为它是一个混合参考文件。根据PDF规范(ISO 32000-1)这样的文件
is readable by readers designed only to support versions of PDF before PDF 1.5. Such a file contains objects referenced by standard cross-reference tables in addition to objects in object streams that are referenced by cross-reference streams.
对于这些文件,startxref 偏移指向 pre-1.5 交叉引用 table 和尾部 XRefStm 1.5 交叉引用流的入口点。
PDF 规范还规定
the XRefStm entry shall not be used in the trailer dictionary of the main cross-reference section but only in an update cross-reference section.
因此文件中看起来很有趣的结构:
18 0 obj
<</Type/ObjStm/N 10/First 67/Filter/FlateDecode/Length 357>>
stream
[...object stream data...]
endstream
endobj
[...]
25 0 obj
<</Type/XRef/Size 25/W[ 1 4 2] /Root 1 0 R/Info 9 0 R/ID[<8812105F6F93284DAEF240C8C1FC4C4E><8812105F6F93284DAEF240C8C1FC4C4E>] /Filter/FlateDecode/Length 97>>
stream
[...cross reference stream data...]
endstream
endobj
xref
0 26
[...cross reference table with 25 entries, objects in object stream are marked free...]
trailer
<</Size 26/Root 1 0 R/Info 9 0 R/ID[<8812105F6F93284DAEF240C8C1FC4C4E><8812105F6F93284DAEF240C8C1FC4C4E>] >>
startxref
[points to the preceding cross reference table]
48341
%%EOF
xref
0 0
[...empty incremental update cross reference table...]
trailer
[XRefStm points to the cross reference stream in object 25]
<</Size 26/Root 1 0 R/Info 9 0 R/ID[<8812105F6F93284DAEF240C8C1FC4C4E><8812105F6F93284DAEF240C8C1FC4C4E>] /Prev 48341/XRefStm 48045>>
startxref
[points to the empty incremental update cross reference table]
49017
%%EOF
因此,虽然看起来很有趣,但这个结构是正确的。
出了什么问题
阅读原始文档时,iText 7 识别出该文档同时包含交叉引用 table 和交叉引用流,并选择交叉引用流。 (实际上PdfReader.readXrefSection
先读取空的交叉引用table,然后在trailer中找到XRefStm入口,然后读取交叉引用流。)
创建增量更新时,iText 7 会记住源 PDF 已通过交叉引用流进行解析,因此使用完全压缩,即特别是它使用对象流和交叉引用流。
创建该交叉引用流时,它会将其 Prev 条目设置为原始 PDF 的最终 startxref 指向的内容,即空交叉引用 table,而不是它实际使用的交叉引用流。
但是不允许这样的混合结构(交叉引用流指向交叉引用 table 作为 Prev)。
因此,iText 在第一步中在其结果文档中创建了一个无效的交叉引用结构,因此,在第二步中发现了一个损坏的 PDF 来处理并投诉。
因此,由于此页面在错误的搜索结果中排名很高,但没有提供太多恢复步骤,所以我想我发布了如何解决此问题。
鉴于:
- 从您不一定能控制的地方收到“损坏的”文件;和
- 需要对这些文件做些什么
我发现 iTextSharp 中的某些操作会强制恢复。这并不完全“干净”,因为它修改了文件,但由于您收到此错误,您可能更关心完成某些事情而不是完全符合 PDF 规范。
解决方法基本上就是向 PDF 添加一段空文本。就我而言,这让我可以继续处理我收到的文件。
public byte[] InsertEmptyText(byte[] file)
{
var customText = new CustomText();
customText.FontSize = 1;
customText.Align = TextAlign.Right;
customText.Text = "";
customText.StartingPointPosition = new System.Drawing.Point(50, 50);
customText.PageNumber = 1;
PdfInsertObject pi = new PdfInsertObject();
pi.LoadPdfDocument(file);
pi.AddText(customText);
return pi.InsertObjects();
}
在创建 PdfReader 之前通过它传递数据(并捕获结果)让我继续。
我用append方式签名的PDF是从Office Word 2016导出的
这是我的文件:word.pdf
我收到了这条错误信息:
com.itextpdf.kernel.PdfException: Append mode requires a document without errors, even if recovery is possible.
我正在使用 iText7 7.0.4。
您尝试在追加模式下更改的文档已损坏。很可能,交叉引用中定义的字节偏移量 table 与 PDF 对象的实际字节位置不对应。
在你的例子中,我在文件末尾看到一些奇怪的东西:
xref
0 26
0000000010 65535 f
0000000017 00000 n
0000000166 00000 n
0000000222 00000 n
0000000492 00000 n
0000000755 00000 n
0000000932 00000 n
0000001180 00000 n
0000001233 00000 n
0000001286 00000 n
0000000011 65535 f
0000000012 65535 f
0000000013 65535 f
0000000014 65535 f
0000000015 65535 f
0000000016 65535 f
0000000017 65535 f
0000000018 65535 f
0000000019 65535 f
0000000020 65535 f
0000000000 65535 f
0000001961 00000 n
0000002154 00000 n
0000044863 00000 n
0000048000 00000 n
0000048045 00000 n
trailer
<</Size 26/Root 1 0 R/Info 9 0 R/ID[<8812105F6F93284DAEF240C8C1FC4C4E><8812105F6F93284DAEF240C8C1FC4C4E>] >>
startxref
48341
%%EOF
xref
0 0
trailer
<</Size 26/Root 1 0 R/Info 9 0 R/ID[<8812105F6F93284DAEF240C8C1FC4C4E><8812105F6F93284DAEF240C8C1FC4C4E>] /Prev 48341/XRefStm 48045>>
startxref
49017
%%EOF
您有一个包含两个预告片的 PDF。一个预告片声称交叉引用 table 存储在流中:
/XRefStm 48045
同时指示交叉引用的开始 table 在字节位置 49017:
startxref
49017
另一个预告片声称有一个未压缩的交叉引用 table 并且它从字节位置 48341 开始:
startxref
48341
确实:存在未压缩的交叉引用流:
xref
0 26
0000000010 65535 f
0000000017 00000 n
您了解文件中的不一致之处吗?
当您使用附加模式时,iText 不会更改原始文档的任何内容:一个字节都没有更改;在原始文件的最后一个 %%EOF
标记之后添加新字节。但是,当原始文件损坏时,iText 拒绝这样做。我希望您理解其中的原理:如果 iText 允许您这样做,您会使情况变得更糟。
要解决这个问题,您需要先修复损坏的文件。这可以通过 "manipulating" 文档完成而不更改任何内容,但是 在正常模式下执行此操作,而不是在追加模式下执行此操作。
您是否尝试过删除多余的预告片。我扔掉了:
xref
0 0
trailer
<</Size 26/Root 1 0 R/Info 9 0 R/ID[<8812105F6F93284DAEF240C8C1FC4C4E><8812105F6F93284DAEF240C8C1FC4C4E>] /Prev 48341/XRefStm 48045>>
startxref
49017
%%EOF
Adobe Reader 删除这些字节后没有抱怨。
这实际上是 iText 7 中的一个错误,它创建了对混合引用文件的增量更新。
错误情况
不幸的是,问题中的描述没有清楚地描述重现错误的方法。因此,可以像这样重现错误:
在附加模式中标记 OP 的 sample document(它不需要是签名用例)。
此步骤不会但会产生有问题的错误。
在附加模式中再次标记步骤 1 的输出(同样不需要用于签名)。
这一步例外
com.itextpdf.kernel.PdfException: Append mode requires a document without errors, even if recovery is possible.
出现。
有问题的 PDF
OP 的 PDF 很特别,因为它是一个混合参考文件。根据PDF规范(ISO 32000-1)这样的文件
is readable by readers designed only to support versions of PDF before PDF 1.5. Such a file contains objects referenced by standard cross-reference tables in addition to objects in object streams that are referenced by cross-reference streams.
对于这些文件,startxref 偏移指向 pre-1.5 交叉引用 table 和尾部 XRefStm 1.5 交叉引用流的入口点。
PDF 规范还规定
the XRefStm entry shall not be used in the trailer dictionary of the main cross-reference section but only in an update cross-reference section.
因此文件中看起来很有趣的结构:
18 0 obj
<</Type/ObjStm/N 10/First 67/Filter/FlateDecode/Length 357>>
stream
[...object stream data...]
endstream
endobj
[...]
25 0 obj
<</Type/XRef/Size 25/W[ 1 4 2] /Root 1 0 R/Info 9 0 R/ID[<8812105F6F93284DAEF240C8C1FC4C4E><8812105F6F93284DAEF240C8C1FC4C4E>] /Filter/FlateDecode/Length 97>>
stream
[...cross reference stream data...]
endstream
endobj
xref
0 26
[...cross reference table with 25 entries, objects in object stream are marked free...]
trailer
<</Size 26/Root 1 0 R/Info 9 0 R/ID[<8812105F6F93284DAEF240C8C1FC4C4E><8812105F6F93284DAEF240C8C1FC4C4E>] >>
startxref
[points to the preceding cross reference table]
48341
%%EOF
xref
0 0
[...empty incremental update cross reference table...]
trailer
[XRefStm points to the cross reference stream in object 25]
<</Size 26/Root 1 0 R/Info 9 0 R/ID[<8812105F6F93284DAEF240C8C1FC4C4E><8812105F6F93284DAEF240C8C1FC4C4E>] /Prev 48341/XRefStm 48045>>
startxref
[points to the empty incremental update cross reference table]
49017
%%EOF
因此,虽然看起来很有趣,但这个结构是正确的。
出了什么问题
阅读原始文档时,iText 7 识别出该文档同时包含交叉引用 table 和交叉引用流,并选择交叉引用流。 (实际上PdfReader.readXrefSection
先读取空的交叉引用table,然后在trailer中找到XRefStm入口,然后读取交叉引用流。)
创建增量更新时,iText 7 会记住源 PDF 已通过交叉引用流进行解析,因此使用完全压缩,即特别是它使用对象流和交叉引用流。
创建该交叉引用流时,它会将其 Prev 条目设置为原始 PDF 的最终 startxref 指向的内容,即空交叉引用 table,而不是它实际使用的交叉引用流。
但是不允许这样的混合结构(交叉引用流指向交叉引用 table 作为 Prev)。
因此,iText 在第一步中在其结果文档中创建了一个无效的交叉引用结构,因此,在第二步中发现了一个损坏的 PDF 来处理并投诉。
因此,由于此页面在错误的搜索结果中排名很高,但没有提供太多恢复步骤,所以我想我发布了如何解决此问题。
鉴于:
- 从您不一定能控制的地方收到“损坏的”文件;和
- 需要对这些文件做些什么
我发现 iTextSharp 中的某些操作会强制恢复。这并不完全“干净”,因为它修改了文件,但由于您收到此错误,您可能更关心完成某些事情而不是完全符合 PDF 规范。
解决方法基本上就是向 PDF 添加一段空文本。就我而言,这让我可以继续处理我收到的文件。
public byte[] InsertEmptyText(byte[] file)
{
var customText = new CustomText();
customText.FontSize = 1;
customText.Align = TextAlign.Right;
customText.Text = "";
customText.StartingPointPosition = new System.Drawing.Point(50, 50);
customText.PageNumber = 1;
PdfInsertObject pi = new PdfInsertObject();
pi.LoadPdfDocument(file);
pi.AddText(customText);
return pi.InsertObjects();
}
在创建 PdfReader 之前通过它传递数据(并捕获结果)让我继续。