pdfbox-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jahew...@apache.org
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 GMT
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<Object> 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<Object> 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.
- * <p>
- * Allowed operators are all which are permitted in text objects.
- * The Tf operator is required specifying the font and the font size
- * </p>
+ * Represents a default appearance string, as found in the /DA entry of free text annotations.
+ * 
+ * <p>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.
  * 
- * <p>
- * <strong>Currently only the Tf operator is abstracted through this class!</strong>
- * </p>
+ * Note: This class is not yet public, as its API is still unstable.
  */
-class DefaultAppearanceHandler
+class PDAppearanceString
 {
+    private final List<Object> 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<Object> 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<Object> getTokens()
+    void writeTo(PDPageContentStream contents) throws IOException
     {
-        return appearanceTokens;
+        contents.setFont(getFont(), getFontSize());
+        // todo: set more state...
     }
-    
-    private List<Object> 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<Object> tokens = new ArrayList<Object>();
-        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 



Mime
View raw message