【问题标题】:PDFBox Inconsistent PDTextField Autosize Behavior after setValue设置值后 PDFBox 不一致的 PDTextField 自动调整大小行为
【发布时间】:2019-07-08 15:25:25
【问题描述】:

我正在使用 Apache PDFBox 在 PDF 文档上配置 PDTextField,我使用以下方法将 Lato 加载到文档中:

font = PDType0Font.load(
    @j_pd_document,
    java.io.FileInputStream.new('/path/to/Lato-Regular.ttf')
) # => Lato-Regular

font_name = pd_default_resources.add(font).get_name # => F4

然后,我将 font_name 传递给 default_appearance_stringPDTextField,如下所示:

j_text_field.set_default_appearance("/#{font_name} 0 Tf 0 g") # where font_name is
                                                              # passed in from above

当我继续在PDTextField 上调用setValue 时,现在会出现问题。因为我将defaultAppearanceString 中的font_size 设置为0,根据库的example,文本应该自行缩放以适合文本框的给定区域。但是,对于某些字段,这种“缩放以适应”的行为是不一致的:它并不总是选择最大的字体大小来适应PDTextField。是否有任何进一步的配置可能允许这种情况发生?以下是我注意到发生此问题的 PDF。

未填充,已加载字体: http://www.filedropper.com/0postfontload

已填充,文本框文本大小不一致: http://www.filedropper.com/file_327

旁注:我通过jruby 使用PDFBox,这只是一个允许Ruby 调用Java 库的集成层。可用的库的所有 java 方法;像 thisExampleMethod 这样的 java 方法将一对一转换为 ruby​​ this_example_method


更新

针对cmets,第二个上传文件示例中出现的不正确的有:

  • 第一页居民姓名字段(两个文本字段的文本对于给定的输入字段大小来说太小了)
  • 第二页电话字段(四个文本字段的文本超出给定输入字段大小)

【问题讨论】:

  • 很遗憾,您没有明确指出您认为哪些字段是错误的。我假设您的意思是 CARE PROVIDERS Address 字段,其中文字看起来太小了。问题是预先存在的表单外观已经是无用的了。如果已经存在预先存在的正常外观,PDFBox 会尝试重新使用它,以防您的文档导致这种损坏的外观。您应该在设置值之前删除以前的外观。如果您也认为其他字段不正确,请指出您所指的字段。
  • @mkl 感谢您的回复,我已经用不正确的字段更新了问题。 PDTextField 上的 setDefaultAppearance 是否不会覆盖该字段的现有外观?您有推荐的方法来消除以前的外观吗?
  • 关于更新:ResidentName 字段 - 这里的问题又是由于最初存在的外观大小与场地。 Phone 字段 - 您的意思是只显示“T e”的字段?这些字段被配置为 MaxLen 值为2 的梳状字段。这恰当地解释了外观。
  • @mkl 有没有办法完全重置 PDField 的现有外观/行为而不涉及完全创建新字段?
  • 有一种方法可以删除现有的外观流。但是这些旧的外观流只会导致您的部分问题,如上所述,某些字段是 2 个字符梳字段,而您希望它们完全不同。此外,您有许多带有多个小部件的字段。这些字段的所有小部件自动具有相同的值,这对您的表单没有多大意义(例如,这 2 个字符梳小部件中的 all 表示 相同的字段,但肯定是所有这些办公室、手机、寻呼机和传真号码不得重合)。

标签: java jruby pdfbox


【解决方案1】:

特别是“居民姓名”字段、“电话”字段和“护理提供者地址”字段的外观非常显眼。 OP只提到了前两者。

让我们检查这些字段;所有屏幕截图均使用 MS Windows 上的 Adob​​e Reader DC 制作:

居民姓名字段

填写的居民姓名字段如下所示

虽然高度合适,但字形比应有的窄。其实这个效果在原始PDF中已经可以看到了:

这种水平压缩是由与分别匹配的正常外观流边界框具有不同纵横比的字段小部件矩形引起的:

  • 小部件矩形:[ 45.72 601.44 118.924 615.24 ][ 119.282 601.127 192.486 614.927 ],在这两种情况下都是 73.204*13.8。
  • 外观边界框:[ 0 0 147.24 13.8 ],即147.24*13.8。

因此它们具有相同的高度,但外观边界框的宽度大约是小部件矩形的两倍。因此,当外观显示在小部件矩形中时,在外观流中正常绘制的文本会被压缩到其宽度的一半。

不幸的是,当设置字段的值时,PDFBox 会按原样重新使用外观流,并且只更新默认外观的细节,即字体名称、字体大小和颜色以及实际文本值,显然是假设其他属性的外观是因为他们是有原因的。因此,PDFBox 输出也显示了这种水平压缩

为了使PDFBox创建一个合适的外观,需要在设置新值之前移除旧的外观。

电话字段

填写的电话字段如下所示

在原始文件中也有类似的显示

即使整个单词有足够的空间,也只显示前两个字母,这是由于这些字段的配置:它们被配置为最大长度为 2 个字符的梳状字段。

要在此处设置一个值并完全显示 PDFBox 并且不那么间隔,您必须删除最大长度(或至少必须使其不小于您的值的长度)并取消设置梳标志。

护理提供者地址字段

填写它们看起来像这样:

最初它们看起来很相似:

这种垂直压缩再次是由具有与分别匹配的正常外观流边界框不同的纵横比的字段小部件矩形引起的:

  • 小部件矩形:[ 278.6 642.928 458.36 657.96 ],即 179.76*15.032。
  • 外观边界框:[ 0 0 179.76 58.56 ],即179.76*58.56。

就像上面的居民姓名字段一样,必须在设置新值之前删除旧的外观,以使 PDFBox 创建正确的外观。

并发症

实际上,在填写护理提供者地址字段时还有一个问题,删除旧外观后,它们看起来像这样:

这是由于 PDFBox 的一个缺点:这些字段被配置为多行文本字段。虽然用于单行文本字段的 PDFBox 会根据内容正确计算字体大小,然后确保文本在垂直方向上非常合适,但对于多行字段,它的处理非常粗糙,它选择硬编码的字体大小为 12 并且不微调垂直位置,见AppearanceGeneratorHelper方法calculateFontSize(PDFont, PDRectangle)insertGeneratedAppearance(PDAnnotationWidget, PDAppearanceStream, OutputStream)的代码。

在您的表单中,这些地址字段无论如何都只有一行高,一个明显的解决方案是将这些字段设置为单行字段,即清除 Multiline 标志。

示例代码

使用 Java 可以实现上述解决方案,如下所示:

final int FLAG_MULTILINE = 1 << 12;
final int FLAG_COMB = 1 << 24;

PDDocument doc = PDDocument.load(originalStream);
PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();

PDType0Font font = PDType0Font.load(doc, fontStream, false);
String font_name = acroForm.getDefaultResources().add(font).getName();

for (PDField field : acroForm.getFieldTree()) {
    if (field instanceof PDTextField) {
        PDTextField textField = (PDTextField) field;
        textField.getCOSObject().removeItem(COSName.MAX_LEN);
        textField.getCOSObject().setFlag(COSName.FF, FLAG_COMB | FLAG_MULTILINE, false);;
        textField.setDefaultAppearance(String.format("/%s 0 Tf 0 g", font_name));
        textField.getWidgets().forEach(w -> w.getAppearance().setNormalAppearance((PDAppearanceEntry)null));
        textField.setValue("Test");
    }
}

(FillInForm 测试testFill0DropOldAppearanceNoCombNoMaxNoMultiLine)

示例代码输出截图

居民姓名字段值现在不再垂直压缩:

电话和护理提供者地址字段现在看起来也很合适:

【讨论】:

  • 标志梳COSName.FF, FLAG_COMB 设置在此解决方案中特别有用。我在文档或示例中找不到使用此功能的任何地方 - 老实说,您的示例代码可能是他们放在 PDFBox 网站上的示例案例。
  • @mkl "PDFBox 不幸地重用了外观流" - 我相信您或 Maruan 在某处提到 PDF 规范要求重用它。
  • @Xenyal 虽然很有趣,但您的问题似乎不切实际:通常人们会得到一个 PDF 文件,其中的字段为空以将数据写入其中,或者字段填充以从中读取数据。您的问题已填满,必须更改。
  • "您是否知道在小部件上的setNormalAppearance 之后,在尝试setValue 时会抛出NullPointerException 的任何情况?" - 没有具体的;可能会发生在默认外观值损坏的情况下,例如引用不存在的字体等。但我不是该主题的专家...
  • @Xenyal 始终使用最新版本。如果您使用 maven,则使用 maven 版本插件。这样做的好处是可以减少出现错误的风险,包括安全错误。不更新的唯一原因是您是否为 equifax 工作。
猜你喜欢
  • 1970-01-01
  • 2023-03-20
  • 2010-11-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多