Return-Path: X-Original-To: apmail-pdfbox-commits-archive@www.apache.org Delivered-To: apmail-pdfbox-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 2955918AFD for ; Tue, 19 May 2015 01:12:27 +0000 (UTC) Received: (qmail 33014 invoked by uid 500); 19 May 2015 01:12:27 -0000 Delivered-To: apmail-pdfbox-commits-archive@pdfbox.apache.org Received: (qmail 32986 invoked by uid 500); 19 May 2015 01:12:27 -0000 Mailing-List: contact commits-help@pdfbox.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@pdfbox.apache.org Delivered-To: mailing list commits@pdfbox.apache.org Received: (qmail 32976 invoked by uid 99); 19 May 2015 01:12:27 -0000 Received: from eris.apache.org (HELO hades.apache.org) (140.211.11.105) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 19 May 2015 01:12:27 +0000 Received: from hades.apache.org (localhost [127.0.0.1]) by hades.apache.org (ASF Mail Server at hades.apache.org) with ESMTP id E5481AC02E6 for ; Tue, 19 May 2015 01:12:26 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r1680146 - in /pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form: AppearanceGeneratorHelper.java DefaultAppearanceHandler.java PDAppearanceString.java PDVariableText.java Date: Tue, 19 May 2015 01:12:26 -0000 To: commits@pdfbox.apache.org From: jahewson@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20150519011226.E5481AC02E6@hades.apache.org> Author: jahewson Date: Tue May 19 01:12:26 2015 New Revision: 1680146 URL: http://svn.apache.org/r1680146 Log: PDFBOX-2333: Replace DefaultAppearanceHandler with PDAppearanceString Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAppearanceString.java - copied, changed from r1679630, pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/DefaultAppearanceHandler.java Removed: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/DefaultAppearanceHandler.java Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDVariableText.java Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java?rev=1680146&r1=1680145&r2=1680146&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/AppearanceGeneratorHelper.java Tue May 19 01:12:26 2015 @@ -24,12 +24,10 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.pdfbox.contentstream.operator.Operator; import org.apache.pdfbox.cos.COSName; -import org.apache.pdfbox.cos.COSNumber; import org.apache.pdfbox.cos.COSStream; import org.apache.pdfbox.pdfparser.PDFStreamParser; import org.apache.pdfbox.pdfwriter.ContentStreamWriter; import org.apache.pdfbox.pdmodel.PDPageContentStream; -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.PDFontDescriptor; @@ -53,7 +51,7 @@ class AppearanceGeneratorHelper private static final Operator EMC = Operator.getOperator("EMC"); private final PDVariableText field; - private final DefaultAppearanceHandler defaultAppearanceHandler; + private final PDAppearanceString defaultAppearance; private String value; /** @@ -65,7 +63,7 @@ class AppearanceGeneratorHelper AppearanceGeneratorHelper(PDVariableText field) throws IOException { this.field = field; - this.defaultAppearanceHandler = new DefaultAppearanceHandler(field.getDefaultAppearance()); + this.defaultAppearance = field.getDefaultAppearanceString(); } /** @@ -110,8 +108,7 @@ class AppearanceGeneratorHelper // TODO support appearances other than "normal" } - PDFont font = getFontAndUpdateResources(appearanceStream); - setAppearanceContent(appearanceStream, widget, font); + setAppearanceContent(widget, appearanceStream); } } } @@ -130,112 +127,17 @@ class AppearanceGeneratorHelper } /** - * To update an existing appearance stream first copy any needed resources from the - * document’s DR dictionary into the stream’s Resources dictionary. - * If the DR and Resources dictionaries contain resources with the same name, - * the one already in the Resources dictionary shall be left intact, - * not replaced with the corresponding value from the DR dictionary. - */ - private PDFont getFontAndUpdateResources(PDAppearanceStream appearanceStream) throws IOException - { - PDFont font; - PDResources streamResources = appearanceStream.getResources(); - PDResources formResources = field.getAcroForm().getDefaultResources(); - - if (streamResources == null && formResources == null) - { - throw new IOException("Unable to generate field appearance - " + - "missing required resources"); - } - - COSName cosFontName = defaultAppearanceHandler.getFontName(); - - if (streamResources != null) - { - font = streamResources.getFont(cosFontName); - if (font != null) - { - return font; - } - } - else - { - streamResources = new PDResources(); - appearanceStream.setResources(streamResources); - } - - if (formResources != null) - { - font = formResources.getFont(cosFontName); - if (font != null) - { - streamResources.put(cosFontName, font); - return font; - } - } - - // if we get here the font might be there but under a different name - // which is incorrect but try to treat the resource name as the font name - font = resolveFont(streamResources, formResources, cosFontName); - - if (font != null) - { - streamResources.put(cosFontName, font); - return font; - } - else - { - throw new IOException("Unable to generate field appearance - " + - "missing required font resources: " + cosFontName); - } - } - - /** - * Get the font from the resources. - * - * @return the retrieved font - * @throws IOException - */ - private PDFont resolveFont(PDResources streamResources, PDResources formResources, - COSName cosFontName) throws IOException - { - // if the font couldn't be retrieved it might be because the font name - // in the DA string didn't point to the font resources dictionary entry but - // is the name of the font itself. So try to resolve that. - - PDFont font; - if (streamResources != null) - { - for (COSName fontName : streamResources.getFontNames()) - { - font = streamResources.getFont(fontName); - if (font.getName().equals(cosFontName.getName())) - { - return font; - } - } - } - - if (formResources != null) - { - for (COSName fontName : formResources.getFontNames()) - { - font = formResources.getFont(fontName); - if (font.getName().equals(cosFontName.getName())) - { - return font; - } - } - } - return null; - } - - /** * Constructs and sets new contents for given appearance stream. */ - private void setAppearanceContent(PDAppearanceStream appearanceStream, - PDAnnotationWidget widget, PDFont font) throws IOException + private void setAppearanceContent(PDAnnotationWidget widget, + PDAppearanceStream appearanceStream) throws IOException { + // first copy any needed resources from the document’s DR dictionary into + // the stream’s Resources dictionary + defaultAppearance.copyNeededResourcesTo(appearanceStream); + + // then replace the existing contents of the appearance stream from /Tx BMC + // to the matching EMC ByteArrayOutputStream output = new ByteArrayOutputStream(); ContentStreamWriter writer = new ContentStreamWriter(output); @@ -255,7 +157,7 @@ class AppearanceGeneratorHelper // insert field contents PDRectangle boundingBox = resolveBoundingBox(widget, appearanceStream); - insertGeneratedAppearance(boundingBox, output, font, tokens, appearanceStream); + insertGeneratedAppearance(widget, appearanceStream, boundingBox, output); int emcIndex = tokens.indexOf(Operator.getOperator("EMC")); if (emcIndex == -1) @@ -276,19 +178,22 @@ class AppearanceGeneratorHelper /** * Generate and insert text content and clipping around it. */ - private void insertGeneratedAppearance(PDRectangle boundingBox, OutputStream output, - PDFont font, List tokens, PDAppearanceStream appearanceStream) - throws IOException + private void insertGeneratedAppearance(PDAnnotationWidget widget, PDAppearanceStream appearanceStream, + PDRectangle bbox, OutputStream output) throws IOException { PDPageContentStream contents = new PDPageContentStream(field.getAcroForm().getDocument(), appearanceStream, output); - + // Acrobat calculates the left and right padding dependent on the offset of the border edge // This calculation works for forms having been generated by Acrobat. // The minimum distance is always 1f even if there is no rectangle being drawn around. - float lineWidth = getLineWidth(tokens); - PDRectangle paddingEdge = applyPadding(boundingBox,Math.max(1f, lineWidth)); - PDRectangle contentEdge = applyPadding(paddingEdge,Math.max(1f, lineWidth)); + float lineWidth = 0; + if (widget.getBorderStyle() != null) + { + lineWidth = widget.getBorderStyle().getWidth(); + } + PDRectangle paddingEdge = applyPadding(bbox, Math.max(1f, lineWidth)); + PDRectangle contentEdge = applyPadding(paddingEdge, Math.max(1f, lineWidth)); contents.saveGraphicsState(); @@ -300,22 +205,15 @@ class AppearanceGeneratorHelper // start the text output contents.beginText(); - // calculate the fontSize + // write the /DA string + field.getDefaultAppearanceString().writeTo(contents); + + // get the font + PDFont font = field.getDefaultAppearanceString().getFont(); + + // calculate the fontSize (because 0 = autosize) float fontSize = calculateFontSize(font, contentEdge); - if (!defaultAppearanceHandler.getTokens().isEmpty()) - { - defaultAppearanceHandler.setFontSize(fontSize); - ContentStreamWriter daWriter = new ContentStreamWriter(output); - contents.setFont(font, fontSize); - - // the font has already been set so only write the remaining parts of the DA string - daWriter.writeTokens(defaultAppearanceHandler.getTokens().subList( - defaultAppearanceHandler.getTokens().indexOf(Operator.getOperator("Tf")) + 1, - defaultAppearanceHandler.getTokens().size() - )); - } - // calculation of the vertical offset from where the text will be printed float verticalOffset = calculateVerticalOffset(paddingEdge, contentEdge, font, fontSize); @@ -376,27 +274,6 @@ class AppearanceGeneratorHelper } /** - * w in an appearance stream represents the lineWidth. - * - * @return the linewidth - */ - private float getLineWidth(List tokens) - { - float retval = 0f; - if (tokens != null) - { - int btIndex = tokens.indexOf(Operator.getOperator("BT")); - int wIndex = tokens.indexOf(Operator.getOperator("w")); - // the w should only be used if it is before the first BT. - if (wIndex > 0 && (wIndex < btIndex || btIndex == -1)) - { - retval = ((COSNumber) tokens.get(wIndex - 1)).floatValue(); - } - } - return retval; - } - - /** * My "not so great" method for calculating the fontsize. It does not work superb, but it * handles ok. * @@ -405,13 +282,7 @@ class AppearanceGeneratorHelper */ private float calculateFontSize(PDFont pdFont, PDRectangle contentEdge) throws IOException { - // default font size is 12 in Acrobat - float fontSize = 12f; - - if (!defaultAppearanceHandler.getTokens().isEmpty()) - { - fontSize = defaultAppearanceHandler.getFontSize(); - } + float fontSize = defaultAppearance.getFontSize(); // if the font size is 0 the size depends on the content if (fontSize == 0 && !isMultiLine()) Copied: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAppearanceString.java (from r1679630, pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/DefaultAppearanceHandler.java) URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAppearanceString.java?p2=pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAppearanceString.java&p1=pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/DefaultAppearanceHandler.java&r1=1679630&r2=1680146&rev=1680146&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/DefaultAppearanceHandler.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAppearanceString.java Tue May 19 01:12:26 2015 @@ -18,103 +18,159 @@ package org.apache.pdfbox.pdmodel.intera import java.io.ByteArrayInputStream; import java.io.IOException; -import java.util.ArrayList; import java.util.List; - import org.apache.pdfbox.contentstream.operator.Operator; -import org.apache.pdfbox.cos.COSFloat; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSNumber; +import org.apache.pdfbox.cos.COSString; import org.apache.pdfbox.pdfparser.PDFStreamParser; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream; /** - * The default appearance, an inheritable attribute contained in the dictionaries - * /DA entry, contains any graphics state or text state operators needed - * to establish the graphics state parameters, such as text size - * and color, for displaying the field’s variable text. - *

- * Allowed operators are all which are permitted in text objects. - * The Tf operator is required specifying the font and the font size - *

+ * Represents a default appearance string, as found in the /DA entry of free text annotations. + * + *

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

- * Currently only the Tf operator is abstracted through this class! - *

+ * Note: This class is not yet public, as its API is still unstable. */ -class DefaultAppearanceHandler +class PDAppearanceString { + private final List tokens; + private final PDResources defaultResources; + /** - * The tokens making up the content of the default appearance string. + * Constructor for reading an existing DA string. + * + * @param defaultResources DR entry + * @param defaultAppearance DA entry + * @throws IOException If the DA could not be parsed */ - private List appearanceTokens; - - DefaultAppearanceHandler(String defaultApperanceString) throws IOException + PDAppearanceString(COSString defaultAppearance, PDResources defaultResources) throws IOException { - appearanceTokens = getStreamTokens(defaultApperanceString); + if (defaultAppearance == null) + { + throw new IllegalArgumentException("/DA is a required entry"); + } + + if (defaultResources == null) + { + throw new IllegalArgumentException("/DR is a required entry"); + } + + ByteArrayInputStream stream = new ByteArrayInputStream(defaultAppearance.getBytes()); + PDFStreamParser parser = new PDFStreamParser(stream); + parser.parse(); + tokens = parser.getTokens(); + parser.close(); + + this.defaultResources = defaultResources; } /** - * Get the font size. - * @return resolved font size. + * Returns the font size. */ - float getFontSize() + public float getFontSize() { - if (!appearanceTokens.isEmpty()) + if (!tokens.isEmpty()) { // daString looks like "BMC /Helv 3.4 Tf EMC" // use the fontsize of the default existing apperance stream - int fontIndex = appearanceTokens.indexOf(Operator.getOperator("Tf")); + int fontIndex = tokens.indexOf(Operator.getOperator("Tf")); if (fontIndex != -1) { - return ((COSNumber) appearanceTokens.get(fontIndex - 1)).floatValue(); + return ((COSNumber) tokens.get(fontIndex - 1)).floatValue(); } } - return 0f; + + // default font size is 12 in Acrobat + return 12; } /** - * Set the font size. - * @param fontSize the font size for the Tf operator + * w in an appearance stream represents the lineWidth. + * + * @return the linewidth + */ + public float getLineWidth() + { + float retval = 0f; + if (tokens != null) + { + int btIndex = tokens.indexOf(Operator.getOperator("BT")); + int wIndex = tokens.indexOf(Operator.getOperator("w")); + // the w should only be used if it is before the first BT. + if (wIndex > 0 && (wIndex < btIndex || btIndex == -1)) + { + retval = ((COSNumber) tokens.get(wIndex - 1)).floatValue(); + } + } + return retval; + } + + /** + * Returns the font. + * + * @throws IOException If the font could not be found. */ - void setFontSize(float fontSize) + public PDFont getFont() throws IOException { - int fontIndex = appearanceTokens.indexOf(Operator.getOperator("Tf")); - if (fontIndex != -1) + COSName name = getFontResourceName(); + PDFont font = defaultResources.getFont(name); + + // todo: handle cases where font == null with special mapping logic (see PDFBOX-2661) + if (font == null) { - appearanceTokens.set(fontIndex - 1, new COSFloat(fontSize)); + throw new IOException("Could not find font: /" + name.getName()); } + + return font; } + /** - * Get the font name. - * @return the resolved font name. + * Returns the name of the font in the Resources. */ - COSName getFontName() + private COSName getFontResourceName() { - int setFontOperatorIndex = appearanceTokens.indexOf(Operator.getOperator("Tf")); - return (COSName) appearanceTokens.get(setFontOperatorIndex - 2); + int setFontOperatorIndex = tokens.indexOf(Operator.getOperator("Tf")); + return (COSName) tokens.get(setFontOperatorIndex - 2); } - + /** - * Get the tokens resolved from the default appearance string. - * @return the resolved tokens + * Writes the DA string to the given content stream. */ - List getTokens() + void writeTo(PDPageContentStream contents) throws IOException { - return appearanceTokens; + contents.setFont(getFont(), getFontSize()); + // todo: set more state... } - - private List getStreamTokens(String defaultAppearanceString) throws IOException + + /** + * Copies any needed resources from the document’s DR dictionary into the stream’s Resources + * dictionary. Resources with the same name shall be left intact. + */ + void copyNeededResourcesTo(PDAppearanceStream appearanceStream) throws IOException { - List tokens = new ArrayList(); - if (defaultAppearanceString != null && !defaultAppearanceString.isEmpty()) + // make sure we have resources + PDResources streamResources = appearanceStream.getResources(); + if (streamResources == null) { - PDFStreamParser parser = null; - ByteArrayInputStream stream = new ByteArrayInputStream(defaultAppearanceString.getBytes()); - parser = new PDFStreamParser(stream); - parser.parse(); - tokens = parser.getTokens(); - parser.close(); + streamResources = new PDResources(); + appearanceStream.setResources(streamResources); } - return tokens; + + // fonts + COSName fontName = getFontResourceName(); + if (streamResources.getFont(fontName) == null) + { + streamResources.put(fontName, getFont()); + } + + // todo: other kinds of resource... } } Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDVariableText.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDVariableText.java?rev=1680146&r1=1680145&r2=1680146&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDVariableText.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDVariableText.java Tue May 19 01:12:26 2015 @@ -24,6 +24,7 @@ import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSNumber; import org.apache.pdfbox.cos.COSStream; import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.pdmodel.PDResources; /** * Base class for fields which use "Variable Text". @@ -76,6 +77,23 @@ public abstract class PDVariableText ext } /** + * Get the default appearance. + * + * This is an inheritable attribute. + * + * The default appearance contains a set of default graphics and text operators + * to define the field’s text size and color. + * + * @return the DA element of the dictionary object + */ + PDAppearanceString getDefaultAppearanceString() throws IOException + { + COSString da = (COSString) getInheritableAttribute(COSName.DA); + PDResources dr = getAcroForm().getDefaultResources(); + return new PDAppearanceString(da, dr); + } + + /** * Set the default appearance. * * This will set the local default appearance for the variable text field only not