<commit_before>/*
 * 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.interactive.form;

import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fontbox.util.BoundingBox;
import org.apache.pdfbox.contentstream.operator.Operator;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSString;
import org.apache.pdfbox.pdfparser.PDFStreamParser;
import org.apache.pdfbox.pdfwriter.ContentStreamWriter;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDSimpleFont;
import org.apache.pdfbox.pdmodel.font.PDType3CharProc;
import org.apache.pdfbox.pdmodel.font.PDType3Font;
import org.apache.pdfbox.pdmodel.font.PDVectorFont;
import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
import org.apache.pdfbox.pdmodel.interactive.action.PDFormFieldAdditionalActions;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceCharacteristicsDictionary;
import org.apache.pdfbox.pdmodel.PDAppearanceContentStream;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceEntry;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDBorderStyleDictionary;
import org.apache.pdfbox.util.Matrix;

/**
 * Create the AcroForms field appearance helper.
 * 
 * @author <NAME>
 * @author <NAME>
 */
class AppearanceGeneratorHelper
{
    private static final Log LOG = LogFactory.getLog(AppearanceGeneratorHelper.class);

    private static final Operator BMC = Operator.getOperator("BMC");
    private static final Operator EMC = Operator.getOperator("EMC");
 
    private final PDVariableText field;
    
    private PDDefaultAppearanceString defaultAppearance;
    private String value;
    
    /**
     * The highlight color
     *
     * The color setting is used by Adobe to display the highlight box for selected entries in a list box.
     *
     * Regardless of other settings in an existing appearance stream Adobe will always use this value.
     */
    private static final float[] HIGHLIGHT_COLOR = {153/255f, 193/255f, 215/255f};
 
    /**
     * The scaling factor for font units to PDF units
     */
    private static final int FONTSCALE = 1000;
    
    /**
     * The default font size used for multiline text
     */
    private static final float DEFAULT_FONT_SIZE = 12;

    /**
     * The minimum/maximum font sizes used for multiline text auto sizing
     */
    private static final float MINIMUM_FONT_SIZE = 4;
    private static final float MAXIMUM_FONT_SIZE = 300;
    
    /**
     * The default padding applied by Acrobat to the fields bbox.
     */
    private static final float DEFAULT_PADDING = 0.5f;
    
    /**
     * Constructs a COSAppearance from the given field.
     *
     * @param field the field which you wish to control the appearance of
     * @throws IOException 
     */
    AppearanceGeneratorHelper(PDVariableText field) throws IOException
    {
        this.field = field;
        validateAndEnsureAcroFormResources();
        
        try
        {
            this.defaultAppearance = field.getDefaultAppearanceString();
        }
        catch (IOException ex)
        {
            throw new IOException("Could not process default appearance string '" +
                                   field.getDefaultAppearance() + "' for field '" +
                                   field.getFullyQualifiedName() + "': " + ex.getMessage(), ex);
        }
    }
    
    /*
     * Adobe Reader/Acrobat are adding resources which are at the field/widget level
     * to the AcroForm level. 
     */
    private void validateAndEnsureAcroFormResources()
    {
        // add font resources which might be available at the field 
        // level but are not at the AcroForm level to the AcroForm
        // to match Adobe Reader/Acrobat behavior        
        if (field.getAcroForm().getDefaultResources() == null)
        {
            return;
        }
        
        PDResources acroFormResources = field.getAcroForm().getDefaultResources();
        
        for (PDAnnotationWidget widget : field.getWidgets())
        {
            PDAppearanceStream stream = widget.getNormalAppearanceStream();
            if (stream == null)
            {
                continue;
            }
            PDResources widgetResources = stream.getResources();
            if (widgetResources == null)
            {
                continue;
            }
            for (COSName fontResourceName : widgetResources.getFontNames())
            {
                try
                {
                    if (acroFormResources.getFont(fontResourceName) == null)
                    {
                        LOG.debug("Adding font resource " + fontResourceName + " from widget to AcroForm");
                        acroFormResources.put(fontResourceName, widgetResources.getFont(fontResourceName));
                    }
                }
                catch (IOException e)
                {
                    LOG.warn("Unable to match field level font with AcroForm font", e);
                }
            }
        }
    }
    
    /**
     * This is the public method for setting the appearance stream.
     *
     * @param apValue the String value which the appearance should represent
     * @throws IOException If there is an error creating the stream.
     */
    public void setAppearanceValue(String apValue) throws IOException
    {
        value = getFormattedValue(apValue);

        // Treat multiline field values in single lines as single lime values.
        // This is in line with how Adobe Reader behaves when entering text
        // interactively but NOT how it behaves when the field value has been
        // set programmatically and Reader is forced to generate the appearance<commit_msg>PDFBOX-5254: preserve a possible indirect object reference<commit_after>/*
 * 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.interactive.form;

import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fontbox.util.BoundingBox;
import org.apache.pdfbox.contentstream.operator.Operator;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSString;
import org.apache.pdfbox.pdfparser.PDFStreamParser;
import org.apache.pdfbox.pdfwriter.ContentStreamWriter;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDSimpleFont;
import org.apache.pdfbox.pdmodel.font.PDType3CharProc;
import org.apache.pdfbox.pdmodel.font.PDType3Font;
import org.apache.pdfbox.pdmodel.font.PDVectorFont;
import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
import org.apache.pdfbox.pdmodel.interactive.action.PDFormFieldAdditionalActions;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceCharacteristicsDictionary;
import org.apache.pdfbox.pdmodel.PDAppearanceContentStream;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceEntry;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDBorderStyleDictionary;
import org.apache.pdfbox.util.Matrix;

/**
 * Create the AcroForms field appearance helper.
 * 
 * @author <NAME>
 * @author <NAME>
 */
class AppearanceGeneratorHelper
{
    private static final Log LOG = LogFactory.getLog(AppearanceGeneratorHelper.class);

    private static final Operator BMC = Operator.getOperator("BMC");
    private static final Operator EMC = Operator.getOperator("EMC");
 
    private final PDVariableText field;
    
    private PDDefaultAppearanceString defaultAppearance;
    private String value;
    
    /**
     * The highlight color
     *
     * The color setting is used by Adobe to display the highlight box for selected entries in a list box.
     *
     * Regardless of other settings in an existing appearance stream Adobe will always use this value.
     */
    private static final float[] HIGHLIGHT_COLOR = {153/255f, 193/255f, 215/255f};
 
    /**
     * The scaling factor for font units to PDF units
     */
    private static final int FONTSCALE = 1000;
    
    /**
     * The default font size used for multiline text
     */
    private static final float DEFAULT_FONT_SIZE = 12;

    /**
     * The minimum/maximum font sizes used for multiline text auto sizing
     */
    private static final float MINIMUM_FONT_SIZE = 4;
    private static final float MAXIMUM_FONT_SIZE = 300;
    
    /**
     * The default padding applied by Acrobat to the fields bbox.
     */
    private static final float DEFAULT_PADDING = 0.5f;
    
    /**
     * Constructs a COSAppearance from the given field.
     *
     * @param field the field which you wish to control the appearance of
     * @throws IOException 
     */
    AppearanceGeneratorHelper(PDVariableText field) throws IOException
    {
        this.field = field;
        validateAndEnsureAcroFormResources();
        
        try
        {
            this.defaultAppearance = field.getDefaultAppearanceString();
        }
        catch (IOException ex)
        {
            throw new IOException("Could not process default appearance string '" +
                                   field.getDefaultAppearance() + "' for field '" +
                                   field.getFullyQualifiedName() + "': " + ex.getMessage(), ex);
        }
    }
    
    /*
     * Adobe Reader/Acrobat are adding resources which are at the field/widget level
     * to the AcroForm level. 
     */
    private void validateAndEnsureAcroFormResources()
    {
        // add font resources which might be available at the field 
        // level but are not at the AcroForm level to the AcroForm
        // to match Adobe Reader/Acrobat behavior        
        if (field.getAcroForm().getDefaultResources() == null)
        {
            return;
        }
        
        PDResources acroFormResources = field.getAcroForm().getDefaultResources();
        
        for (PDAnnotationWidget widget : field.getWidgets())
        {
            PDAppearanceStream stream = widget.getNormalAppearanceStream();
            if (stream == null)
            {
                continue;
            }
            PDResources widgetResources = stream.getResources();
            if (widgetResources == null)
            {
                continue;
            }
            COSDictionary widgetFontDict = widgetResources.getCOSObject()
                    .getCOSDictionary(COSName.FONT);
            COSDictionary acroFormFontDict = acroFormResources.getCOSObject()
                    .getCOSDictionary(COSName.FONT);
            for (COSName fontResourceName : widgetResources.getFontNames())
            {
                try
                {
                    if (acroFormResources.getFont(fontResourceName) == null)
                    {
                        LOG.debug("Adding font resource " + fontResourceName + " from widget to AcroForm");
                        // use the COS-object to preserve a possible indirect object reference
                        acroFormFontDict.setItem(fontResourceName,
                                widgetFontDict.getItem(fontResourceName));
                    }
                }
                catch (IOException e)
                {
                    LOG.warn("Unable to match field level font with AcroForm font", e);
                }
            }
        }
    }
    
    /**
     * This is the public method for setting the appearance stream.
     *
     * @param apValue the String value which the appearance should represent
     * @throws IOException If there is an error creating the stream.
     */
    public void setAppearanceValue(String apValue) throws IOException
    {
        value = getFormattedValue(apValue);

        // Treat multiline field values in single lines as single lime values.
        // This is in line with how Adobe Reader behaves when entering text
        // interactively but NOT how it behaves when the field value has been
        // set programmatically and Reader is forced to generate the appearance
{'fraction_non_alphanumeric': 0.07587784205569756, 'fraction_numerical': 0.005246872057042917, 'mean_word_length': 2.6314118221787983, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '835506', 'n_tokens_mistral': 3926, 'n_tokens_neox': 3815, 'n_words': 1157}