All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.itextpdf.forms.fields.AbstractPdfFormField Maven / Gradle / Ivy

/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2024 Apryse Group NV
    Authors: Apryse Software.

    This program is offered under a commercial and under the AGPL license.
    For commercial licensing, contact us at https://itextpdf.com/sales.  For AGPL licensing, see below.

    AGPL licensing:
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see .
 */
package com.itextpdf.forms.fields;

import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.source.PdfTokenizer;
import com.itextpdf.io.source.RandomAccessFileOrArray;
import com.itextpdf.io.source.RandomAccessSourceFactory;
import com.itextpdf.kernel.colors.Color;
import com.itextpdf.kernel.colors.DeviceCmyk;
import com.itextpdf.kernel.colors.DeviceGray;
import com.itextpdf.kernel.colors.DeviceRgb;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.pdf.IConformanceLevel;
import com.itextpdf.kernel.pdf.PdfAConformanceLevel;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfObject;
import com.itextpdf.kernel.pdf.PdfObjectWrapper;
import com.itextpdf.kernel.pdf.PdfString;

import java.util.ArrayList;
import java.util.List;

/**
 * This class represents a single field or field group in an {@link com.itextpdf.forms.PdfAcroForm
 * AcroForm}.
 *
 * 

* To be able to be wrapped with this {@link PdfObjectWrapper} the {@link PdfObject} * must be indirect. */ public abstract class AbstractPdfFormField extends PdfObjectWrapper { /** * Size of text in form fields when font size is not explicitly set. */ public static final int DEFAULT_FONT_SIZE = 12; /** * Minimal size of text in form fields. */ public static final int MIN_FONT_SIZE = 4; private static final PdfName[] TERMINAL_FIELDS = new PdfName[] {PdfName.Btn, PdfName.Tx, PdfName.Ch, PdfName.Sig}; /** * Index of font value in default appearance element. */ private static final int DA_FONT = 0; /** * Index of font size value in default appearance element. */ private static final int DA_SIZE = 1; /** * Index of color value in default appearance element. */ private static final int DA_COLOR = 2; protected PdfFont font; protected float fontSize = -1; protected Color color; /** * @deprecated since 8.0.4, this is not used anymore! Use pdfConformanceLevel instead */ @Deprecated() protected PdfAConformanceLevel pdfAConformanceLevel; protected IConformanceLevel pdfConformanceLevel; /** * Parent form field. */ protected PdfFormField parent; /** * Indicates if the form field appearance stream regeneration is enabled. */ private boolean enableFieldRegeneration = true; /** * Creates a form field as a wrapper object around a {@link PdfDictionary}. * This {@link PdfDictionary} must be an indirect object. * * @param pdfObject the dictionary to be wrapped, must have an indirect reference. */ protected AbstractPdfFormField(PdfDictionary pdfObject) { super(pdfObject); ensureObjectIsAddedToDocument(pdfObject); setForbidRelease(); retrieveStyles(); } /** * Gets the wrapped dictionary. * * @return the wrapped dictionary. */ @Override public PdfDictionary getPdfObject() { return super.getPdfObject(); } /** * Sets a parent {@link PdfFormField} for the current object. * * @param parent another form field that this field belongs to, usually a group field. */ public void setParent(PdfFormField parent) { if (!parent.getPdfObject().equals(this.getParent()) && !parent.getPdfObject().equals(this.getPdfObject())) { put(PdfName.Parent, parent.getPdfObject()); } this.parent = parent; } /** * Gets the parent dictionary. * * @return another form field that this field belongs to. */ public PdfDictionary getParent() { PdfDictionary parentDict = getPdfObject().getAsDictionary(PdfName.Parent); if (parentDict == null) { parentDict = parent == null ? null : parent.getPdfObject(); } return parentDict; } /** * Gets the parent field. * * @return another form field that this field belongs to. */ public PdfFormField getParentField() { return this.parent; } /** * Gets the current field name. * * @return the current field name, as a {@link PdfString}. */ public PdfString getFieldName() { String parentName = ""; PdfDictionary parentDict = getParent(); if (parentDict != null) { PdfFormField parentField = getParentField(); if (parentField == null) { parentField = PdfFormField.makeFormField(getParent(), getDocument()); } PdfString pName = parentField.getFieldName(); if (pName != null) { parentName = pName.toUnicodeString() + "."; } } PdfString name = getPdfObject().getAsString(PdfName.T); if (name != null) { return new PdfString(parentName + name.toUnicodeString(), PdfEncodings.UNICODE_BIG); } if (isTerminalFormField()) { return new PdfString(parentName, PdfEncodings.UNICODE_BIG); } return null; } /** * Gets default appearance string containing a sequence of valid page-content graphics or text state operators that * define such properties as the field's text size and color. * * @return the default appearance graphics, as a {@link PdfString}. */ public abstract PdfString getDefaultAppearance(); /** * Gets the current fontSize of the form field. * * @return the current fontSize. */ public float getFontSize() { float fontSizeToReturn = fontSize == -1 && parent != null ? parent.getFontSize() : fontSize; if (fontSizeToReturn == -1) { fontSizeToReturn = DEFAULT_FONT_SIZE; } return fontSizeToReturn; } /** * Gets the current font of the form field. * * @return the current {@link PdfFont font} */ public PdfFont getFont() { PdfFont fontToReturn = font == null && parent != null ? parent.getFont() : font; if (fontToReturn == null) { fontToReturn = getDocument().getDefaultFont(); } return fontToReturn; } /** * Gets the current color of the form field. * * @return the current {@link Color color} */ public Color getColor() { return color == null && parent != null ? parent.getColor() : color; } /** * Gets the declared conformance level. * Deprecated use {@link AbstractPdfFormField} getPdfConformanceLevel * * @return the {@link PdfAConformanceLevel} */ @Deprecated() public PdfAConformanceLevel getPdfAConformanceLevel() { if (pdfConformanceLevel == null && parent != null) { return parent.getPdfAConformanceLevel(); } if (pdfConformanceLevel instanceof PdfAConformanceLevel){ return (PdfAConformanceLevel) pdfConformanceLevel; } return null; } /** * Gets the declared conformance level. * * @return the {@link IConformanceLevel} */ public IConformanceLevel getPdfConformanceLevel() { return pdfConformanceLevel == null && parent != null ? parent.getPdfConformanceLevel() : pdfConformanceLevel; } /** * This method regenerates appearance stream of the field. Use it if you * changed any field parameters and didn't use setValue method which * generates appearance by itself. * * @return whether or not the regeneration was successful. */ public abstract boolean regenerateField(); /** * This method disables regeneration of the field and its children appearance stream. So all of its children * in the hierarchy will also not be regenerated. * *

* Note that after this method is called field will be regenerated * only during {@link AbstractPdfFormField#enableFieldRegeneration()} call. */ public void disableFieldRegeneration() { this.enableFieldRegeneration = false; if (this instanceof PdfFormField) { for (AbstractPdfFormField child : ((PdfFormField) this).getChildFields()) { child.disableFieldRegeneration(); } } } /** * This method enables regeneration of the field appearance stream. Please note that this method enables * regeneration for the children of the field. Also, appearance will be regenerated during this method call. * *

* Should be called after {@link AbstractPdfFormField#disableFieldRegeneration()} method call. */ public void enableFieldRegeneration() { this.enableFieldRegeneration = true; if (this instanceof PdfFormField) { for (AbstractPdfFormField child : ((PdfFormField) this).getAllChildFields()) { child.enableFieldRegeneration = true; } } regenerateField(); } /** * This method disables regeneration of the current field appearance stream. */ public void disableCurrentFieldRegeneration() { this.enableFieldRegeneration = false; } /** * This method enables regeneration of the current field appearance stream and regenerates it. */ public void enableCurrentFieldRegeneration() { this.enableFieldRegeneration = true; regenerateField(); } /** * This method checks if field appearance stream regeneration is enabled. * * @return true if regeneration is enabled for this field (and all of its ancestors), false otherwise. */ public boolean isFieldRegenerationEnabled() { return this.enableFieldRegeneration; } /** * Sets the text color and does not regenerate appearance stream. * * @param color the new value for the Color. * * @return the edited field. */ void setColorNoRegenerate(Color color) { this.color = color; } /** * Gets the appearance state names. * * @return an array of Strings containing the names of the appearance states. */ public abstract String[] getAppearanceStates(); /** * Inserts the value into the {@link PdfDictionary} of this field and associates it with the specified key. * If the key is already present in this field dictionary, * this method will override the old value with the specified one. * * @param key key to insert or to override. * @param value the value to associate with the specified key. * * @return the edited field. */ public AbstractPdfFormField put(PdfName key, PdfObject value) { getPdfObject().put(key, value); setModified(); return this; } /** * Removes the specified key from the {@link PdfDictionary} of this field. * * @param key key to be removed. * * @return the edited field. */ public AbstractPdfFormField remove(PdfName key) { getPdfObject().remove(key); setModified(); return this; } /** * Releases underlying pdf object and other pdf entities used by wrapper. * This method should be called instead of direct call to {@link PdfObject#release()} if the wrapper is used. */ public void release() { if (!getPdfObject().isModified()) { unsetForbidRelease(); } getPdfObject().release(); } /** * Gets the {@link PdfDocument} that owns that form field. * * @return the {@link PdfDocument} that owns that form field. */ public PdfDocument getDocument() { return getPdfObject().getIndirectReference().getDocument(); } /** * {@inheritDoc} * * @return {@inheritDoc} */ @Override protected boolean isWrappedObjectMustBeIndirect() { return true; } /** * Sets the text color and regenerates appearance stream. * * @param color the new value for the Color. * * @return the edited {@link AbstractPdfFormField}. */ public AbstractPdfFormField setColor(Color color) { this.color = color; regenerateField(); return this; } /** * Basic setter for the font property. Regenerates the field * appearance after setting the new value. * Note that the font will be added to the document so ensure that the font is embedded * if it's a pdf/a document. * * @param font The new font to be set. * * @return The edited {@link AbstractPdfFormField}. */ public AbstractPdfFormField setFont(PdfFont font) { updateFontAndFontSize(font, this.fontSize); regenerateField(); return this; } /** * Basic setter for the fontSize property. Regenerates the * field appearance after setting the new value. * * @param fontSize The new font size to be set. * * @return The edited {@link AbstractPdfFormField}. */ public AbstractPdfFormField setFontSize(float fontSize) { updateFontAndFontSize(this.font, fontSize); regenerateField(); return this; } /** * Basic setter for the fontSize property. Regenerates the * field appearance after setting the new value. * * @param fontSize The new font size to be set. * * @return The edited {@link AbstractPdfFormField}. */ public AbstractPdfFormField setFontSize(int fontSize) { setFontSize((float) fontSize); return this; } /** * Sets zero font size which will be interpreted as auto-size according to ISO 32000-1, 12.7.3.3. * * @return the edited {@link AbstractPdfFormField}. */ public AbstractPdfFormField setFontSizeAutoScale() { this.fontSize = 0; regenerateField(); return this; } /** * Combined setter for the font and fontSize * properties. Regenerates the field appearance after setting the new value. * * @param font The new font to be set. * @param fontSize The new font size to be set. * * @return The edited {@link AbstractPdfFormField}. */ public AbstractPdfFormField setFontAndSize(PdfFont font, float fontSize) { updateFontAndFontSize(font, fontSize); regenerateField(); return this; } /** * Determines whether current form field is terminal or not. * * @return true if this form field is a terminal one, false otherwise. */ public boolean isTerminalFormField() { if (getPdfObject() == null || getPdfObject().get(PdfName.FT) == null) { return false; } for (PdfName terminalField : TERMINAL_FIELDS) { if (terminalField.equals(getPdfObject().get(PdfName.FT))) { return true; } } return false; } void updateFontAndFontSize(PdfFont font, float fontSize) { this.font = font; this.fontSize = fontSize; } void retrieveStyles() { PdfString defaultAppearance = getDefaultAppearance(); if (defaultAppearance != null) { Object[] fontData = splitDAelements(defaultAppearance.getValue()); if (fontData[DA_SIZE] != null && fontData[DA_FONT] != null) { color = (Color) fontData[DA_COLOR]; fontSize = (float) fontData[DA_SIZE]; font = resolveFontName((String) fontData[DA_FONT]); } } } PdfObject getAcroFormObject(PdfName key, int type) { PdfObject acroFormObject = null; PdfDictionary acroFormDictionary = getDocument().getCatalog().getPdfObject().getAsDictionary(PdfName.AcroForm); if (acroFormDictionary != null) { acroFormObject = acroFormDictionary.get(key); } return (acroFormObject != null && acroFormObject.getType() == type) ? acroFormObject : null; } private static Object[] splitDAelements(String da) { PdfTokenizer tk = new PdfTokenizer(new RandomAccessFileOrArray(new RandomAccessSourceFactory().createSource( PdfEncodings.convertToBytes(da, null)))); List stack = new ArrayList<>(); Object[] ret = new Object[3]; try { while (tk.nextToken()) { if (tk.getTokenType() == PdfTokenizer.TokenType.Comment) { continue; } if (tk.getTokenType() == PdfTokenizer.TokenType.Other) { switch (tk.getStringValue()) { case "Tf": if (stack.size() >= 2) { ret[DA_FONT] = stack.get(stack.size() - 2); ret[DA_SIZE] = new Float(stack.get(stack.size() - 1)); } break; case "g": if (stack.size() >= 1) { float gray = new Float(stack.get(stack.size() - 1)); if (gray != 0) { ret[DA_COLOR] = new DeviceGray(gray); } } break; case "rg": if (stack.size() >= 3) { float red = new Float(stack.get(stack.size() - 3)); float green = new Float(stack.get(stack.size() - 2)); float blue = new Float(stack.get(stack.size() - 1)); ret[DA_COLOR] = new DeviceRgb(red, green, blue); } break; case "k": if (stack.size() >= 4) { float cyan = new Float(stack.get(stack.size() - 4)); float magenta = new Float(stack.get(stack.size() - 3)); float yellow = new Float(stack.get(stack.size() - 2)); float black = new Float(stack.get(stack.size() - 1)); ret[DA_COLOR] = new DeviceCmyk(cyan, magenta, yellow, black); } break; default: stack.clear(); break; } } else { stack.add(tk.getStringValue()); } } } catch (Exception ignored) { } return ret; } private PdfFont resolveFontName(String fontName) { PdfDictionary defaultResources = (PdfDictionary) getAcroFormObject(PdfName.DR, PdfObject.DICTIONARY); PdfDictionary defaultFontDic = defaultResources != null ? defaultResources.getAsDictionary(PdfName.Font) : null; if (fontName != null && defaultFontDic != null) { PdfDictionary daFontDict = defaultFontDic.getAsDictionary(new PdfName(fontName)); if (daFontDict != null) { return getDocument().getFont(daFontDict); } } return null; } /** * Indicate whether some other object is "equal to" this one. Compares wrapped objects. */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } return getPdfObject() == ((AbstractPdfFormField) o).getPdfObject(); } /** * Generate a hash code for this object. */ @Override public int hashCode() { return getPdfObject().hashCode(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy