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

1

11

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".

  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?

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.

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;
}
Apocryphal answered 12/5, 2015 at 3:27 Comment(0)
L
25

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

Using poor-man's-bold

  1. How do I bold the field? ... What I need to set to bold the field?

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:

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)

Instead of

Using the OP's method

you now get

Using the setFieldBold method

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?

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

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

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

Using /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.

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

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 Font entry of the default resource dictionary (referenced from the DR entry of the interactive form dictionary; see Table 218).

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

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.

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.

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

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)

Resulting in

Using mixed custom and standard fonts

PDType1Font has 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 Font entries in the default resources, i.e. without relying on proprietary default font names like HeBo.

PS

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

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

Lipetsk answered 14/5, 2015 at 23:6 Comment(15)
Thank you for the answer. The "bold" part is working fine, The "font" part is throwing java.lang.ArrayIndexOutOfBoundsException: -1 at java.util.ArrayList.elementData(ArrayList.java:371) at java.util.ArrayList.get(ArrayList.java:384) at org.apache.pdfbox.pdmodel.interactive.form.PDAppearance.getFontAndUpdateResources(PDAppearance.java:815) at org.apache.pdfbox.pdmodel.interactive.form.PDAppearance.setAppearanceValue(PDAppearance.java:316) at org.apache.pdfbox.pdmodel.interactive.form.PDVariableText.setValue(PDVariableText.java:131)Apocryphal
When I debug it using eclipse, at the PDTrueTypeFont line in the prepareFont method, the font value is "FO". Is that normal? I intentionally gave a wrong name and it throws a null pointer (to verify that the font is at the right path and is available to this method)Apocryphal
Also, I found this: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. I used HeBo and it worked fine to bold the font (instead of /Helv I used /HeBoApocryphal
The "font" part is throwing... - please share your sample PDF and the very code and inputs you used. As mentioned above, you can find the running example unit test on github which does work.Lipetsk
instead of /Helv I used /HeBo - as mentioned above, in PDF you usually make text bold by using a font with bold glyphs, HeBo = Helvetica-bold is that pendant for Helvetica.Lipetsk
Does the code that you provided work for a PDF to set the 'default' or /HeBo 10 Tf 0 g for some fields and the 'font' for a few fields? If I need two different fonts (default font and custom font) in the same acroForm, does your code work? thank you. I used the code that you provided and was able to load the custom font successfully, but was unable to load two fonts in the same acroForm. Any input will be highly appreciated. Thanks again !Apocryphal
Please be aware that the names Helv, HeBo, etc. are built-in defaults in Adobe Reader used if no fonts are defined. If you also have a custom font for forms, those defaults might not work. Thus, you will have to explicitly define such fonts. This is no magic but does require a line of code or two.Lipetsk
Does the code that you provided work for a PDF to set the 'default' or /HeBo 10 Tf 0 g for some fields and the 'font' for a few fields? - As I added to my answer, /HeBo etc are not in general available by default. In which situation they are, I cannot tell. Nonetheless I corrected an issue in my code above, it used to drop any existing fonts when adding a new forn to the default form resources. Now it keeps them. The clean way to go is to check which fonts already are available in the default form resources and add all required missing ones.Lipetsk
yes /HeBo works out of the box. For me to make it work, I had to add two fonts to the resources and use both of them. I could not get the default and custom font working together.Apocryphal
I edited my question and added the code that I used to add two fonts to the resources.Apocryphal
@Apocryphal I updated the answer with a section "Using custom and standard fonts". Here I show how you can properly register the standard 14 fonts side-by-side with any custom fonts and use them in forms. This way you use a different name than HeBo for Helvetica Bold but you do it in a way not relying on some PDF viewer implementation.Lipetsk
Regarding the Custom font solution (adding the TTF to the PDResources), the font name seems to be determined by the API (e.g., in my test, it was called F3 rather than something more intuitive like LiberationSans-Regular. Do you know of a way to control the name? It's because I'm setting the font with Adobe PDF Javascript later (and so a hard-coded name is important).Kalmar
@Kalmar Instead of PDResources.add(PDFont) you can use PDResources.put(COSName, PDFont). This allows you to control the name for the font. As a drawback, though, this may result in the same font being registered with different name or (even worse) overwriting an existing, different registered font with your new one.Lipetsk
do u know if its possible to have one multiline field contain some bold, some regular, some italis etc? i may have to combine several fonts into oone field, but what would the default appearance sring look like?Bronchial
If you want different styles in a single text field, you should consider setting the RichText flag of the field and use the RV entry of the field to set the rich text value (including certain XHTML and XFA styling tags). For details see ISO 32000-1 section 12.7.3.4 Rich Text Strings. I doubt, though, that PDFBox has appearance creation code for rich text. Thus, you may have to set NeedAppearances to true.Lipetsk

© 2022 - 2024 — McMap. All rights reserved.