pdfjs 在包含非标准字符时无法查看 PDF

pdfjs can't view PDF when containing non standard characeters

解决方案: 要使 HTML 代码正常工作,请交换“reader.readAsText” 对于“reader.readAsBinaryString”,如@KJ 的回答

中所述

我正在尝试使用 PDFJS 查看 PDF。我有以下代码,它适用于我从 PDFJS 网站获得的演示 PDF,但它不适用于我尝试过的其他 PDF。这是有效的演示 PDF 的原始文本:

%PDF-1.7
1 0 obj  % entry point
<</Type/Catalog/Pages 2 0 R>>
endobj
2 0 obj<</Type/Pages/MediaBox[ 0 0 200 200]/Count 1/Kids[3 0 R]>>endobj
3 0 obj<</Type/Page/Parent 2 0 R/Resources<</Font<</F1 4 0 R>>>>/Contents 5 0 R>>endobj
4 0 obj<</Type/Font/Subtype/Type1/BaseFont/Times-Roman>>endobj
5 0 obj  % page content
<</Length 44>> stream
BT 70 50 TD /F1 12 Tf(Hello, world!) Tj ET
endstream endobj
xref trailer <</Size 6/Root 1 0 R>> startxref
%%EOF

这是我的 html 成功加载上述 PDF 的代码:

<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.228/pdf.js"></script>
<input type="file" id="input"/> </br> <canvas id="can" width=1000 height=1000/>
<script>
    document.getElementById('input').addEventListener('change', function(e){
        var reader = new FileReader()
        reader.onload = function(x){
            window['pdfjs-dist/build/pdf'].getDocument({data:x.target.result}).promise.then(function(pdf){
                pdf.getPage(1).then(function(page){
                    page.render({canvasContext:document.getElementById('can').getContext('2d'),
                        viewport:page.getViewport({scale:1})})
        })})}
        reader.readAsText(e.target.files[0])
    }, false)
</script>

但是,我的其他 PDF 根本无法加载。例如,我生成了一个 1 页的 PDF,在背面仅包含单词 'TEST' 并下载了它。当我尝试将此 PDF 上传到我的 html 代码时,我在控制台中收到以下错误:

Warning: Invalid stream: "FormatError: Bad FCHECK in flate stream: 120, 253"
util.js:306 Warning: Indexing all PDF objects
2util.js:306 Warning: Invalid stream: "FormatError: Bad FCHECK in flate stream: 120, 253"
viewPDF.html:1 Uncaught (in promise) InvalidPDFException {name: "InvalidPDFException", message: "Invalid PDF structure"}
Promise.then (async)
reader.onload @ viewPDF.html:7
load (async)
(anonymous) @ viewPDF.html:6

我怀疑我遇到的问题与无法使用的 PDF 包含非标准字符有关。这是背面 PDF 的前几行:

%PDF-1.5
%���
3 0 obj
<< /Linearized 1 /L 11602 /H [ 678 125 ] /O 7 /E 11072 /N 1 /T 11321 >>
endobj

4 0 obj
<< /Type /XRef /Length 51 /Filter /FlateDecode /DecodeParms << /Columns 4 /Predictor 12 >> /W [ 1 2 1 ] /Index [ 3 14 ] /Info 1 0 R /Root 5 0 R /Size 17 /Prev 11322                 /ID [<8f1689fb6a16051fd66ebeadaa364b8d><4a8030207ba6597007a967ed52a9309d>] >>
stream
x�cbd�g`b`8 $��XF@���*��    ��@�Y�����v�#�.
endstream
endobj

5 0 obj
<< /Pages 14 0 R /Type /Catalog >>
endobj
6 0 obj
<< /Filter /FlateDecode /S 36 /Length 48 >>
stream
x�c```e``Z��
            pe31
                B�����,��v�>aW�

编辑:

可以在 Firefox、Chromium、mac 的预览应用程序等中查看背面的 PDF。从这个意义上讲,PDF 格式正确。一般来说,我希望能够使用格式正确的 PDF,因为它们可以被标准的 PDF 查看器查看。如果 PDFJS 只能处理在某种更严格意义上正确格式化的 PDF,那么在我看来,这要么是 PDFJS 中的错误,要么是缺乏功能。如果是这种情况,我想要一个 PDFJS 的替代品,我可能会尝试使用它。但我怀疑情况并非如此,如果我实际上错误地将上传的文件交给 PDFJS,这会导致我遇到的问题。

为了给您更多背景信息,我的目标是构建一个简单的 html 应用程序,允许用户上传他们的任何 PDF 文件并进行查看。从这里可以看出:'https://github.com/mozilla/pdf.js#online-demo' 它似乎适用于各种 PDF,但我自己却无法让它工作。

特别是,我希望用户能够查看可在标准 PDF 查看器(例如网络浏览器(例如:firefox 等))中查看的任何 PDF。我希望从用户本地计算机加载 PDF,而不是 url link。我希望这个澄清有所帮助。如果有任何问题,请告诉我。谢谢你的时间。

TL;DR跳到最后

您输出编码的二进制流,如这些符号所见,并且随着您使 PDF 变得更复杂,它们将越来越需要数学字体、图像和普通嵌入字体。只要所有输出都被索引,就可以将它们以 ascii 代码输出并接受 table。您的背页代码也因 WEB /Linearized 输出而更加复杂。

PDF 的结构并不简单,您的最小工作示例应该看起来更像这样,其中包含外部参照 table。

%PDF-1.7
%µ¶

1 0 obj
<</Type/Catalog/Pages 2 0 R>>
endobj

2 0 obj
<</Type/Pages/MediaBox[0 0 200 200]/Count 1/Kids[3 0 R]>>
endobj

3 0 obj
<</Type/Page/Parent 2 0 R/Resources<</Font<</F1 4 0 R>>>>/Contents 5 0 R>>
endobj

4 0 obj
<</Type/Font/Subtype/Type1/BaseFont/Times-Roman>>
endobj

5 0 obj
<</Length 63>>
stream
q
BT
-50 TL
/F1 12 Tf
1 0 0 1 70 50 Tm
(Hello, world!) Tj
ET
Q

endstream
endobj

xref
0 6
0000000000 65536 f 
0000000016 00000 n 
0000000062 00000 n 
0000000136 00000 n 
0000000227 00000 n 
0000000293 00000 n 

trailer
<</Size 6/Root 1 0 R>>
startxref
405
%%EOF

此格式的主要问题是十进制字节地址需要正确,因此大文件中 \n \r\n 和 \r 之间的不同 OS 行结尾可能会显着改变这些值,这样一个字节错误,文件就损坏了。

InvalidPDFException 通常意味着以下两种情况之一:A) 错误的 PDF B) 错误的 pdf 处理如果您知道 pdf 是 100% 有效的(永远不确定)那么它必须是 B)二进制在某处被解析为 ASCII 文本类型但是一些 jslibs 期望/接受 PDF 作为 text.base64 然后将客户端转换为 binary.pdf

该错误表明二进制扁平化数据已被 ascii 处理损坏,大概在传输或接收时未存储为 application/pdf 数据,因为 pdf 的部分编码就像 zip 文件一样

终于

在讨论中我注意到脚本的最后一行说 readAsText() 和那些基于 ascii 的 PDF 正在工作,同时在不同的浏览器中测试上面脚本的许多变体(试图清除控制台注释)我测试的最简单的解决方案all(除了IE11不会玩球)是用reader.readAsBinaryString(e.target.files[0])代替它。但是我建议使用一个更完整的跨浏览器示例来处理密码等。对其他人来说会更好,但如果它对你有用,那就足够了。