简体   繁体   English

设置值后的 PDFBox 不一致的 PDTextField 自动调整大小行为

[英]PDFBox Inconsistent PDTextField Autosize Behavior after setValue

I am using Apache PDFBox for configuration of PDTextField 's on a PDF document where I load Lato onto the document using:我正在使用 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

I then pass the font_name into a default_appearance_string for the PDTextField like so:然后我将 font_name 传递到PDTextFielddefault_appearance_string中, PDTextField所示:

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

The issue now occurs when I proceed to invoke setValue on the PDTextField .现在,当我继续在PDTextField上调用setValue时会出现问题。 Because I set the font_size in the defaultAppearanceString to 0 , according to the library's example , the text should scale itself to fit in the text box's given area.因为我将defaultAppearanceStringfont_size设置为0 ,根据库的示例,文本应该自行缩放以适应文本框的给定区域。 However, the behaviour of this 'scale-to-fit' is inconsistent for certain fields: it does not always choose the largest font size to fit in the PDTextField .但是,这种“按比例缩放”的行为对于某些字段是不一致的:它并不总是选择适合PDTextField的最大字体大小。 Might there be any further configuration that might allow for this to happen?是否有任何进一步的配置可能允许这种情况发生? Below are the PDFs where I've noticed this problem occurring.以下是我注意到发生此问题的 PDF。

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

Filled, with inconsisteny textbox text sizing: http://www.filedropper.com/file_327填充,文本框文本大小不一致: http : //www.filedropper.com/file_327

Side Note : I am using PDFBox through jruby which is just a integration layer that allows Ruby to invoke Java libraries.边注:我使用PDFBox的通过jruby这仅仅是一个集成层,使红宝石调用Java库。 All java methods for the library available;库的所有java方法可用; a java method like thisExampleMethod would have a one-to-one translation into ruby this_example_method .thisExampleMethod这样的 java 方法将一对一转换为 ruby this_example_method


Updates更新

In response to comments, the appearances that are incorrect in the second uploaded file example are:针对评论,第二个上传文件示例中出现错误的表象是:

  • 1st page Resident Name field (two text fields that have text that is too small for the given input field size)第一页居民姓名字段(两个文本字段的文本对于给定的输入字段大小来说太小)
  • 2nd page Phone fields (four text fields that have text that overflows the given input field size)第二页电话字段(四个文本字段的文本超出给定的输入字段大小)

Especially the appearances of the Resident Name fields, the Phone fields, and the Care Providers Address fields appear conspicuous.尤其是“居民姓名”字段、“电话”字段和“护理提供者地址”字段的出现显得格外醒目。 Only the former two are mentioned by the OP. OP只提到了前两个。

Let's inspect these fields;让我们检查这些字段; all screen shots are made using Adobe Reader DC on MS Windows:所有屏幕截图都是在 MS Windows 上使用 Adob​​e Reader DC 制作的:

The Resident Name fields居民姓名字段

The filled in Resident Name fields look like this填写的居民姓名字段如下所示

截屏

While the height is appropriate, the glyphs are narrower than they should be.虽然高度合适,但字形比应有的要窄。 Actually this effect can already be seen in the original PDF:其实这个效果已经可以在原始PDF中看到了:

截屏

This horizontal compression is caused by the field widget rectangles having a different aspect ratio than the respectively matching normal appearance stream bounding box:这种水平压缩是由与分别匹配的正常外观流边界框具有不同纵横比的字段小部件矩形引起的:

  • The widget rectangles: [ 45.72 601.44 118.924 615.24 ] and [ 119.282 601.127 192.486 614.927 ] , ie 73.204*13.8 in both cases.小部件矩形: [ 45.72 601.44 118.924 615.24 ][ 119.282 601.127 192.486 614.927 ] ,即两种情况下的 73.204*13.8 。
  • The appearance bounding box: [ 0 0 147.24 13.8 ] , ie 147.24*13.8.外观边界框: [ 0 0 147.24 13.8 ] ,即 147.24*13.8。

So they have the same height but the appearance bounding box is approximately twice as wide as the widget rectangle.所以它们具有相同的高度,但外观边界框的宽度大约是小部件矩形的两倍。 Thus, the text drawn normally in the appearance stream gets compressed to half its width when the appearance is displayed in the widget rectangle.因此,当外观显示在小部件矩形中时,在外观流中正常绘制的文本被压缩到其宽度的一半。

When setting the value of a field PDFBox unfortunately re-uses the appearance stream as is and only updates details from the default appearance, ie font name, font size, and color, and the actual text value, apparently assuming the other properties of the appearance are as they are for a reason.当设置字段的值时,PDFBox 不幸地按原样重新使用外观流,仅更新默认外观的详细信息,即字体名称、字体大小和颜色以及实际文本值,显然假设外观的其他属性之所以如此,是有原因的。 Thus, the PDFBox output also shows this horizontal compression因此,PDFBox 输出也显示了这种水平压缩

To make PDFBox create a proper appearance, it is necessary to remove the old appearances before setting the new value.为了使 PDFBox 创建一个合适的外观,需要在设置新值之前删除旧外观。

The Phone fields电话字段

The filled in Phone fields look like this填写的电话字段如下所示

截屏

and again there is a similar display in the original file原始文件中也有类似的显示

截屏

That only the first two letters are shown even though there is enough space for the whole word, is due to the configuration of these fields: They are configured as comb fields with a maximum length of 2 characters.即使有足够的空间容纳整个单词,也只显示前两个字母,这是由于这些字段的配置:它们被配置为最大长度为 2 个字符的组合字段。

To have a value here set with PDFBox displayed completely and not so spaced out, you have to remove the maximum length (or at least have to make it no less than the length of your value) and unset the comb flag.要在此处设置一个值并完全显示 PDFBox 而不是那么间隔,您必须删除最大长度(或至少必须使其不小于您的值的长度)并取消设置梳状标志。

The Care Providers Address fields护理提供者地址字段

Filled in they look like this:填写它们看起来像这样:

截屏

Originally they look similar:最初它们看起来很相似:

截屏

This vertical compression is again caused by the field widget rectangles having a different aspect ratio than the respectively matching normal appearance stream bounding box:这种垂直压缩再次由具有与分别匹配的正常外观流边界框不同的纵横比的字段小部件矩形引起:

  • A widget rectangle: [ 278.6 642.928 458.36 657.96 ] , ie 179.76*15.032.小部件矩形: [ 278.6 642.928 458.36 657.96 ] ,即 179.76*15.032。
  • The appearance bounding box: [ 0 0 179.76 58.56 ] , ie 179.76*58.56.外观边界框: [ 0 0 179.76 58.56 ] ,即 179.76*58.56。

Just like in the case of the Resident Name fields above it is necessary to remove the old appearances before setting the new value to make PDFBox create a proper appearance.就像上面的居民姓名字段的情况一样,在设置新值之前需要删除旧的外观,以使 PDFBox 创建正确的外观。

A complication并发症

Actually there is an additional issue when filling in the Care Providers Address fields, after removing the old appearances they look like this:实际上在填写护理提供者地址字段时还有一个问题,在删除旧外观后,它们看起来像这样:

截屏

This is due to a shortcoming of PDFBox: These fields are configured as multi line text fields.这是由于 PDFBox 的一个缺点:这些字段被配置为多行文本字段。 While PDFBox for single line text fields properly calculates the font size based on the content and later finely makes sure that the text vertically fits quite well, it proceeds very crudely for multi line fields, it selects a hard coded font size of 12 and does not fine tune the vertical position, see the code of the AppearanceGeneratorHelper methods calculateFontSize(PDFont, PDRectangle) and insertGeneratedAppearance(PDAnnotationWidget, PDAppearanceStream, OutputStream) .虽然用于单行文本字段的 PDFBox 会根据内容正确计算字体大小,然后精细地确保文本垂直适合,但对于多行字段,它的处理非常粗糙,它选择了 12 的硬编码字体大小,并且不会微调垂直位置,参见AppearanceGeneratorHelper方法calculateFontSize(PDFont, PDRectangle)insertGeneratedAppearance(PDAnnotationWidget, PDAppearanceStream, OutputStream)

As in your form these address fields anyways are only one line high, an obvious solution would be to make these fields single line fields, ie clear the Multiline flag.由于在您的表单中,这些地址字段无论如何都只有一行高,一个明显的解决方案是使这些字段成为单行字段,即清除多行标志。

Example code示例代码

Using Java one can implement the solutions explained above like this:使用 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 test testFill0DropOldAppearanceNoCombNoMaxNoMultiLine ) FillInForm测试testFill0DropOldAppearanceNoCombNoMaxNoMultiLine

Screen shots of the output of the example code示例代码输出的屏幕截图

The Resident Name field value now is not vertically compressed anymore:居民姓名字段值现在不再垂直压缩:

截屏

The Phone and Care Providers Address fields also look appropriate now:电话和护理提供者地址字段现在看起来也很合适:

截屏

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM