Java 包 com.itext.pdf 在替换内容时出现 rendering/displaying 问题

Java package com.itext.pdf has rendering/displaying issue while replacing content

我正在使用 java 中的 com.itextpdf 库来生成和编辑 PDF。 我面临一个有线问题:PDF 内容(日期)未 rendered/displayed 正确地包含在 PDF 中。

我最初创建了一个仅使用 itext 的 PDF 文件,后来在 post 处理中 - 替换了 PDF 内容(日期)。

例如:日期 2020 年 11 月 28 日呈现如下(每个 运行 的轻微渲染变化 - 在普通或 space 或数字级别):

我尝试过的事情:

  1. 从旧版本升级 itext:5.5.6 和最新版本:5.5.13.2
  2. 尝试了多种字体。
  3. 编码风格:UTF-8ISO-8859-1,还是不行。

任何指针都会有所帮助。

   //initial placeholder:    
   String TEMPORARY_DATE_PLACE_HOLDER = "----------------";
   //BaseFont (tried with both embedded as true / false):
  BaseFont.createFont("/arial.ttf", BaseFont.WINANSI, false);
    -
    -
    -
  // post processing: where the placeholder is replaced.
    reader = new PdfReader(InputPDF);
    PdfDictionary dict   = reader.getPageN(1);
    PdfObject     object = dict.getDirectObject(PdfName.CONTENTS);
        if (object instanceof PRStream) {
           PRStream stream     = (PRStream) object;
            byte[]   data       = PdfReader.getStreamBytes(stream);
            String CHARACTER_ENCODING_SET = "ISO-8859-1";
            String   dataString = new String(data, CHARACTER_ENCODING_SET);
            
            if ( dateFormatList.contains(requiredDate)) {
                dataString = dataString.replaceAll(TEMPORARY_DATE_PLACE_HOLDER, new SimpleDateFormat(dateFormat).format(requiredDate));
            }   
        stream.setData(dataString.getBytes(CHARACTER_ENCODING_SET));
    }
    
    stamper = new PdfStamper(reader, out);
    stamper.close();
    reader.close();
    byte[] fileContent = out.toByteArray();
    helperToWrite(new ByteArrayInputStream(fileContent), "OutputPDF");
    
    //Helper method to write into File:
    private File helperToWrite(nputStream inputStream, String name){
    try (OutputStream outputStream = new FileOutputStream(file)) {
                int    read  = 0;
                byte[] bytes = new byte[1024];
    
                while ((read = inputStream.read(bytes)) != -1) {
                    outputStream.write(bytes, 0, read);
                }
            } catch (Exception e) {
            }
            return file;
}

你既没有展示你的代码,也没有任何示例PDF,所以无法认真分析情况。但是由于您仍然提供了赏金,您似乎也对(有根据的)猜测感兴趣。因此,我开始了:

问题

你说你要替换日期。考虑到您的问题,我假设您采用的是天真的方式,即通过在内容流中搜索和替换。

如果是这种情况,您可以找到最合理的解释 为什么 发生在 this old answer 中:要替换的文本使用的字体是子集嵌入,即仅将原始文档中实际使用的字体字形嵌入到文档中。另一方面,您的替换文本包含此子集未涵盖的字符。因此,这些字符丢失了。

一般的解决方案

上面提到的答案也解释了通常要做什么:首先使用带坐标的文本提取确定要替换的文本的坐标,然后删除该文本,例如通过编辑,最后使用自己的字体对象添加替换。

此外, 更详细地总结了在 PDF 中对文本进行通用搜索和替换时必须解决的问题。

因此,到目前为止,您的问题与这些问题重复。

针对您的特殊情况的另一种解决方案

虽然有一个方面不同,但您说您是 "使用 java 中的 com.itextpdf 库来 生成和编辑 PDF",即您不必编辑任意 PDF,而只需编辑您使用 iText 自己生成的 PDF。因此,您还可以创建不同的原始 PDF,使其更适合您以后的编辑!

要以更适合文本搜索和替换的方式生成模板 PDF,要么根本不子集嵌入字体,要么确保嵌入的子集足够大以容纳您计划的替换文本。

如果在您的 BaseFont.createFont 调用中将布尔参数 embedded 设置为 true (== BaseFont.EMBEDDED) 或者如果您设置字符串参数 encoding 到“Identity-H”(== BaseFont.IDENTITY_H)或“Identity-V”(== BaseFont.IDENTITY_V)。对于前一种方法,您必须避免这种情况。

作为子集的 iText 嵌入字体包含 PDF 内容流中显示说明的文本所需的所有字形。为确保某些字形在子集中,只需将其绘制在某处即可。因此,对于后一种方法,将替换文本中可能需要的所有字符收集到一个字符串中并绘制出来。您可以不可见地绘制它(白色对白色,渲染模式不可见,被某些东西覆盖,在剪辑路径或裁剪框之外,......)或者您可以在搜索和替换过程中将其删除。

即使是非嵌入式字体,iText 在某些情况下也会创建子集。因此,即使对于非嵌入字体,您也应该明确禁用子集化。

已应用于您的代码

您同时分享了您的关键代码。事实上,我可以用它重现这个问题。问题是你的 BaseFont

BaseFont.createFont("/arial.ttf", BaseFont.WINANSI, false)

即使未嵌入也已被子集化。因此,请改用以下内容:

BaseFont baseFont = BaseFont.createFont("/arial.ttf", BaseFont.WINANSI, false);
baseFont.setSubset(false);