Java PDFBox 为 PDF 表单中的几个字段设置自定义字体

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30181250/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 16:36:46  来源:igfitidea点击:

Java PDFBox setting custom font for a few fields in PDF Form

javapdffontspdfboxpdf-form

提问by user972391

I am using Apache PDFBox to read a fillable PDF form and fill the fields based on some data. I am using the below code (as per suggestions from other SO answers) to get the default Appearance String and changing it (as you can see below, I am changing the font size from 10 to 12 if the field name is "Field1".

我正在使用 Apache PDFBox 读取可填写的 PDF 表单并根据某些数据填写字段。我正在使用下面的代码(根据其他 SO 答案的建议)来获取默认的外观字符串并对其进行更改(如下所示,如果字段名称为“Field1”,我会将字体大小从 10 更改为 12。

  1. How do I bold the field? Any documentation on what order the /Helv 10 Tf 0 g are arranged? What I need to set to bold the field?
  2. If I understand right, there are 14 basic fonts that I can use in PDFBox out of the box (pun unintended). I would like to use one or more fonts that look like Signatures (cursive). Any out of the box fonts that do that? If not, if I have my own font, how do I set in the method to be written to the PDF?
  1. 我如何加粗该字段?关于 /Helv 10 Tf 0 g 排列顺序的任何文档?我需要设置什么来加粗该字段?
  2. 如果我理解正确,我可以在 PDFBox 中使用 14 种开箱即用的基本字体(非故意的双关语)。我想使用一种或多种看起来像签名(草书)的字体。任何开箱即用的字体可以做到这一点?如果没有,如果我有自己的字体,如何在写入PDF的方法中进行设置?

Please note, the below code works fine by filling the specific 'value' passed in the method parameter in the specific 'name' field of the method parameter.

请注意,通过在方法参数的特定“名称”字段中填充方法参数中传递的特定“值”,以下代码可以正常工作。

Thank you !

谢谢 !

public static void setField(String name, String value ) throws     IOException {
    PDDocumentCatalog docCatalog = _pdfDocument.getDocumentCatalog();
    PDAcroForm acroForm = docCatalog.getAcroForm();
    PDField field = acroForm.getField( name );

    COSDictionary dict = ((PDField)field).getDictionary();
    COSString defaultAppearance = (COSString) dict.getDictionaryObject(COSName.DA);
    if (defaultAppearance != null)
    {
        dict.setString(COSName.DA, "/Helv 10 Tf 0 g");
        if(name.equalsIgnoreCase("Field1"))
        {
            dict.setString(COSName.DA, "/Helv 12 Tf 0 g");
        }
    }
    if(field instanceof PDTextbox)
    {
        field= new PDTextbox(acroForm, dict);
        ((PDField)field).setValue(value);
    }

As per mkl's answer, to use two fonts in the same PDF, I used the following method: I could not get the default font and a custom font working together, so I added two fonts to the resources and used them.

根据 mkl 的回答,要在同一个 PDF 中使用两种字体,我使用了以下方法:我无法将默认字体和自定义字体一起使用,因此我向资源中添加了两种字体并使用了它们。

public List<String> prepareFont(PDDocument _pdfDocument) throws IOException
{
    PDDocumentCatalog docCatalog = _pdfDocument.getDocumentCatalog();
    PDAcroForm acroForm = docCatalog.getAcroForm();

    PDResources res = acroForm.getDefaultResources();
    if (res == null)
        res = new PDResources();

    InputStream fontStream = getClass().getResourceAsStream("LiberationSans-Regular.ttf");
InputStream fontStream2 = getClass().getResourceAsStream("Font2.ttf");
    PDTrueTypeFont font = PDTrueTypeFont.loadTTF(_pdfDocument, fontStream);
PDTrueTypeFont font2 = PDTrueTypeFont.loadTTF(_pdfDocument, fontStream2);
    String fontName = res.addFont(font); 
String fontName2 = res.addFont(font2);
    acroForm.setDefaultResources(res);
    List<String> fontList = new ArrayList<String>();    fontList.add(font1);fontList.add(font2);
    return fontList;
}

回答by mkl

(You can find a runnable example here: FillFormCustomFont.java)

(你可以在这里找到一个可运行的例子:FillFormCustomFont.java

Using poor-man's-bold

使用穷人的大胆

  1. How do I bold the field? ... What I need to set to bold the field?
  1. 我如何加粗该字段?...我需要设置什么来加粗该字段?

In PDF you usually make text bold by using a font with bold glyphs, also see your second question. If you don't have such a bold font at hands, you may instead use some poor-man's-bold technique, e.g. not only filling the letter but also stroking a line along its borders:

在 PDF 中,您通常使用带有粗体字形的字体将文本设为粗体,另请参阅您的第二个问题。如果你手头没有这么粗的字体,你可以改用一些穷人的粗体技术,例如不仅填充字母而且沿着它的边框画一条线:

public static void setFieldBold(String name, String value) throws IOException
{
    PDDocumentCatalog docCatalog = _pdfDocument.getDocumentCatalog();
    PDAcroForm acroForm = docCatalog.getAcroForm();
    PDField field = acroForm.getField(name);

    COSDictionary dict = ((PDField) field).getDictionary();
    COSString defaultAppearance = (COSString) dict
            .getDictionaryObject(COSName.DA);
    if (defaultAppearance != null)
    {
        dict.setString(COSName.DA, "/Helv 10 Tf 2 Tr .5 w 0 g");
        if (name.equalsIgnoreCase("Field1")) {
            dict.setString(COSName.DA, "/Helv 12 Tf 0 g");
        }
    }
    if (field instanceof PDTextbox)
    {
        field = new PDTextbox(acroForm, dict);
        ((PDField) field).setValue(value);
    }
}

(2 Tr .5 w= use rendering mode 2, i.e. fill and stroke, and use a line width of .5)

2 Tr .5 w= 使用渲染模式 2,即填充和描边,并使用 0.5 的线宽)

Instead of

代替

Using the OP's method

使用 OP 的方法

you now get

你现在得到

Using the setFieldBold method

使用 setFieldBold 方法

Using custom fonts

使用自定义字体

  1. If I understand right, there are 14 basic fonts that I can use in PDFBox out of the box (pun unintended). I would like to use one or more fonts that look like Signatures (cursive). Any out of the box fonts that do that? If not, if I have my own font, how do I set in the method to be written to the PDF?
  1. 如果我理解正确,我可以在 PDFBox 中使用 14 种开箱即用的基本字体(非故意的双关语)。我想使用一种或多种看起来像签名(草书)的字体。任何开箱即用的字体可以做到这一点?如果没有,如果我有自己的字体,如何在写入PDF的方法中进行设置?

If you want to use an own font, you first need to register it in the AcroFormdefault resources like this:

如果要使用自己的字体,首先需要在AcroForm默认资源中注册它,如下所示:

public String prepareFont(PDDocument _pdfDocument) throws IOException
{
    PDDocumentCatalog docCatalog = _pdfDocument.getDocumentCatalog();
    PDAcroForm acroForm = docCatalog.getAcroForm();

    PDResources res = acroForm.getDefaultResources();
    if (res == null)
        res = new PDResources();

    InputStream fontStream = getClass().getResourceAsStream("LiberationSans-Regular.ttf");
    PDTrueTypeFont font = PDTrueTypeFont.loadTTF(_pdfDocument, fontStream);
    String fontName = res.addFont(font);
    acroForm.setDefaultResources(res);

    return fontName;
}

This method returns the font name to use in

此方法返回要使用的字体名称

public static void setField(String name, String value, String fontName) throws IOException
{
    PDDocumentCatalog docCatalog = _pdfDocument.getDocumentCatalog();
    PDAcroForm acroForm = docCatalog.getAcroForm();
    PDField field = acroForm.getField(name);

    COSDictionary dict = ((PDField) field).getDictionary();
    COSString defaultAppearance = (COSString) dict
            .getDictionaryObject(COSName.DA);
    if (defaultAppearance != null)
    {
        dict.setString(COSName.DA, "/" + fontName + " 10 Tf 0 g");
        if (name.equalsIgnoreCase("Field1")) {
            dict.setString(COSName.DA, "/" + fontName + " 12 Tf 0 g");
        }
    }
    if (field instanceof PDTextbox)
    {
        field = new PDTextbox(acroForm, dict);
        ((PDField) field).setValue(value);
    }
}

You now get

你现在得到

Using the setFieldBold method with font parameter

使用带有字体参数的 setFieldBold 方法

The difference is not too big because the fonts are quite similar. Use the font of your choice for more effect.

区别不是太大,因为字体非常相似。使用您选择的字体以获得更多效果。

Using /Helv, /HeBo, ...

使用/Helv, /HeBo, ...

The OP found a list of font names /Helv, /HeBo, ..., probably in the PDFBox issue PDFBOX-1234, which appear to be usable without defining them in any resource dictionary.

OP 找到了一个字体名称列表/Helv/HeBo,...,可能在 PDFBox 问题PDFBOX-1234 中,它们似乎可以使用而无需在任何资源字典中定义它们。

These names are not a PDF feature, i.e. the PDF specification does not know about them, on the contrary:

这些名称不是 PDF 功能,即 PDF 规范不知道它们,相反:

The default appearance string (DA) contains any graphics state or text state operators needed to establish the graphics state parameters, such as text size and colour, for displaying the field's variable text. Only operators that are allowed within text objects shall occur in this string (see Figure 9). At a minimum, the string shall include a Tf(text font) operator along with its two operands, font and size. The specified font value shall match a resource name in the Fontentry of the default resource dictionary (referenced from the DRentry of the interactive form dictionary; see Table 218).

(section 12.7.3.3 Field Dictionaries / Variable Text in ISO 32000-1)

默认外观字符串 ( DA) 包含建立图形状态参数(例如文本大小和颜色)所需的任何图形状态或文本状态运算符,以显示字段的可变文本。只有允许在文本对象中使用的运算符才会出现在此字符串中(参见图 9)。字符串至少应包括Tf(文本字体)运算符及其两个操作数,字体和大小。指定的字体值应与默认资源字典的字体条目中的资源名称匹配(从交互式表单字典的DR条目中引用;见表 218)。

(ISO 32000-1 中的第 12.7.3.3 节字段字典/变量文本)

Thus, the specification does not know those default font names.

因此,规范不知道那些默认字体名称。

Nonetheless, Adobe Reader/Acrobat seem to support them, most likely because at some time in the distant past some form generating tool assumed them to be there and support for those forms was kept due to compatibility reasons.

尽管如此,Adobe Reader/Acrobat 似乎支持它们,很可能是因为在遥远的过去某个时间,某些表单生成工具假设它们存在并且由于兼容性原因保留了对这些表单的支持。

Using this feature, therefore, might not be the best choice but your mileage may vary.

因此,使用此功能可能不是最佳选择,但您的里程可能会有所不同。

Using custom and standard fonts

使用自定义和标准字体

In his comments the OP indicated he wanted to use both custom and standard fonts in forms.

在他的评论中,OP 表示他想在表单中使用自定义字体和标准字体。

To do this I generalized the method prepareFonta bit and refactored the TTF import into a separate method:

为此,我对该方法进行prepareFont了一些概括,并将 TTF 导入重构为一个单独的方法:

public List<String> prepareFont(PDDocument _pdfDocument, List<PDFont> fonts) throws IOException
{
    PDDocumentCatalog docCatalog = _pdfDocument.getDocumentCatalog();
    PDAcroForm acroForm = docCatalog.getAcroForm();

    PDResources res = acroForm.getDefaultResources();
    if (res == null)
        res = new PDResources();

    List<String> fontNames = new ArrayList<String>();
    for (PDFont font: fonts)
    {
        fontNames.add(res.addFont(font));
    }

    acroForm.setDefaultResources(res);

    return fontNames;
}

public PDFont loadTrueTypeFont(PDDocument _pdfDocument, String resourceName) throws IOException
{
    try ( InputStream fontStream = getClass().getResourceAsStream(resourceName); )
    {
        return PDTrueTypeFont.loadTTF(_pdfDocument, fontStream);
    }
}

Using these methods you can mix custom and standard fonts like this:

使用这些方法,您可以像这样混合自定义字体和标准字体:

PDDocument doc = PDDocument.load(originalStream);
List<String> fontNames = prepareFont(doc, Arrays.asList(loadTrueTypeFont(doc, "LiberationSans-Regular.ttf"), PDType1Font.HELVETICA_BOLD));

setField(doc, "FirstName", "My first name", fontNames.get(0));
setField(doc, "LastName", "My last name", fontNames.get(1));

doc.save(new File(RESULT_FOLDER, "acroform-setFieldCustomStandard.pdf"));
doc.close();

(FillFormCustomFont.testSetFieldCustomStandard_acroform)

( FillFormCustomFont.testSetFieldCustomStandard_acroform)

Resulting in

导致

Using mixed custom and standard fonts

使用混合的自定义和标准字体

PDType1Fonthas constants for all 14 standard fonts. Thus, like this you can use standard fonts (mixed with custom fonts if desired) in form fields in a way that generates the proper Fontentries in the default resources, i.e. without relying on proprietary default font names like HeBo.

PDType1Font具有所有 14 种标准字体的常量。因此,像这样,您可以在表单字段中使用标准字体(如果需要,可以与自定义字体混合),以在默认资源中生成正确的字体条目的方式,即不依赖专有的默认字体名称,如HeBo

PS

聚苯乙烯

Any documentation on what order the /Helv 10 Tf 0 g are arranged?

关于 /Helv 10 Tf 0 g 排列顺序的任何文档?

Yes, there is, cf. the specification ISO 32000-1.

是的,有,参见。ISO 32000-1规范。