【问题标题】:Error replacing text in PDF with NodeJS and hummus用 NodeJS 和鹰嘴豆泥替换 PDF 中的文本时出错
【发布时间】:2021-04-02 10:02:55
【问题描述】:

How do I replace a string in a PDF file using NodeJS? 有一个解决方案来替换 PDF 中的文本。使用相同的代码,我有一个令人费解的问题:文本在 PDF 的源代码中被替换但不呈现。改编自上述解决方案的相关行是:

  console.log(replaceText);
  var string = new Buffer(data).toString().replace(findText, replaceText);
  console.log(string);

控制台显示在PDF的源字符串中被替换:

/TT0 1 TF 65.5689 -24.5097 24.5097 65.5689 363.9941 762.3682 (e)Tj 61.1539 -34.0617 34.0617 61.1539 381.1689 756.6411 (n)Tj 54.8214 -43.5272 43.5272 54.8214 408.6333 741.0947 (d)Tj 48.8331 -50.153 50.153 48.8331 426.3779 726.999 (a)Tj 52 0 0 52 75.8203 226.9756 (abcdefghijklmnopqrstuvwxyz)Tj 33 0 0 33 25.8203 302.9756 (E)Tj (ste cheque-prenda, para:)Tj 1.818 -7.152 时差 (www.emocoes.org/abcdefghijklmnopqrstuvwxyz)Tj 外星人

PDF 看起来像这样:

在这种情况下缺少 K、X 和 Y。在 Adob​​e Illustrator 中打开文件显示它们仍在其他字母后面:

我找不到明确的模式:有时 H 和 J 在其他替换字符串中也会丢失,而丢失的字母与其他字体不同(我测试了 Open Sans 和 Times New Roman)。

问题是什么,我该如何解决?

我的代码是:

function customizeVoucher(findText, replaceText) {
  var sourceFile = path.join(__dirname, "../private/vouchers/custom-old.pdf");
  var link = "/vouchers/cheque-prenda-" + replaceText + ".pdf";
  var targetFile = path.join(__dirname, "../private" + link);
  var pageNumber = 0;
  
  var writer = hummus.createWriterToModify(sourceFile, {
    modifiedFilePath: targetFile,
    log: path.join(__dirname, "../hummus.md")
  });
  var sourceParser = writer.createPDFCopyingContextForModifiedFile().getSourceDocumentParser();
  var pageObject = sourceParser.parsePage(pageNumber);
  var textObjectId = pageObject.getDictionary().toJSObject().Contents.getObjectID();
  var textStream = sourceParser.queryDictionaryObject(pageObject.getDictionary(), 'Contents');
  //read the original block of text data
  var data = [];
  var readStream = sourceParser.startReadingFromStream(textStream);
  while(readStream.notEnded()){
    Array.prototype.push.apply(data, readStream.read(10000));
  }
  console.log(replaceText);
  var string = new Buffer(data).toString().replace(findText, replaceText);
  console.log(string);

  // Create and write our new text object.
  var objectsContext = writer.getObjectsContext();
  objectsContext.startModifiedIndirectObject(textObjectId);
  
  var stream = objectsContext.startUnfilteredPDFStream();
  stream.getWriteStream().write(strToByteArray(string));
  objectsContext.endPDFStream(stream);
  
  objectsContext.endIndirectObject();
  
  writer.end();

  return link;
}

源 PDF 是here

【问题讨论】:

  • 很可能仅将字体作为子集嵌入,仅存在最初需要的字形。这只是您提到的过于简单的文本替换方法失败的众多情况中的一种。
  • @mkl 确实如此:隐藏所有字形的文件不存在此问题。 Illustrator 可以渲染它可能是因为它具有源字体。你能写一个答案并推荐一个更可靠的解决方案吗?
  • 我可以写一个答案,但我没有更可靠的 Javascript 解决方案。
  • 没关系,能不能加个不是Javascript的更靠谱的方法?
  • 在带有 itext 的 Java 中,我首先应用带坐标的文本提取(以查找要替换的文本),在这些坐标处使用编辑删除文本,然后将替换添加为新文本。明年我回到办公室时,我可以写一些更详细的内容。

标签: javascript node.js pdf


【解决方案1】:

在您的示例 PDF 中,使用了两种字体,MyriadPro-RegularAmaticSC-Bold,它们都仅作为子集嵌入:

因此,当您使用代码替换显示说明的文本中的字符串时,只有在该说明之前所选字体的相应子集的字形在常规 PDF 查看器中可见。另一方面,用于编辑目的的 Adob​​e Illustrator 会使用完整的字体,如果它们在本地可用的话。

如果您自己创建了模板 PDF 并且仍然可以重新创建它,请执行此操作,但请确保嵌入了所有必需的字形。您可以通过在某处放置一个包含相应字体中所有必需字符的不可见字符串来确保;这通常会使 PDF 创建者嵌入所有这些字形。您可以通过使用文本渲染模式invisible、通过在白色上绘制白色、通过用其他东西覆盖、通过在剪辑路径之外或页面边界之外绘制来绘制不可见的字符串,...

如果您无法重新创建模板,您可以按照gal kahanaanswer you took your code from 引用的Hummus 问题"How to search and replace text within a document?" 中提出的建议:

棘手的部分是在字体定义中添加任何新字符。假设 PDF 仅具有渲染文本所需的字符,这可能意味着您需要知道使用了哪种原始字体……从 PDF 中实现它并不是很容易,但可以做到。进行实际嵌入...您可能最好使用鹰嘴豆泥创建一种新字体,具有相同的名称,并使用该字体编写所有文本。只需将放置旧字体的 Tf 命令替换为新字体,然后使用 Tjs 放置新文本

如果是您的示例 PDF,您有

/TT0 1 Tf
65.5689 -24.5097 24.5097 65.5689 363.9941 762.3682 Tm
(e) Tj
61.1539 -34.0617 34.0617 61.1539 381.1689 756.6411 Tm
(n) Tj
54.8214 -43.5272 43.5272 54.8214 408.6333 741.0947 Tm
(d) Tj
48.8331 -50.153 50.153 48.8331 426.3779 726.999 Tm
(a) Tj
52 0 0 52 75.8203 226.9756 Tm
(nomegenerico) Tj
33 0 0 33 25.8203 302.9756 Tm
(E) Tj
(ste cheque-prenda, para:) Tj
1.818 -7.152 Td
(www.emocoes.org/nomegenerico) Tj

因此,如果您使用 Hummus 将足够完整的 AmaticSC-Bold 字体副本添加到具有新名称的页面资源中,例如ASCB,然后替换

(nomegenerico) Tj

通过

/ASCB 1 Tf
(REPLACEMENT_TEXT) Tj
/TT0 1 Tf

还有

(www.emocoes.org/nomegenerico) Tj

通过

/ASCB 1 Tf
(www.emocoes.org/REPLACEMENT_TEXT) Tj
/TT0 1 Tf

听从 gal kahana 的建议。


注意:虽然上面讨论的方法很可能适用于您的模板,但一般情况要复杂得多,请参阅this answer 了解一些背景。

对于更通用的解决方案,您至少需要考虑字体编码。在您的 PDF 中,这两种字体都与 WinAnsiEncoding 一起使用,这与 Latin-1 非常相似,但通常每种字体都可以有自己的编码,并且该编码不必是标准编码,而是可能完全是定制一个。这要求您跟踪内容流中当前设置的字体,并从字体资源中查找相应的信息以正确解释以下文本字符串。

Gal kahana 在文章 "Extracting Text from PDF files" 中解释了如何使用 Hummus 做到这一点。对于通用文本替换方法,您“仅”必须扩展那里提供的代码以允许替换绘制特定文本片段的指令。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-07
    • 2020-09-04
    • 1970-01-01
    • 1970-01-01
    • 2013-01-09
    • 1970-01-01
    • 2013-01-03
    相关资源
    最近更新 更多