/* * Copyright 2006-2017 ICEsoft Technologies Canada Corp. * * Licensed 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.icepdf.core.pobjects.acroform; import org.icepdf.core.pobjects.Name; import org.icepdf.core.pobjects.Resources; import org.icepdf.core.pobjects.Stream; import org.icepdf.core.pobjects.StringObject; import org.icepdf.core.pobjects.fonts.Font; import org.icepdf.core.pobjects.graphics.GraphicsState; import org.icepdf.core.util.Library; import org.icepdf.core.util.Utils; import org.icepdf.core.util.content.ContentParser; import org.icepdf.core.util.content.ContentParserFactory; import java.awt.*; import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; import static org.icepdf.core.pobjects.acroform.InteractiveForm.DR_KEY; /** * When the contents and properties of a field are known in advance, its visual * appearance can be specified by an appearance stream defined in the PDF file * (see 12.5.5, "Appearance Streams," and 12.5.6.19, "Widget Annotations"). In * some cases, however, the field may contain text whose value is not known * until viewing time. * * @since 5.1 */ public class VariableTextFieldDictionary extends FieldDictionary { private static final Logger logger = Logger.getLogger(VariableTextFieldDictionary.class.toString()); public enum Quadding { LEFT_JUSTIFIED, CENTERED, RIGHT_JUSTIFIED } /** * The 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 colour. */ public static final Name DA_KEY = new Name("DA"); /** * A code specifying the form of quadding (justification) that shall be used * in displaying the text: * 0 Left-justified * 1 Centered * 2 Right-justified * Default value: 0 (left-justified). */ public static final Name Q_KEY = new Name("Q"); /** * A default style string, as described in 12.7.3.4, "Rich Text Strings." */ public static final Name DS_KEY = new Name("DS"); /** * Variable text fields. */ private String defaultAppearance; private String defaultStyle; private String defaultRichText; /** * A rich text string, as described in 12.7.3.4, "Rich Text Strings." */ public static final Name RV_KEY = new Name("RV"); protected Quadding quadding = Quadding.LEFT_JUSTIFIED; protected float size = 12; protected Name fontName = new Name("Helv"); protected Font font = null; protected Color color = Color.BLACK; public VariableTextFieldDictionary(Library library, HashMap entries) { super(library, entries); // parse out quadding Number value = library.getInt(entries, Q_KEY); int quad = value.intValue(); switch (quad) { case 0: quadding = Quadding.LEFT_JUSTIFIED; break; case 1: quadding = Quadding.CENTERED; break; case 2: quadding = Quadding.RIGHT_JUSTIFIED; break; default: quadding = Quadding.LEFT_JUSTIFIED; break; } // get the default style string Object tmp = library.getObject(entries, DS_KEY); if (tmp != null) { defaultStyle = Utils.convertStringObject(library, (StringObject) tmp); } tmp = library.getObject(entries, RV_KEY); if (tmp != null) { if (tmp instanceof StringObject) { defaultStyle = Utils.convertStringObject(library, (StringObject) tmp); } else if (tmp instanceof Stream) { defaultStyle = new String(((Stream) tmp).getDecodedStreamBytes()); } } // parse out fontName, size and color. // /ZaDb 12 Tf 0 g tmp = library.getObject(entries, DA_KEY); if (tmp instanceof StringObject) { defaultAppearance = Utils.convertStringObject(library, (StringObject) tmp); Resources resources = library.getResources(entries, DR_KEY); // use the DA and DR dictionary to get a valid graphics state and thus the // the font and colour information we need to generate a new content stream if (resources != null) { try { ContentParser cp = ContentParserFactory.getInstance() .getContentParser(library, resources); cp.parseTextBlocks(new byte[][]{defaultAppearance.getBytes()}); GraphicsState gs = cp.getGraphicsState(); if (gs != null) { color = gs.getFillColor(); size = gs.getTextState().tsize; if (gs.getTextState().font != null && gs.getTextState().font.getSubTypeFormat() != Font.CID_FORMAT) { font = gs.getTextState().font; fontName = gs.getTextState().fontName; } } } catch (Throwable e) { logger.warning("Could not validate default appearance, defaulting."); } } } } /** * If the DA key is present the appearance stream is generated as is, however if not then the content * is passed and we try to pull the color, size, font, and font name. * * @param content * @return */ public String generateDefaultAppearance(String content, Resources resources) { try { String possibleContent; if (library.getObject(entries, DA_KEY) != null) { possibleContent = library.getString(entries, DA_KEY); } else if (parentField != null && library.getObject(parentField.getEntries(), DA_KEY) != null) { possibleContent = library.getString(parentField.getEntries(), DA_KEY); } else { possibleContent = content != null ? content : ""; } if (resources == null) { resources = library.getCatalog().getInteractiveForm().getResources(); } ContentParser cp = ContentParserFactory.getInstance() .getContentParser(library, resources); cp.parseTextBlocks(new byte[][]{possibleContent.getBytes()}); GraphicsState gs = cp.getGraphicsState(); if (gs != null) { if (gs.getFillColor() != null) color = gs.getFillColor(); if (gs.getTextState().tsize > 0) { size = gs.getTextState().tsize; } else { // default to basic size, as last resort. size = 10; } // further work is needed here to add font mapping support when CID fonts are detected, // this may also be a fix for our asian font write support problem. if (gs.getTextState().font != null && gs.getTextState().font.getSubTypeFormat() != Font.CID_FORMAT) { if (gs.getTextState().font != null) font = gs.getTextState().font; if (gs.getTextState().fontName != null) fontName = gs.getTextState().fontName; } } } catch (Throwable e) { logger.warning("Could not generate default appearance stream."); if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "Error parsing text feld content stream", e); } } return color.getRed() / 255.0f + " " + color.getGreen() / 255.0f + " " + color.getBlue() / 255.0f + " rg " + "/" + fontName + " " + size + " Tf "; } public String getDefaultAppearance() { return defaultAppearance; } public float getDefaultFontSize() { return size; } public Name getFontName() { return fontName; } public float getSize() { return size; } public Font getFont() { return font; } public Color getColor() { return color; } public Quadding getQuadding() { return quadding; } }