Author: msahyoun
Date: Tue Sep  9 14:20:30 2014
New Revision: 1623830

URL: http://svn.apache.org/r1623830
Log:
PDFBOX-2249 Basic support to handle listbox appearances

Modified:
    
pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAppearance.java
    
pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDChoiceField.java

Modified: 
pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAppearance.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAppearance.java?rev=1623830&r1=1623829&r2=1623830&view=diff
==============================================================================
--- 
pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAppearance.java
 (original)
+++ 
pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAppearance.java
 Tue Sep  9 14:20:30 2014
@@ -21,47 +21,40 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintWriter;
-
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 
 import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSFloat;
+import org.apache.pdfbox.cos.COSInteger;
 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.pdfparser.PDFStreamParser;
 import org.apache.pdfbox.pdfwriter.ContentStreamWriter;
-
 import org.apache.pdfbox.pdmodel.PDResources;
-
 import org.apache.pdfbox.pdmodel.common.COSObjectable;
 import org.apache.pdfbox.pdmodel.common.PDRectangle;
-
 import org.apache.pdfbox.pdmodel.font.PDFont;
 import org.apache.pdfbox.pdmodel.font.PDFontDescriptor;
 import org.apache.pdfbox.pdmodel.font.PDSimpleFont;
-
 import 
org.apache.pdfbox.pdmodel.interactive.action.PDFormFieldAdditionalActions;
 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
-
 import org.apache.pdfbox.util.PDFOperator;
 
 /**
- * This one took me a while, but i'm proud to say that it handles
- * the appearance of a textbox. This allows you to apply a value to
- * a field in the document and handle the appearance so that the
- * value is actually visible too.
- * The problem was described by Ben Litchfield, the author of the
- * example: org.apache.pdfbox.examlpes.fdf.ImportFDF. So Ben, here is the
- * solution.
+ * This one took me a while, but i'm proud to say that it handles the 
appearance of a textbox. This allows you to apply
+ * a value to a field in the document and handle the appearance so that the 
value is actually visible too. The problem
+ * was described by Ben Litchfield, the author of the example: 
org.apache.pdfbox.examlpes.fdf.ImportFDF. So Ben, here is
+ * the solution.
  *
  * @author sug
  * @author <a href="mailto:b...@benlitchfield.com";>Ben Litchfield</a>
@@ -77,34 +70,74 @@ public class PDAppearance
     private final PDAcroForm acroForm;
     private List<COSObjectable> widgets = new ArrayList<COSObjectable>();
 
+    /**
+     * 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 String HIGHLIGHT_COLOR = "0.600006 0.756866 0.854904 
rg";
+
+    /**
+     * The default padding.
+     *
+     * Adobe adds a default padding of 1 to the widgets bounding box.
+     *
+     */
+    private static final int DEFAULT_PADDING = 1;
+
+    /**
+     * The padding area.
+     *
+     * The box from where he padding into the content area will be calculated.
+     *
+     * The default value is to do a padding of 1 on each side of the widgets 
bounding box.
+     * 
+     * This might be overwritten by a new setting within the BMC/EMC sequence
+     */
+    private PDRectangle paddingEdge = null;
+
+    /**
+     * The content area.
+     *
+     * The inner box where the content will be printed The default value is to 
do a padding of 1 on each side of the
+     * paddingEdge.
+     *
+     * This might be overwritten by a new setting within the BMC/EMC sequence
+     */
+    private PDRectangle contentArea = null;
 
     /**
      * Constructs a COSAppearnce from the given field.
      *
-     * @param theAcroForm the acro form that this field is part of.
-     * @param field the field which you wish to control the appearance of
-     * @throws IOException If there is an error creating the appearance.
+     * @param theAcroForm
+     *            the acro form that this field is part of.
+     * @param field
+     *            the field which you wish to control the appearance of
+     * @throws IOException
+     *             If there is an error creating the appearance.
      */
-    public PDAppearance( PDAcroForm theAcroForm, PDVariableText field ) throws 
IOException
+    public PDAppearance(PDAcroForm theAcroForm, PDVariableText field) throws 
IOException
     {
         acroForm = theAcroForm;
         parent = field;
 
         widgets = field.getKids();
-        if( widgets == null )
+        if (widgets == null)
         {
             widgets = new ArrayList<COSObjectable>();
-            widgets.add( field.getWidget() );
+            widgets.add(field.getWidget());
         }
 
         defaultAppearance = getDefaultAppearance();
 
-
     }
 
     /**
-     * Returns the default apperance of a textbox. If the textbox
-     * does not have one, then it will be taken from the AcroForm.
+     * Returns the default appearance of a textbox. If the textbox does not 
have one, then it will be taken from the
+     * AcroForm.
+     * 
      * @return The DA element
      */
     private COSString getDefaultAppearance()
@@ -113,13 +146,13 @@ public class PDAppearance
         COSString dap = parent.getDefaultAppearance();
         if (dap == null)
         {
-            COSArray kids = 
(COSArray)parent.getDictionary().getDictionaryObject( COSName.KIDS );
-            if( kids != null && kids.size() > 0 )
+            COSArray kids = (COSArray) 
parent.getDictionary().getDictionaryObject(COSName.KIDS);
+            if (kids != null && kids.size() > 0)
             {
-                COSDictionary firstKid = (COSDictionary)kids.getObject( 0 );
-                dap = (COSString)firstKid.getDictionaryObject( COSName.DA );
+                COSDictionary firstKid = (COSDictionary) kids.getObject(0);
+                dap = (COSString) firstKid.getDictionaryObject(COSName.DA);
             }
-            if( dap == null )
+            if (dap == null)
             {
                 dap = (COSString) 
acroForm.getDictionary().getDictionaryObject(COSName.DA);
             }
@@ -130,14 +163,14 @@ public class PDAppearance
     private int getQ()
     {
         int q = parent.getQ();
-        if( parent.getDictionary().getDictionaryObject( COSName.Q ) == null )
+        if (parent.getDictionary().getDictionaryObject(COSName.Q) == null)
         {
-            COSArray kids = 
(COSArray)parent.getDictionary().getDictionaryObject( COSName.KIDS );
-            if( kids != null && kids.size() > 0 )
+            COSArray kids = (COSArray) 
parent.getDictionary().getDictionaryObject(COSName.KIDS);
+            if (kids != null && kids.size() > 0)
             {
-                COSDictionary firstKid = (COSDictionary)kids.getObject( 0 );
-                COSNumber qNum = (COSNumber)firstKid.getDictionaryObject( 
COSName.Q );
-                if( qNum != null )
+                COSDictionary firstKid = (COSDictionary) kids.getObject(0);
+                COSNumber qNum = (COSNumber) 
firstKid.getDictionaryObject(COSName.Q);
+                if (qNum != null)
                 {
                     q = qNum.intValue();
                 }
@@ -151,39 +184,39 @@ public class PDAppearance
      *
      * @return The tokens in the original appearance stream
      */
-    private List getStreamTokens( PDAppearanceStream appearanceStream ) throws 
IOException
+    private List getStreamTokens(PDAppearanceStream appearanceStream) throws 
IOException
     {
         List tokens = null;
-        if( appearanceStream != null )
+        if (appearanceStream != null)
         {
-            tokens = getStreamTokens( appearanceStream.getStream() );
+            tokens = getStreamTokens(appearanceStream.getStream());
         }
         return tokens;
     }
 
-    private List getStreamTokens( COSString string ) throws IOException
+    private List getStreamTokens(COSString string) throws IOException
     {
         PDFStreamParser parser;
 
         List tokens = null;
-        if( string != null )
+        if (string != null)
         {
-            ByteArrayInputStream stream = new ByteArrayInputStream( 
string.getBytes() );
-            parser = new PDFStreamParser( stream, 
acroForm.getDocument().getDocument().getScratchFile() );
+            ByteArrayInputStream stream = new 
ByteArrayInputStream(string.getBytes());
+            parser = new PDFStreamParser(stream, 
acroForm.getDocument().getDocument().getScratchFile());
             parser.parse();
             tokens = parser.getTokens();
         }
         return tokens;
     }
 
-    private List getStreamTokens( COSStream stream ) throws IOException
+    private List getStreamTokens(COSStream stream) throws IOException
     {
         PDFStreamParser parser;
 
         List tokens = null;
-        if( stream != null )
+        if (stream != null)
         {
-            parser = new PDFStreamParser( stream );
+            parser = new PDFStreamParser(stream);
             parser.parse();
             tokens = parser.getTokens();
         }
@@ -195,204 +228,352 @@ public class PDAppearance
      *
      * @return true if it contains any content
      */
-    private boolean containsMarkedContent( List stream )
+    private boolean containsMarkedContent(List stream)
     {
-        return stream.contains( PDFOperator.getOperator( "BMC" ) );
+        return stream.contains(PDFOperator.getOperator("BMC"));
+    }
+
+    /**
+     * Apply padding to a rectangle.
+     *
+     * Padding is used to create different boxes within the widgets 'box 
model'.
+     *
+     * @return a new rectangle with padding applied
+     */
+    private PDRectangle applyPadding(PDRectangle bbox, float padding)
+    {
+        PDRectangle area = new PDRectangle(bbox.getCOSArray());
+
+        area.setLowerLeftX(area.getLowerLeftX() + padding);
+        area.setLowerLeftY(area.getLowerLeftY() + padding);
+        area.setUpperRightX(area.getUpperRightX() - padding);
+        area.setUpperRightY(area.getUpperRightY() - padding);
+
+        return area;
     }
 
     /**
      * This is the public method for setting the appearance stream.
      *
-     * @param apValue the String value which the apperance shoud represent
+     * @param apValue
+     *            the String value which the appearance should represent
      *
-     * @throws IOException If there is an error creating the stream.
+     * @throws IOException
+     *             If there is an error creating the stream.
      */
     public void setAppearanceValue(String apValue) throws IOException
     {
         value = apValue;
+
         Iterator<COSObjectable> widgetIter = widgets.iterator();
-        while( widgetIter.hasNext() )
+        while (widgetIter.hasNext())
         {
             COSObjectable next = widgetIter.next();
             PDField field = null;
             PDAnnotationWidget widget;
-            if( next instanceof PDField )
+            if (next instanceof PDField)
             {
-                field = (PDField)next;
+                field = (PDField) next;
                 widget = field.getWidget();
             }
             else
             {
-                widget = (PDAnnotationWidget)next;
+                widget = (PDAnnotationWidget) next;
             }
             PDFormFieldAdditionalActions actions = null;
-            if( field != null )
+            if (field != null)
             {
                 actions = field.getActions();
             }
-            if( actions != null &&
-                actions.getF() != null &&
-                widget.getDictionary().getDictionaryObject( COSName.AP ) 
==null)
+            if (actions != null && actions.getF() != null
+                    && widget.getDictionary().getDictionaryObject(COSName.AP) 
== null)
             {
-                //do nothing because the field will be formatted by acrobat
-                //when it is opened.  See FreedomExpressions.pdf for an 
example of this.
+                // do nothing because the field will be formatted by acrobat
+                // when it is opened. See FreedomExpressions.pdf for an 
example of this.
             }
             else
             {
 
                 PDAppearanceDictionary appearance = widget.getAppearance();
-                if( appearance == null )
+                if (appearance == null)
                 {
                     appearance = new PDAppearanceDictionary();
-                    widget.setAppearance( appearance );
+                    widget.setAppearance(appearance);
                 }
 
                 Map normalAppearance = appearance.getNormalAppearance();
-                PDAppearanceStream appearanceStream = 
(PDAppearanceStream)normalAppearance.get( "default" );
-                if( appearanceStream == null )
+                PDAppearanceStream appearanceStream = (PDAppearanceStream) 
normalAppearance.get("default");
+                if (appearanceStream == null)
                 {
                     COSStream cosStream = 
acroForm.getDocument().getDocument().createCOSStream();
-                    appearanceStream = new PDAppearanceStream( cosStream );
-                    appearanceStream.setBoundingBox( 
widget.getRectangle().createRetranslatedRectangle() );
-                    appearance.setNormalAppearance( appearanceStream );
+                    appearanceStream = new PDAppearanceStream(cosStream);
+                    
appearanceStream.setBoundingBox(widget.getRectangle().createRetranslatedRectangle());
+                    appearance.setNormalAppearance(appearanceStream);
                 }
 
-                List tokens = getStreamTokens( appearanceStream );
-                List daTokens = getStreamTokens( getDefaultAppearance() );
-                PDFont pdFont = getFontAndUpdateResources( tokens, 
appearanceStream );
-
-                if (!containsMarkedContent( tokens ))
+                List tokens = getStreamTokens(appearanceStream);
+                List daTokens = getStreamTokens(getDefaultAppearance());
+                PDFont pdFont = getFontAndUpdateResources(tokens, 
appearanceStream);
+
+                // Special handling for listboxes to address PDFBOX-2249
+                // TODO: Shall be addressed properly in a future release
+                if (parent instanceof PDChoiceField
+                        && (parent.getFieldFlags() & ((PDChoiceField) 
parent).FLAG_COMBO) == 0)
                 {
-                    ByteArrayOutputStream output = new ByteArrayOutputStream();
-
-                    //BJL 9/25/2004 Must prepend existing stream
-                    //because it might have operators to draw things like
-                    //rectangles and such
-                    ContentStreamWriter writer = new ContentStreamWriter( 
output );
-                    writer.writeTokens( tokens );
-
-                    output.write( " /Tx BMC\n".getBytes("ISO-8859-1") );
-                    insertGeneratedAppearance( widget, output, pdFont, tokens, 
appearanceStream );
-                    output.write( " EMC".getBytes("ISO-8859-1") );
-                    writeToStream( output.toByteArray(), appearanceStream );
+                    generateListboxAppearance(widget, pdFont, tokens, 
daTokens, appearanceStream, value);
                 }
                 else
                 {
-                    if( tokens != null )
+                    if (!containsMarkedContent(tokens))
                     {
-                        if( daTokens != null )
-                        {
-                            int bmcIndex = tokens.indexOf( 
PDFOperator.getOperator( "BMC" ));
-                            int emcIndex = tokens.indexOf( 
PDFOperator.getOperator( "EMC" ));
-                            if( bmcIndex != -1 && emcIndex != -1 &&
-                                emcIndex == bmcIndex+1 )
-                            {
-                                //if the EMC immediately follows the BMC index 
then should
-                                //insert the daTokens inbetween the two 
markers.
-                                tokens.addAll( emcIndex, daTokens );
-                            }
-                        }
                         ByteArrayOutputStream output = new 
ByteArrayOutputStream();
-                        ContentStreamWriter writer = new ContentStreamWriter( 
output );
-                        float fontSize = calculateFontSize( pdFont, 
appearanceStream.getBoundingBox(), tokens, null );
-                        boolean foundString = false;
-                        for( int i=0; i<tokens.size(); i++ )
+
+                        // BJL 9/25/2004 Must prepend existing stream
+                        // because it might have operators to draw things like
+                        // rectangles and such
+                        ContentStreamWriter writer = new 
ContentStreamWriter(output);
+                        writer.writeTokens(tokens);
+
+                        output.write(" /Tx BMC\n".getBytes("ISO-8859-1"));
+                        insertGeneratedAppearance(widget, output, pdFont, 
tokens, appearanceStream);
+                        output.write(" EMC".getBytes("ISO-8859-1"));
+                        writeToStream(output.toByteArray(), appearanceStream);
+                    }
+                    else
+                    {
+                        if (tokens != null)
                         {
-                            if( tokens.get( i ) instanceof COSString )
+                            if (daTokens != null)
                             {
-                                foundString = true;
-                                COSString drawnString 
=((COSString)tokens.get(i));
-                                drawnString.reset();
-                                drawnString.append( 
apValue.getBytes("ISO-8859-1") );
+                                int bmcIndex = 
tokens.indexOf(PDFOperator.getOperator("BMC"));
+                                int emcIndex = 
tokens.indexOf(PDFOperator.getOperator("EMC"));
+                                if (bmcIndex != -1 && emcIndex != -1 && 
emcIndex == bmcIndex + 1)
+                                {
+                                    // if the EMC immediately follows the BMC 
index then should
+                                    // insert the daTokens inbetween the two 
markers.
+                                    tokens.addAll(emcIndex, daTokens);
+                                }
                             }
-                        }
-                        int setFontIndex = tokens.indexOf( 
PDFOperator.getOperator( "Tf" ));
-                        tokens.set( setFontIndex-1, new COSFloat( fontSize ) );
-                        if( foundString )
-                        {
-                            writer.writeTokens( tokens );
-                        }
-                        else
-                        {
-                            int bmcIndex = tokens.indexOf( 
PDFOperator.getOperator( "BMC" ) );
-                            int emcIndex = tokens.indexOf( 
PDFOperator.getOperator( "EMC" ) );
-
-                            if( bmcIndex != -1 )
+                            ByteArrayOutputStream output = new 
ByteArrayOutputStream();
+                            ContentStreamWriter writer = new 
ContentStreamWriter(output);
+                            float fontSize = calculateFontSize(pdFont, 
appearanceStream.getBoundingBox(), tokens, null);
+                            boolean foundString = false;
+                            for (int i = 0; i < tokens.size(); i++)
                             {
-                                writer.writeTokens( tokens, 0, bmcIndex+1 );
+                                if (tokens.get(i) instanceof COSString)
+                                {
+                                    foundString = true;
+                                    COSString drawnString = ((COSString) 
tokens.get(i));
+                                    drawnString.reset();
+                                    
drawnString.append(apValue.getBytes("ISO-8859-1"));
+                                }
                             }
-                            else
+                            int setFontIndex = 
tokens.indexOf(PDFOperator.getOperator("Tf"));
+                            tokens.set(setFontIndex - 1, new 
COSFloat(fontSize));
+                            if (foundString)
                             {
-                                writer.writeTokens( tokens );
+                                writer.writeTokens(tokens);
                             }
-                            output.write( "\n".getBytes("ISO-8859-1") );
-                            insertGeneratedAppearance( widget, output,
-                                pdFont, tokens, appearanceStream );
-                            if( emcIndex != -1 )
+                            else
                             {
-                                writer.writeTokens( tokens, emcIndex, 
tokens.size() );
+                                int bmcIndex = 
tokens.indexOf(PDFOperator.getOperator("BMC"));
+                                int emcIndex = 
tokens.indexOf(PDFOperator.getOperator("EMC"));
+
+                                if (bmcIndex != -1)
+                                {
+                                    writer.writeTokens(tokens, 0, bmcIndex + 
1);
+                                }
+                                else
+                                {
+                                    writer.writeTokens(tokens);
+                                }
+                                output.write("\n".getBytes("ISO-8859-1"));
+                                insertGeneratedAppearance(widget, output, 
pdFont, tokens, appearanceStream);
+                                if (emcIndex != -1)
+                                {
+                                    writer.writeTokens(tokens, emcIndex, 
tokens.size());
+                                }
                             }
+                            writeToStream(output.toByteArray(), 
appearanceStream);
+                        }
+                        else
+                        {
+                            // hmm?
                         }
-                        writeToStream( output.toByteArray(), appearanceStream 
);
                     }
-                    else
+                }
+            }
+        }
+
+    }
+
+    private void generateListboxAppearance(PDAnnotationWidget fieldWidget, 
PDFont pdFont, List tokens, List daTokens,
+            PDAppearanceStream appearanceStream, String fieldValue) throws 
IOException
+    {
+
+        // create paddingEdge and contentArea from bounding box
+        // Default the contentArea to the boundingBox
+        // taking the padding into account
+        paddingEdge = applyPadding(appearanceStream.getBoundingBox(), 
DEFAULT_PADDING);
+        contentArea = applyPadding(paddingEdge, DEFAULT_PADDING);
+
+        if (!containsMarkedContent(tokens))
+        {
+            ByteArrayOutputStream output = new ByteArrayOutputStream();
+
+            // BJL 9/25/2004 Must prepend existing stream
+            // because it might have operators to draw things like
+            // rectangles and such
+            ContentStreamWriter writer = new ContentStreamWriter(output);
+            writer.writeTokens(tokens);
+
+            output.write(" /Tx BMC\n".getBytes("ISO-8859-1"));
+            insertGeneratedListboxAppearance(fieldWidget, output, pdFont, 
tokens, appearanceStream);
+            output.write(" EMC".getBytes("ISO-8859-1"));
+            writeToStream(output.toByteArray(), appearanceStream);
+        }
+        else
+        {
+            if (tokens != null)
+            {
+                if (daTokens != null)
+                {
+                    int bmcIndex = 
tokens.indexOf(PDFOperator.getOperator("BMC"));
+                    int emcIndex = 
tokens.indexOf(PDFOperator.getOperator("EMC"));
+                    if (bmcIndex != -1 && emcIndex != -1 && emcIndex == 
bmcIndex + 1)
                     {
-                        //hmm?
+                        // if the EMC immediately follows the BMC index then 
should
+                        // insert the daTokens inbetween the two markers.
+                        tokens.addAll(emcIndex, daTokens);
                     }
                 }
+
+                ByteArrayOutputStream output = new ByteArrayOutputStream();
+                ContentStreamWriter writer = new ContentStreamWriter(output);
+                float fontSize = calculateListboxFontSize(pdFont, 
appearanceStream.getBoundingBox(), tokens, daTokens);
+                boolean foundString = false;
+
+                int setFontIndex = 
tokens.indexOf(PDFOperator.getOperator("Tf"));
+                tokens.set(setFontIndex - 1, new COSFloat(fontSize));
+
+                int bmcIndex = tokens.indexOf(PDFOperator.getOperator("BMC"));
+                /*
+                 * Get the contentArea.
+                 * 
+                 * There might be an inner box defined which defines the area 
where the text is printed. This typically
+                 * looks like ... q 1 1 98 70 re W ...
+                 */
+
+                {
+                    int beginTextIndex = 
tokens.indexOf(PDFOperator.getOperator("BT"));
+                    if (beginTextIndex != -1)
+                    {
+
+                        ListIterator innerTokens = 
tokens.listIterator(bmcIndex);
+
+                        while (innerTokens.hasNext())
+                        {
+                            if (innerTokens.next() == 
PDFOperator.getOperator("re")
+                                    && innerTokens.next() == 
PDFOperator.getOperator("W"))
+                            {
+
+                                COSArray array = new COSArray();
+                                array.add((COSNumber) 
tokens.get(innerTokens.previousIndex() - 5));
+                                array.add((COSNumber) 
tokens.get(innerTokens.previousIndex() - 4));
+                                array.add((COSNumber) 
tokens.get(innerTokens.previousIndex() - 3));
+                                array.add((COSNumber) 
tokens.get(innerTokens.previousIndex() - 2));
+
+                                paddingEdge = new PDRectangle(array);
+
+                                // as the re operator is using start and 
width/height adjust the generated
+                                // dimensions
+                                
paddingEdge.setUpperRightX(paddingEdge.getLowerLeftX() + 
paddingEdge.getUpperRightX());
+                                
paddingEdge.setUpperRightY(paddingEdge.getLowerLeftY() + 
paddingEdge.getUpperRightY());
+
+                                contentArea = applyPadding(paddingEdge, 
paddingEdge.getLowerLeftX()
+                                        - 
appearanceStream.getBoundingBox().getLowerLeftX());
+
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                int emcIndex = tokens.indexOf(PDFOperator.getOperator("EMC"));
+
+                if (bmcIndex != -1)
+                {
+                    writer.writeTokens(tokens, 0, bmcIndex + 1);
+                }
+                else
+                {
+                    writer.writeTokens(tokens);
+                }
+                output.write("\n".getBytes("ISO-8859-1"));
+                insertGeneratedListboxAppearance(fieldWidget, output, pdFont, 
tokens, appearanceStream);
+                if (emcIndex != -1)
+                {
+                    writer.writeTokens(tokens, emcIndex, tokens.size());
+                }
+
+                writeToStream(output.toByteArray(), appearanceStream);
+            }
+            else
+            {
+                // hmm?
             }
         }
     }
 
-    private void insertGeneratedAppearance( PDAnnotationWidget fieldWidget, 
OutputStream output,
-        PDFont pdFont, List tokens, PDAppearanceStream appearanceStream ) 
throws IOException
+    private void insertGeneratedAppearance(PDAnnotationWidget fieldWidget, 
OutputStream output, PDFont pdFont,
+            List tokens, PDAppearanceStream appearanceStream) throws 
IOException
     {
-        PrintWriter printWriter = new PrintWriter( output, true );
+        PrintWriter printWriter = new PrintWriter(output, true);
         float fontSize = 0.0f;
         PDRectangle boundingBox = appearanceStream.getBoundingBox();
-        if( boundingBox == null )
+        if (boundingBox == null)
         {
             boundingBox = 
fieldWidget.getRectangle().createRetranslatedRectangle();
         }
-        printWriter.println( "BT" );
-        if( defaultAppearance != null )
+        printWriter.println("BT");
+        if (defaultAppearance != null)
         {
             String daString = defaultAppearance.getString();
-            PDFStreamParser daParser = new PDFStreamParser(new 
ByteArrayInputStream( daString.getBytes("ISO-8859-1") ), null );
+            PDFStreamParser daParser = new PDFStreamParser(new 
ByteArrayInputStream(daString.getBytes("ISO-8859-1")),
+                    null);
             daParser.parse();
             List<Object> daTokens = daParser.getTokens();
-            fontSize = calculateFontSize( pdFont, boundingBox, tokens, 
daTokens );
-            int fontIndex = daTokens.indexOf( PDFOperator.getOperator( "Tf" ) 
);
-            if(fontIndex != -1 )
+            fontSize = calculateFontSize(pdFont, boundingBox, tokens, 
daTokens);
+            int fontIndex = daTokens.indexOf(PDFOperator.getOperator("Tf"));
+            if (fontIndex != -1)
             {
-                daTokens.set( fontIndex-1, new COSFloat( fontSize ) );
+                daTokens.set(fontIndex - 1, new COSFloat(fontSize));
             }
             ContentStreamWriter daWriter = new ContentStreamWriter(output);
-            daWriter.writeTokens( daTokens );
+            daWriter.writeTokens(daTokens);
         }
-        printWriter.println( getTextPosition( boundingBox, pdFont, fontSize, 
tokens ) );
+        printWriter.println(getTextPosition(boundingBox, pdFont, fontSize, 
tokens));
         int q = getQ();
-        if( q == PDTextbox.QUADDING_LEFT )
+        if (q == PDTextbox.QUADDING_LEFT)
         {
-            //do nothing because left is default
+            // do nothing because left is default
         }
-        else if( q == PDTextbox.QUADDING_CENTERED ||
-                 q == PDTextbox.QUADDING_RIGHT )
+        else if (q == PDTextbox.QUADDING_CENTERED || q == 
PDTextbox.QUADDING_RIGHT)
         {
             float fieldWidth = boundingBox.getWidth();
-            float stringWidth = (pdFont.getStringWidth( value )/1000)*fontSize;
+            float stringWidth = (pdFont.getStringWidth(value) / 1000) * 
fontSize;
             float adjustAmount = fieldWidth - stringWidth - 4;
 
-            if( q == PDTextbox.QUADDING_CENTERED )
+            if (q == PDTextbox.QUADDING_CENTERED)
             {
-                adjustAmount = adjustAmount/2.0f;
+                adjustAmount = adjustAmount / 2.0f;
             }
 
-            printWriter.println( adjustAmount + " 0 Td" );
+            printWriter.println(adjustAmount + " 0 Td");
         }
         else
         {
-            throw new IOException( "Error: Unknown justification value:" + q );
+            throw new IOException("Error: Unknown justification value:" + q);
         }
         // add the value as hex string to deal with non ISO-8859-1 data values
         if (!isMultiLineValue(value))
@@ -413,36 +594,171 @@ public class PDAppearance
         printWriter.flush();
     }
 
-    private PDFont getFontAndUpdateResources( List tokens, PDAppearanceStream 
appearanceStream ) throws IOException
+    private void insertGeneratedListboxAppearance(PDAnnotationWidget 
fieldWidget, OutputStream output, PDFont pdFont,
+            List tokens, PDAppearanceStream appearanceStream) throws 
IOException
+    {
+        PrintWriter printWriter = new PrintWriter(output, true);
+        float fontSize = 0.0f;
+        PDRectangle boundingBox = appearanceStream.getBoundingBox();
+        if (boundingBox == null)
+        {
+            boundingBox = 
fieldWidget.getRectangle().createRetranslatedRectangle();
+        }
+
+        List<Object> daTokens = null;
+
+        if (defaultAppearance != null)
+        {
+            String daString = defaultAppearance.getString();
+            PDFStreamParser daParser = new PDFStreamParser(new 
ByteArrayInputStream(daString.getBytes("ISO-8859-1")),
+                    null);
+            daParser.parse();
+            daTokens = daParser.getTokens();
+
+            fontSize = calculateListboxFontSize(pdFont, contentArea, tokens, 
daTokens);
+            int fontIndex = daTokens.indexOf(PDFOperator.getOperator("Tf"));
+            if (fontIndex != -1)
+            {
+                daTokens.set(fontIndex - 1, new COSFloat(fontSize));
+            }
+        }
+
+        // print the paddingEdge
+        printWriter.println("q");
+
+        printWriter.println(paddingEdge.getLowerLeftX() + " " + 
paddingEdge.getLowerLeftY() + " "
+                + paddingEdge.getWidth() + " " + paddingEdge.getHeight() + " " 
+ " re");
+        printWriter.println("W");
+        printWriter.println("n");
+
+        // print the highlight color
+        printWriter.println(HIGHLIGHT_COLOR);
+
+        /*
+         * for a listbox output the rectangle highlighting the selected value
+         */
+
+        COSArray indexEntries = ((PDChoiceField) parent).getSelectedOptions();
+
+        int selectedIndex = ((COSInteger) indexEntries.get(0)).intValue();
+
+        // The first entry which shall be presented might be adjusted by the 
optional TI key
+        // If this entry is present the first entry to be displayed is the 
keys value otherwise
+        // display starts with the first entry in Opt.
+        int topIndex = ((PDChoiceField) parent).getTopIndex();
+
+        if ("Ch".equals(parent.findFieldType()) && ((parent.getFieldFlags() & 
(0x1000000)) == 0))
+        {
+
+            float highlightBoxHeight = pdFont.getFontBoundingBox().getHeight() 
/ 1000 * fontSize;
+
+            printWriter.println(paddingEdge.getLowerLeftX() + " "
+                    + (paddingEdge.getUpperRightY() - highlightBoxHeight * 
(selectedIndex - topIndex + 1)) + " "
+                    + paddingEdge.getWidth() + " " + (highlightBoxHeight) + " 
re");
+            printWriter.println("f");
+            printWriter.println("0 g");
+            printWriter.println("0 G");
+            printWriter.println("1 w");
+        }
+
+        // start of text output
+        printWriter.println("BT");
+
+        if (defaultAppearance != null)
+        {
+            ContentStreamWriter daWriter = new ContentStreamWriter(output);
+            daWriter.writeTokens(daTokens);
+        }
+
+        int q = getQ();
+        if (q == PDTextbox.QUADDING_LEFT)
+        {
+            // do nothing because left is default
+        }
+        else if (q == PDTextbox.QUADDING_CENTERED || q == 
PDTextbox.QUADDING_RIGHT)
+        {
+            float fieldWidth = boundingBox.getWidth();
+            float stringWidth = (pdFont.getStringWidth(value) / 1000) * 
fontSize;
+            float adjustAmount = fieldWidth - stringWidth - 4;
+
+            if (q == PDTextbox.QUADDING_CENTERED)
+            {
+                adjustAmount = adjustAmount / 2.0f;
+            }
+
+            printWriter.println(adjustAmount + " 0 Td");
+        }
+        else
+        {
+            throw new IOException("Error: Unknown justification value:" + q);
+        }
+
+        COSArray options = ((PDChoiceField) parent).getOptions();
+
+        float yTextPos = contentArea.getUpperRightY();
+
+        for (int i = topIndex; i < options.size(); i++)
+        {
+            COSBase option = options.getObject(i);
+            COSArray optionPair = (COSArray) option;
+            COSString optionKey = (COSString) optionPair.getObject(0);
+            COSString optionValue = (COSString) optionPair.getObject(1);
+
+            if (i == topIndex)
+            {
+                yTextPos = yTextPos - pdFont.getFontDescriptor().getAscent() / 
1000 * fontSize;
+            }
+            else
+            {
+                yTextPos = yTextPos - pdFont.getFontBoundingBox().getHeight() 
/ 1000 * fontSize;
+                printWriter.println("BT");
+            }
+
+            printWriter.println(contentArea.getLowerLeftX() + " " + yTextPos + 
" Td");
+            printWriter.println("<" + optionValue.getHexString() + "> Tj");
+
+            if (i - topIndex != (options.size() - 1))
+            {
+                printWriter.println("ET");
+            }
+
+        }
+
+        printWriter.println("ET");
+        printWriter.println("Q");
+        printWriter.flush();
+    }
+
+    private PDFont getFontAndUpdateResources(List tokens, PDAppearanceStream 
appearanceStream) throws IOException
     {
         PDFont retval = null;
         PDResources streamResources = appearanceStream.getResources();
         PDResources formResources = acroForm.getDefaultResources();
-        if( formResources != null )
+        if (formResources != null)
         {
-            if( streamResources == null )
+            if (streamResources == null)
             {
                 streamResources = new PDResources();
-                appearanceStream.setResources( streamResources );
+                appearanceStream.setResources(streamResources);
             }
 
             COSString da = getDefaultAppearance();
-            if( da != null )
+            if (da != null)
             {
                 String data = da.getString();
                 PDFStreamParser streamParser = new PDFStreamParser(
-                        new ByteArrayInputStream( data.getBytes("ISO-8859-1") 
), null );
+                        new ByteArrayInputStream(data.getBytes("ISO-8859-1")), 
null);
                 streamParser.parse();
                 tokens = streamParser.getTokens();
             }
 
-            int setFontIndex = tokens.indexOf( PDFOperator.getOperator( "Tf" 
));
-            COSName cosFontName = (COSName)tokens.get( setFontIndex-2 );
+            int setFontIndex = tokens.indexOf(PDFOperator.getOperator("Tf"));
+            COSName cosFontName = (COSName) tokens.get(setFontIndex - 2);
             String fontName = cosFontName.getName();
-            retval = (PDFont)streamResources.getFonts().get( fontName );
-            if( retval == null )
+            retval = (PDFont) streamResources.getFonts().get(fontName);
+            if (retval == null)
             {
-                retval = (PDFont)formResources.getFonts().get( fontName );
+                retval = (PDFont) formResources.getFonts().get(fontName);
                 streamResources.addFont(retval, fontName);
             }
         }
@@ -457,56 +773,57 @@ public class PDAppearance
     /**
      * Writes the stream to the actual stream in the COSStream.
      *
-     * @throws IOException If there is an error writing to the stream
+     * @throws IOException
+     *             If there is an error writing to the stream
      */
-    private void writeToStream( byte[] data, PDAppearanceStream 
appearanceStream ) throws IOException
+    private void writeToStream(byte[] data, PDAppearanceStream 
appearanceStream) throws IOException
     {
         OutputStream out = 
appearanceStream.getStream().createUnfilteredStream();
-        out.write( data );
+        out.write(data);
         out.flush();
     }
 
     /**
      * w in an appearance stream represents the lineWidth.
+     * 
      * @return the linewidth
      */
-    private float getLineWidth( List tokens )
+    private float getLineWidth(List tokens)
     {
 
         float retval = 1;
-        if( tokens != null )
+        if (tokens != null)
         {
-            int btIndex = tokens.indexOf(PDFOperator.getOperator( "BT" ));
-            int wIndex = tokens.indexOf(PDFOperator.getOperator( "w" ));
-            //the w should only be used if it is before the first BT.
-            if( (wIndex > 0) && (wIndex < btIndex) )
+            int btIndex = tokens.indexOf(PDFOperator.getOperator("BT"));
+            int wIndex = tokens.indexOf(PDFOperator.getOperator("w"));
+            // the w should only be used if it is before the first BT.
+            if ((wIndex > 0) && (wIndex < btIndex))
             {
-                retval = ((COSNumber)tokens.get(wIndex-1)).floatValue();
+                retval = ((COSNumber) tokens.get(wIndex - 1)).floatValue();
             }
         }
         return retval;
     }
 
-    private PDRectangle getSmallestDrawnRectangle( PDRectangle boundingBox, 
List tokens )
+    private PDRectangle getSmallestDrawnRectangle(PDRectangle boundingBox, 
List tokens)
     {
         PDRectangle smallest = boundingBox;
-        for( int i=0; i<tokens.size(); i++ )
+        for (int i = 0; i < tokens.size(); i++)
         {
-            Object next = tokens.get( i );
-            if( next == PDFOperator.getOperator( "re" ) )
+            Object next = tokens.get(i);
+            if (next == PDFOperator.getOperator("re"))
             {
-                COSNumber x = (COSNumber)tokens.get( i-4 );
-                COSNumber y = (COSNumber)tokens.get( i-3 );
-                COSNumber width = (COSNumber)tokens.get( i-2 );
-                COSNumber height = (COSNumber)tokens.get( i-1 );
+                COSNumber x = (COSNumber) tokens.get(i - 4);
+                COSNumber y = (COSNumber) tokens.get(i - 3);
+                COSNumber width = (COSNumber) tokens.get(i - 2);
+                COSNumber height = (COSNumber) tokens.get(i - 1);
                 PDRectangle potentialSmallest = new PDRectangle();
-                potentialSmallest.setLowerLeftX( x.floatValue() );
-                potentialSmallest.setLowerLeftY( y.floatValue() );
-                potentialSmallest.setUpperRightX( x.floatValue() + 
width.floatValue() );
-                potentialSmallest.setUpperRightY( y.floatValue() + 
height.floatValue() );
-                if( smallest == null ||
-                    smallest.getLowerLeftX() < 
potentialSmallest.getLowerLeftX() ||
-                    smallest.getUpperRightY() > 
potentialSmallest.getUpperRightY() )
+                potentialSmallest.setLowerLeftX(x.floatValue());
+                potentialSmallest.setLowerLeftY(y.floatValue());
+                potentialSmallest.setUpperRightX(x.floatValue() + 
width.floatValue());
+                potentialSmallest.setUpperRightY(y.floatValue() + 
height.floatValue());
+                if (smallest == null || smallest.getLowerLeftX() < 
potentialSmallest.getLowerLeftX()
+                        || smallest.getUpperRightY() > 
potentialSmallest.getUpperRightY())
                 {
                     smallest = potentialSmallest;
                 }
@@ -517,119 +834,171 @@ public class PDAppearance
     }
 
     /**
-     * My "not so great" method for calculating the fontsize.
-     * It does not work superb, but it handles ok.
+     * My "not so great" method for calculating the fontsize. It does not work 
superb, but it handles ok.
+     * 
      * @return the calculated font-size
      *
-     * @throws IOException If there is an error getting the font height.
+     * @throws IOException
+     *             If there is an error getting the font height.
      */
-    private float calculateFontSize( PDFont pdFont, PDRectangle boundingBox, 
List tokens, List daTokens )
-        throws IOException
+    private float calculateFontSize(PDFont pdFont, PDRectangle boundingBox, 
List tokens, List daTokens)
+            throws IOException
     {
         float fontSize = 0;
-        if( daTokens != null )
+        if (daTokens != null)
         {
-            //daString looks like   "BMC /Helv 3.4 Tf EMC"
+            // daString looks like "BMC /Helv 3.4 Tf EMC"
 
-            int fontIndex = daTokens.indexOf( PDFOperator.getOperator( "Tf" ) 
);
-            if(fontIndex != -1 )
+            int fontIndex = daTokens.indexOf(PDFOperator.getOperator("Tf"));
+            if (fontIndex != -1)
             {
-                fontSize = ((COSNumber)daTokens.get(fontIndex-1)).floatValue();
+                fontSize = ((COSNumber) daTokens.get(fontIndex - 
1)).floatValue();
             }
         }
-        
+
         float widthBasedFontSize = Float.MAX_VALUE;
-        
-        if( parent.doNotScroll() )
+
+        if (parent.doNotScroll())
         {
-            //if we don't scroll then we will shrink the font to fit into the 
text area.
-            float widthAtFontSize1 = pdFont.getStringWidth( value )/1000.f;
+            // if we don't scroll then we will shrink the font to fit into the 
text area.
+            float widthAtFontSize1 = pdFont.getStringWidth(value) / 1000.f;
             float availableWidth = getAvailableWidth(boundingBox, 
getLineWidth(tokens));
             widthBasedFontSize = availableWidth / widthAtFontSize1;
         }
-        else if( fontSize == 0 )
+        else if (fontSize == 0)
         {
-            float lineWidth = getLineWidth( tokens );
-            float stringWidth = pdFont.getStringWidth( value );
+            float lineWidth = getLineWidth(tokens);
+            float stringWidth = pdFont.getStringWidth(value);
             float height = 0;
-            if( pdFont instanceof PDSimpleFont )
+            if (pdFont instanceof PDSimpleFont)
             {
-                height = 
((PDSimpleFont)pdFont).getFontDescriptor().getFontBoundingBox().getHeight();
+                height = ((PDSimpleFont) 
pdFont).getFontDescriptor().getFontBoundingBox().getHeight();
             }
             else
             {
-                //now much we can do, so lets assume font is square and use 
width
-                //as the height
+                // now much we can do, so lets assume font is square and use 
width
+                // as the height
                 height = pdFont.getAverageFontWidth();
             }
-            height = height/1000f;
+            height = height / 1000f;
 
-            float availHeight = getAvailableHeight( boundingBox, lineWidth );
-            fontSize = Math.min((availHeight/height), widthBasedFontSize);
+            float availHeight = getAvailableHeight(boundingBox, lineWidth);
+            fontSize = Math.min((availHeight / height), widthBasedFontSize);
         }
         return fontSize;
     }
 
     /**
-     * Calculates where to start putting the text in the box.
-     * The positioning is not quite as accurate as when Acrobat
+     * Calculate the fontsize if autosizing was set.
+     *
+     * @return the calculated font-size
+     *
+     * @throws IOException
+     *             If there is an error getting the font height.
+     */
+    private float calculateListboxFontSize(PDFont pdFont, PDRectangle 
contentArea, List tokens, List daTokens)
+            throws IOException
+    {
+        float fontSize = 0;
+        if (daTokens != null)
+        {
+            // daString looks like "BMC /Helv 3.4 Tf EMC"
+
+            int fontIndex = daTokens.indexOf(PDFOperator.getOperator("Tf"));
+            if (fontIndex != -1)
+            {
+                fontSize = ((COSNumber) daTokens.get(fontIndex - 
1)).floatValue();
+            }
+        }
+
+        // font size of 0 means that the font size has to be calculated
+        // dependent on the strings length so that the string with the
+        // largest width fits into the box available.
+        if (fontSize == 0)
+        {
+
+            COSArray options = ((PDChoiceField) parent).getOptions();
+
+            float maxOptWidth = 0;
+
+            for (int i = 0; i < options.size(); i++)
+            {
+                
+                COSBase option = options.getObject(i);
+                COSArray optionPair = (COSArray) option;
+                COSString optionValue = (COSString) optionPair.getObject(1);
+                maxOptWidth = 
Math.max(pdFont.getStringWidth(optionValue.getString()) / 1000.f, maxOptWidth);
+            }
+
+            float availableWidth = getAvailableWidth(contentArea, 
getLineWidth(tokens));
+            fontSize = availableWidth / maxOptWidth;
+        }
+
+        return fontSize;
+    }
+
+    /**
+     * Calculates where to start putting the text in the box. The positioning 
is not quite as accurate as when Acrobat
      * places the elements, but it works though.
      *
      * @return the sting for representing the start position of the text
      *
-     * @throws IOException If there is an error calculating the text position.
+     * @throws IOException
+     *             If there is an error calculating the text position.
      */
-    private String getTextPosition( PDRectangle boundingBox, PDFont pdFont, 
float fontSize, List tokens )
-        throws IOException
+    private String getTextPosition(PDRectangle boundingBox, PDFont pdFont, 
float fontSize, List tokens)
+            throws IOException
     {
-        float lineWidth = getLineWidth( tokens );
+        float lineWidth = getLineWidth(tokens);
         float pos = 0.0f;
-        if(parent.isMultiline())
+        if (parent.isMultiline())
         {
-            int rows = (int) (getAvailableHeight( boundingBox, lineWidth ) / 
((int) fontSize));
-            pos = ((rows)*fontSize)-fontSize;
+            int rows = (int) (getAvailableHeight(boundingBox, lineWidth) / 
((int) fontSize));
+            pos = ((rows) * fontSize) - fontSize;
         }
         else
         {
-            if( pdFont instanceof PDSimpleFont )
+            if (pdFont instanceof PDSimpleFont)
             {
-                //BJL 9/25/2004
-                //This algorithm is a little bit of black magic.  It does
-                //not appear to be documented anywhere.  Through examining a 
few
-                //PDF documents and the value that Acrobat places in there I
-                //have determined that the below method of computing the 
position
-                //is correct for certain documents, but maybe not all.  It does
-                //work f1040ez.pdf and Form_1.pdf
-                PDFontDescriptor fd = 
((PDSimpleFont)pdFont).getFontDescriptor();
+                // BJL 9/25/2004
+                // This algorithm is a little bit of black magic. It does
+                // not appear to be documented anywhere. Through examining a 
few
+                // PDF documents and the value that Acrobat places in there I
+                // have determined that the below method of computing the 
position
+                // is correct for certain documents, but maybe not all. It does
+                // work f1040ez.pdf and Form_1.pdf
+                PDFontDescriptor fd = ((PDSimpleFont) 
pdFont).getFontDescriptor();
                 float bBoxHeight = boundingBox.getHeight();
                 float fontHeight = fd.getFontBoundingBox().getHeight() + 2 * 
fd.getDescent();
-                fontHeight = (fontHeight/1000) * fontSize;
-                pos = (bBoxHeight - fontHeight)/2;
+                fontHeight = (fontHeight / 1000) * fontSize;
+                pos = (bBoxHeight - fontHeight) / 2;
             }
             else
             {
-                throw new IOException( "Error: Don't know how to calculate the 
position for non-simple fonts" );
+                throw new IOException("Error: Don't know how to calculate the 
position for non-simple fonts");
             }
         }
-        PDRectangle innerBox = getSmallestDrawnRectangle( boundingBox, tokens 
);
-        float xInset = 2+ 2*(boundingBox.getWidth() - innerBox.getWidth());
-        return Math.round(xInset) + " "+ pos + " Td";
+        PDRectangle innerBox = getSmallestDrawnRectangle(boundingBox, tokens);
+        float xInset = 2 + 2 * (boundingBox.getWidth() - innerBox.getWidth());
+        return Math.round(xInset) + " " + pos + " Td";
     }
 
     /**
      * calculates the available width of the box.
+     * 
      * @return the calculated available width of the box
      */
-    private float getAvailableWidth( PDRectangle boundingBox, float lineWidth )
+    private float getAvailableWidth(PDRectangle boundingBox, float lineWidth)
     {
         return boundingBox.getWidth() - 2 * lineWidth;
     }
 
     /**
      * calculates the available height of the box.
+     * 
      * @return the calculated available height of the box
      */
-    private float getAvailableHeight( PDRectangle boundingBox, float lineWidth 
)
+    private float getAvailableHeight(PDRectangle boundingBox, float lineWidth)
     {
         return boundingBox.getHeight() - 2 * lineWidth;
     }

Modified: 
pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDChoiceField.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDChoiceField.java?rev=1623830&r1=1623829&r2=1623830&view=diff
==============================================================================
--- 
pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDChoiceField.java
 (original)
+++ 
pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDChoiceField.java
 Tue Sep  9 14:20:30 2014
@@ -43,86 +43,151 @@ public class PDChoiceField extends PDVar
      */
     public static final int FLAG_EDIT = 1 << 18;
 
+    private PDAppearance appearance;
+
     /**
      * @see 
org.apache.pdfbox.pdmodel.interactive.form.PDField#PDField(PDAcroForm,COSDictionary)
      *
-     * @param theAcroForm The acroForm for this field.
-     * @param field The field for this choice field.
+     * @param theAcroForm
+     *            The acroForm for this field.
+     * @param field
+     *            The field for this choice field.
      */
-    public PDChoiceField( PDAcroForm theAcroForm, COSDictionary field)
+    public PDChoiceField(PDAcroForm theAcroForm, COSDictionary field)
     {
         super(theAcroForm, field);
     }
 
+    private void setListboxValue(String value) throws IOException
+    {
+        COSString fieldValue = new COSString(value);
+        getDictionary().setItem(COSName.V, fieldValue);
+
+        // hmm, not sure what the case where the DV gets set to the field
+        // value, for now leave blank until we can come up with a case
+        // where it needs to be in there
+        // getDictionary().setItem( COSName.getPDFName( "DV" ), fieldValue );
+        if (appearance == null)
+        {
+            this.appearance = new PDAppearance(getAcroForm(), this);
+        }
+        appearance.setAppearanceValue(value);
+    }
+
     /**
      * @see 
org.apache.pdfbox.pdmodel.interactive.form.PDField#setValue(java.lang.String)
      *
-     * @param optionValue The new value for this text field.
+     * @param optionValue
+     *            The new value for this text field.
      *
-     * @throws IOException If there is an error calculating the appearance 
stream or the value in not one
-     *   of the existing options.
+     * @throws IOException
+     *             If there is an error calculating the appearance stream or 
the value in not one of the existing
+     *             options.
      */
     public void setValue(String optionValue) throws IOException
     {
         int indexSelected = -1;
-        COSArray options = (COSArray)getDictionary().getDictionaryObject( 
COSName.OPT );
+        COSArray options = (COSArray) 
getDictionary().getDictionaryObject(COSName.OPT);
         int fieldFlags = getFieldFlags();
         boolean isEditable = (FLAG_COMBO & fieldFlags) != 0 && (FLAG_EDIT & 
fieldFlags) != 0;
-        
-        if( options.size() == 0 && ! isEditable )
+
+        if (options.size() == 0 && !isEditable)
         {
-            throw new IOException( "Error: You cannot set a value for a choice 
field if there are no options." );
+            throw new IOException("Error: You cannot set a value for a choice 
field if there are no options.");
         }
         else
         {
             // YXJ: Changed the order of the loops. Acrobat produces PDF's
             // where sometimes there is 1 string and the rest arrays.
             // This code works either way.
-            for( int i=0; i<options.size() && indexSelected == -1; i++ ) 
+            for (int i = 0; i < options.size() && indexSelected == -1; i++)
             {
-                COSBase option = options.getObject( i );
-                if( option instanceof COSArray )
+                COSBase option = options.getObject(i);
+                if (option instanceof COSArray)
                 {
-                    COSArray keyValuePair = (COSArray)option;
-                    COSString key = (COSString)keyValuePair.getObject( 0 );
-                    COSString value = (COSString)keyValuePair.getObject( 1 );
-                    if( optionValue.equals( key.getString() ) || 
optionValue.equals( value.getString() ) )
+                    COSArray keyValuePair = (COSArray) option;
+                    COSString key = (COSString) keyValuePair.getObject(0);
+                    COSString value = (COSString) keyValuePair.getObject(1);
+                    if (optionValue.equals(key.getString()) || 
optionValue.equals(value.getString()))
                     {
-                        //have the parent draw the appearance stream with the 
value
-                        super.setValue( value.getString() );
-                        //but then use the key as the V entry
-                        getDictionary().setItem( COSName.V, key );
+                        // have the parent draw the appearance stream with the 
value
+                        if ((FLAG_COMBO & fieldFlags) != 0)
+                        {
+                            super.setValue(value.getString());
+                        }
+                        else
+                        {
+                            COSArray indexEntries = new COSArray();
+                            indexEntries.add(COSInteger.get((long) i));
+                            getDictionary().setItem(COSName.I, indexEntries);
+                            setListboxValue(value.getString());
+                        }
+                        // but then use the key as the V entry
+                        getDictionary().setItem(COSName.V, key);
                         indexSelected = i;
+
                     }
                 }
                 else
                 {
-                    COSString value = (COSString)option;
-                    if( optionValue.equals( value.getString() ) )
+                    COSString value = (COSString) option;
+                    if (optionValue.equals(value.getString()))
                     {
-                        super.setValue( optionValue );
+                        super.setValue(optionValue);
                         indexSelected = i;
                     }
                 }
             }
         }
-        if( indexSelected == -1 && isEditable ) 
+        if (indexSelected == -1 && isEditable)
         {
-            super.setValue( optionValue );
+            super.setValue(optionValue);
         }
-        else if( indexSelected == -1 )
+        else if (indexSelected == -1)
         {
-            throw new IOException( "Error: '" + optionValue + "' was not an 
available option.");
+            throw new IOException("Error: '" + optionValue + "' was not an 
available option.");
         }
         else
         {
-            COSArray indexArray = 
(COSArray)getDictionary().getDictionaryObject( COSName.I );
-            if( indexArray != null )
+            COSArray indexArray = (COSArray) 
getDictionary().getDictionaryObject(COSName.I);
+            if (indexArray != null)
             {
                 indexArray.clear();
-                indexArray.add( COSInteger.get( indexSelected ) );
+                indexArray.add(COSInteger.get(indexSelected));
             }
         }
     }
 
+    /**
+     * This will get the indices of the selected options "I".
+     * 
+     * @return COSArray containing the indices of all selected options.
+     */
+    public COSArray getSelectedOptions()
+    {
+        return (COSArray) getDictionary().getDictionaryObject(COSName.I);
+    }
+
+    /**
+     * This will get the top index "TI" value.
+     * 
+     * The value returned will be the first item to display in the listbox.
+     * 
+     * @return the top index, default value 0.
+     */
+    public int getTopIndex()
+    {
+        return getDictionary().getInt(COSName.getPDFName("TI"), 0);
+    }
+
+    /**
+     * This will get the option values "Opt".
+     *
+     * @return COSArray containing all options.
+     */
+    public COSArray getOptions()
+    {
+        return (COSArray) getDictionary().getDictionaryObject(COSName.OPT);
+    }
+
 }


Reply via email to