【问题标题】:Java package com.itext.pdf has rendering/displaying issue while replacing contentJava 包 com.itext.pdf 在替换内容时出现渲染/显示问题
【发布时间】:2021-03-19 13:25:15
【问题描述】:

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

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

例如:日期 2020 年 11 月 28 日的渲染如下所示(每次运行的渲染略有变化 - 在公共或空格或数字级别):

我尝试过的事情:

  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;
}

【问题讨论】:

  • 你说你要替换日期。如果您以幼稚的方式执行此操作,即通过在内容流中进行搜索和替换,则会出现此类问题,例如参见 here

标签: java pdf fonts itext


【解决方案1】:

您既没有展示您的代码,也没有展示任何示例 PDF,因此无法认真分析情况。但是,当您仍然提供赏金时,您似乎也对(受过教育的)猜测感兴趣。因此,我来​​了:

问题

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

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

一般的解决方案

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

此外,this 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);

【讨论】:

  • 感谢@mkl 的回复,我已经添加了代码sn-p。
  • @MandarPande 我编辑了我的答案。对于您的代码,即使未嵌入字体,字体也会被子集化。如我的编辑中所述,您可以通过相应地设置 Subset 属性来简单地停用子集。
  • 谢谢你的建议,我今天去看看。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多