Puppeteer生成的PDF复制/粘贴文本产生奇怪的字符

时间:2018-03-27 20:12:25

标签: javascript pdf puppeteer

我正在使用最新版本的puppeteer来生成附加的PDF。当我在Adobe Acrobat中打开它并尝试复制并粘贴文本时,它会变为

  

这是一个测试字符串。

  

Ţħįşįşǻţěşţşţřįňģ。

以下是我生成PDF的代码。

const puppeteer = require('puppeteer');
const argv = require('minimist')(process.argv.slice(2));
const fileName = argv.fileName || "page";
const timeout = 90;

(async () => {
  var pageUrl = "my-url-here"
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  function onTimeout() {
    console.log("Timed out waiting for data after " + timeout + " seconds.");
    process.exit();
  }
  
  console.log("Opening " + pageUrl);
  await page.goto(pageUrl, {waitUntil: 'networkidle2'});
  console.log("Waiting for page to load...");
  
  console.log("Waiting for data to load...");
  await page.waitForSelector('#print-report-loaded', {timeout:timeout*1000}).catch(onTimeout);
  
  var fileFullName = fileName + ".pdf";
  console.log("Saving PDF as " + fileFullName);
  await page.pdf({path: fileFullName});
  console.log("PDF saved successfully as " + fileFullName);

  await browser.close();
})();

Here is link to the generated PDF

任何想法如何解决将不胜感激!

1 个答案:

答案 0 :(得分:1)

Acrobat不会更改文本,它只会复制为这些字体存储的Unicode字符。 '字符'你看到的是Type 3的大纲,他们的形式是"常规"字符,但它们相关的Unicode代码点确实是那些重音字符。

从Acrobat Reader和官方PDF规范的角度来看,一切都是"按设计工作"。

让我们来看看你的PDF。

为了让事情变得不必要,人们会认为这只需要一种字体,但是你的工具生成了两个字体:F0,它将字符代码映射到以下Unicode代码

<(01)> <( )>
<(0D)> <(.)>
<(26)> <(Ț)>
<(32)> <(ǻ)>
<(35)> <(ě)>
<(37)> <(ģ)>
<(38)> <(ħ)>
<(39)> <(į)>
<(3E)> <(ň)>
<(42)> <(ț)>

F1映射到

<(15)> <(ř)>
<(16)> <(ș)>

字符代码以字符串形式写出,一次一个字符(中间有几个命令;这里省略,因为不相关):

<26><38><39>{16}<01><39>{16}<01><32><01><42><35>{16}<42><01>{16}<42>{15}<39><3E><37><0D>

我指出<..>内配对的十六进制代码来自字体F0{..}来自F1。现在,如果使用Unicode字符逐个替换字符索引,则确实会获得Unicode字符串

Țħįș įș ǻ țěșț șțřįňģ.

&#34;字体&#34;这里使用的是Type 3 PostScript字体,完全嵌入PDF中。例如,字体#0定义为

8 0 obj @ 1059      % "F0"
<<
  /Type     /Font
  /Subtype  /Type3
  /CIDToGIDMap  /Identity
  /CharProcs    
  <<
    /g0     11 0 R      % -> stream
    /g1     12 0 R      % -> stream
    /g26    14 0 R      % -> stream
    /g32    15 0 R      % -> stream
    /g35    16 0 R      % -> stream
    /g37    17 0 R      % -> stream
    /g38    18 0 R      % -> stream
    /g39    19 0 R      % -> stream
    /g3E    20 0 R      % -> stream
    /g42    21 0 R      % -> stream
    /gD     13 0 R      % -> stream
  >>
  /Encoding     
  <<
    /Type   /Encoding
    /Differences [ 0 /g0 /g1 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /gD /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0
        /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g26 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0 /g0
        /g0 /g32 /g0 /g0 /g35 /g0 /g37 /g38 /g39 /g0 /g0 /g0 /g0 /g3E /g0 /g0 /g0 /g42 ]
  >>
  /FirstChar    0
  /FontBBox     [ -1 202 598 -801 ]
  /FontDescriptor 10 0 R        % -> FontDescriptor (Font)
  /FontMatrix   [ 0.082254 0 0 -0.082254 0 0 ]
  /LastChar     66
  /ToUnicode    9 0 R       % -> stream
  /Widths   [ 500 300 0 0 0 0 0 0 0 0 0 0 0 244 0 0 0 0 0 0 0 0 0 0 0 0
      0 0 0 0 0 0 0 0 0 0 0 0 641 0 0 0 0 0 0 0 0 0 0 0 579 0
      0 592 0 664 616 263 0 0 0 0 616 0 0 0 404 ]
>>
endobj

除了编码数组之外,其中大部分都不重要,它告诉字符索引与什么字形名称以及CharProcs数组相关联,它将编码数组中的名称与实际绘图指令相关联。这是来自&#34;字体名称加字符索引&#34;的路线。在字符串中显示&#34;编码&#34;中的字符索引,然后使用ToUnicode数组查找每个字符的实际(报告的)Unicode值。< / p>

每个字符本身的绘图说明(对每个/gX流的引用)是movelinefill指令的常规系列 - 再次,没有虽然其他PDF引擎更常包含原始字体,而不仅仅是文字绘图说明。

但是ToUnicode表弄乱了复制的东西。而不是说明字符16#26映射到Unicode U + 0054&Latin Capital T&#39;&#34;,它指向&#34; U + 021A Latin Capital T,逗号低于& #34; - 并且没有明显的原因!它肯定不是随机翻译,但我找不到任何合理的解释,为什么人们会故意编码纯文本......除非有人在那里咧着嘴笑内容和思考,&#34;是的,这我的想法&#34; - 在这种情况下,它将是故意混淆

Github上的Puppeteer代码似乎并不处理PDF本身,因此必须由Chromium处理,Chromium内部使用Skia PDF引擎(据报道,但由PDF二进制标题证实,其中包含#34; D3 EB E9 E1&#34; - &#34; Skia&#34;,当最高位被清零时)。它已被报告为一个错误as early as 2012,但有2017年的报告,这似乎表明这不是一个迫切需要解决的问题。