【问题标题】:Java PDFBox setting custom font for a few fields in PDF FormJava PDFBox为PDF表单中的几个字段设置自定义字体
【发布时间】:2015-07-22 18:13:03
【问题描述】:

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

  1. 如何将字段加粗?关于 /Helv 10 Tf 0 g 的排列顺序的任何文档?我需要设置什么来加粗该字段?
  2. 如果我理解正确的话,我可以在 PDFBox 中使用 14 种基本字体(双关语无意)。我想使用一种或多种看起来像 Signatures(草书)的字体。任何开箱即用的字体可以做到这一点?如果没有,如果我有自己的字体,如何设置写入PDF的方法?

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

谢谢!

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

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

【问题讨论】:

    标签: java pdf fonts pdfbox pdf-form


    【解决方案1】:

    (您可以在此处找到可运行的示例:FillFormCustomFont.java

    使用poor-man's-bold

    1. 如何将字段加粗? ... 我需要设置什么来加粗该字段?

    在 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 = 使用渲染模式 2,即填充和描边,并使用 0.5 的线宽)

    代替

    你现在得到

    使用自定义字体

    1. 如果我理解正确的话,我可以在 PDFBox 中使用 14 种基本字体(双关语无意)。我想使用一种或多种看起来像签名(草书)的字体。任何开箱即用的字体可以做到这一点?如果没有,如果我有自己的字体,如何设置写入PDF的方法?

    如果要使用自己的字体,首先需要在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;
    }
    

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

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

    你现在得到

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

    使用 /Helv/HeBo、...

    OP 找到了字体名称列表/Helv/HeBo、...,可能在 PDFBox 问题PDFBOX-1234 中,似乎可以在没有在任何资源字典中定义它们。

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

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

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

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

    尽管如此,Adobe Reader/Acrobat 似乎支持它们,这很可能是因为在很久以前的某个时候,某些表单生成工具假定它们存在,并且出于兼容性原因而保留了对这些表单的支持。

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

    使用自定义和标准字体

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

    为此,我稍微概括了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);
        }
    }
    

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

    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)

    导致

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

    PS

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

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

    【讨论】:

    • 感谢您的回答。 “粗体”部分工作正常,“字体”部分正在抛出 java.lang.ArrayIndexOutOfBoundsException: -1 at java.util.ArrayList.elementData(ArrayList.java:371) at java.util.ArrayList.get(ArrayList. java:384) 在 org.apache.pdfbox.pdmodel.interactive.form.PDAppearance.getFontAndUpdateResources(PDAppearance.java:815) 在 org.apache.pdfbox.pdmodel.interactive.form.PDAppearance.setAppearanceValue(PDAppearance.java:316)在 org.apache.pdfbox.pdmodel.interactive.form.PDVariableText.setValue(PDVariableText.java:131)
    • 当我使用eclipse调试它时,在prepareFont方法的PDTrueTypeFont行,字体值为“FO”。这正常吗?我故意给了一个错误的名称,它会抛出一个空指针(以验证字体是否在正确的路径并且可用于此方法)
    • 另外,我发现了这个:Cour -> Courier CoBo -> Courier-Bold CoOb -> Courier-Oblique CoBO -> Courier-BoldOblique Helv -> Helvetica HeBo -> Helvetica-Bold HeOb -> Helvetica -Oblique HeBO -> Helvetica-BoldOblique Symb -> Symbol TiRo -> Times-Roman TiBo -> Times-Bold TiIt -> Times-Italic TiBI -> Times-BoldItalic ZaDb -> ZapfDingbats。我使用了 HeBo,它可以很好地加粗字体(而不是 /Helv 我使用 /HeBo
    • “字体”部分正在抛出... - 请分享您的示例 PDF 以及您使用的代码和输入。如上所述,您可以在 github 上找到运行中的示例单元测试,它确实有效。
    • 我使用 /HeBo 而不是 /Helv - 如上所述,在 PDF 中,您通常使用带有粗体字形的字体来使文本变为粗体,HeBo = Helvetica-bold 是 Helvetica 的吊坠。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-27
    • 1970-01-01
    • 2011-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多