AcroFormDefaultsProcessor.java

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.pdfbox.pdmodel.fixup.processor;

import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;


/**
 * Verify and ensure default resources for /AcroForm.
 * 
 * <ul>
 *   <li>a default appearance string is defined</li>
 *   <li>default resources are defined</li>
 *   <li>Helvetica as <code>/Helv</code> and Zapf Dingbats as <code>ZaDb</code> are included.
 *       ZaDb is required for most check boxes and radio buttons</li>
 * </ul>
 * 
 */
public class AcroFormDefaultsProcessor extends AbstractProcessor
{
    public AcroFormDefaultsProcessor(PDDocument document)
    { 
        super(document); 
    }

    @Override
    public void process() {
        /*
         * Get the AcroForm in it's current state.
         *
         * Also note: getAcroForm() applies a default fixup which this processor
         * is part of. So keep the null parameter otherwise this will end
         * in an endless recursive call
         */
        PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm(null);
        if (acroForm != null)
        {
            verifyOrCreateDefaults(acroForm);
        } 
    }

   /*
     * Verify that there are default entries for required 
     * properties.
     * 
     * If these are missing create default entries similar to
     * Adobe Reader / Adobe Acrobat
     *  
     */
    private void verifyOrCreateDefaults(PDAcroForm acroForm)
    {
        final String adobeDefaultAppearanceString = "/Helv 0 Tf 0 g ";

        // DA entry is required
        if (acroForm.getDefaultAppearance().isEmpty())
        {
            acroForm.setDefaultAppearance(adobeDefaultAppearanceString);
            acroForm.getCOSObject().setNeedToBeUpdated(true);
        }

        // DR entry is required
        PDResources defaultResources = acroForm.getDefaultResources();
        if (defaultResources == null)
        {
            defaultResources = new PDResources();
            acroForm.setDefaultResources(defaultResources);
            acroForm.getCOSObject().setNeedToBeUpdated(true);
        }

        // PDFBOX-3732: Adobe Acrobat uses Helvetica as a default font and 
        // stores that under the name '/Helv' in the resources dictionary
        // Zapf Dingbats is included per default for check boxes and 
        // radio buttons as /ZaDb.
        // PDFBOX-4393: the two fonts are added by Adobe when signing
        // and this breaks a previous signature. (Might be an Adobe bug)
        COSDictionary fontDict = defaultResources.getCOSObject().getCOSDictionary(COSName.FONT);
        if (fontDict == null)
        {
            fontDict = new COSDictionary();
            defaultResources.getCOSObject().setItem(COSName.FONT, fontDict);
        }
        if (!fontDict.containsKey(COSName.HELV))
        {
            defaultResources.put(COSName.HELV, new PDType1Font(FontName.HELVETICA));
            defaultResources.getCOSObject().setNeedToBeUpdated(true);
            fontDict.setNeedToBeUpdated(true);
        }
        if (!fontDict.containsKey(COSName.ZA_DB))
        {
            defaultResources.put(COSName.ZA_DB, new PDType1Font(FontName.ZAPF_DINGBATS));
            defaultResources.getCOSObject().setNeedToBeUpdated(true);
            fontDict.setNeedToBeUpdated(true);
        }
    }
}