aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAppearance.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/pdfbox/pdmodel/interactive/form/PDAppearance.java')
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDAppearance.java645
1 files changed, 0 insertions, 645 deletions
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAppearance.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAppearance.java
deleted file mode 100644
index ff18c86..0000000
--- a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAppearance.java
+++ /dev/null
@@ -1,645 +0,0 @@
-/**
- * Copyright (c) 2003-2004, www.pdfbox.org
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. Neither the name of pdfbox; nor the names of its
- * contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * http://www.pdfbox.org
- *
- */
-package org.pdfbox.pdmodel.interactive.form;
-
-import java.io.ByteArrayInputStream;
-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.Map;
-
-import org.pdfbox.cos.COSArray;
-import org.pdfbox.cos.COSDictionary;
-import org.pdfbox.cos.COSFloat;
-import org.pdfbox.cos.COSName;
-import org.pdfbox.cos.COSNumber;
-import org.pdfbox.cos.COSStream;
-import org.pdfbox.cos.COSString;
-
-import org.pdfbox.pdfparser.PDFStreamParser;
-import org.pdfbox.pdfwriter.ContentStreamWriter;
-
-import org.pdfbox.pdmodel.PDResources;
-
-import org.pdfbox.pdmodel.common.PDRectangle;
-
-import org.pdfbox.pdmodel.font.PDFont;
-import org.pdfbox.pdmodel.font.PDFontDescriptor;
-import org.pdfbox.pdmodel.font.PDSimpleFont;
-
-import org.pdfbox.pdmodel.interactive.action.PDAdditionalActions;
-import org.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
-import org.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
-import org.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
-
-import org.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.pdfbox.examlpes.fdf.ImportFDF. So Ben, here is the
- * solution.
- *
- * @author sug
- * @author Ben Litchfield (ben@benlitchfield.com)
- * @version $Revision: 1.17 $
- */
-public class PDAppearance
-{
- private PDVariableText parent;
-
- private String value;
- private COSString defaultAppearance;
-
- private PDAcroForm acroForm;
- private List widgets = new ArrayList();
-
-
- /**
- * 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.
- */
- public PDAppearance( PDAcroForm theAcroForm, PDVariableText field ) throws IOException
- {
- acroForm = theAcroForm;
- parent = field;
-
- widgets = field.getKids();
- if( widgets == null )
- {
- widgets = new ArrayList();
- 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.
- * @return The DA element
- */
- private COSString getDefaultAppearance()
- {
-
- COSString dap = parent.getDefaultAppearance();
- if (dap == null)
- {
- COSArray kids = (COSArray)parent.getDictionary().getDictionaryObject( "Kids" );
- if( kids != null && kids.size() > 0 )
- {
- COSDictionary firstKid = (COSDictionary)kids.getObject( 0 );
- dap = (COSString)firstKid.getDictionaryObject( "DA" );
- }
- if( dap == null )
- {
- dap = (COSString) acroForm.getDictionary().getDictionaryObject(COSName.getPDFName("DA"));
- }
- }
- return dap;
- }
-
- private int getQ()
- {
- int q = parent.getQ();
- if( parent.getDictionary().getDictionaryObject( "Q" ) == null )
- {
- COSArray kids = (COSArray)parent.getDictionary().getDictionaryObject( "Kids" );
- if( kids != null && kids.size() > 0 )
- {
- COSDictionary firstKid = (COSDictionary)kids.getObject( 0 );
- COSNumber qNum = (COSNumber)firstKid.getDictionaryObject( "Q" );
- if( qNum != null )
- {
- q = qNum.intValue();
- }
- }
- }
- return q;
- }
-
- /**
- * Extracts the original appearance stream into a list of tokens.
- *
- * @return The tokens in the original appearance stream
- */
- private List getStreamTokens( PDAppearanceStream appearanceStream ) throws IOException
- {
- List tokens = null;
- if( appearanceStream != null )
- {
- tokens = getStreamTokens( appearanceStream.getStream() );
- }
- return tokens;
- }
-
- private List getStreamTokens( COSString string ) throws IOException
- {
- PDFStreamParser parser;
-
- List tokens = null;
- if( string != null )
- {
- 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
- {
- PDFStreamParser parser;
-
- List tokens = null;
- if( stream != null )
- {
- parser = new PDFStreamParser( stream );
- parser.parse();
- tokens = parser.getTokens();
- }
- return tokens;
- }
-
- /**
- * Tests if the apperance stream already contains content.
- *
- * @return true if it contains any content
- */
- private boolean containsMarkedContent( List stream )
- {
- return stream.contains( PDFOperator.getOperator( "BMC" ) );
- }
-
- /**
- * This is the public method for setting the appearance stream.
- *
- * @param apValue the String value which the apperance shoud represent
- *
- * @throws IOException If there is an error creating the stream.
- */
- public void setAppearanceValue(String apValue) throws IOException
- {
- // MulitLine check and set
- if ( parent.isMultiline() && apValue.indexOf('\n') != -1 )
- {
- apValue = convertToMultiLine( apValue );
- }
-
- value = apValue;
- Iterator widgetIter = widgets.iterator();
- while( widgetIter.hasNext() )
- {
- Object next = widgetIter.next();
- PDAnnotationWidget widget = null;
- if( next instanceof PDField )
- {
- widget = ((PDField)next).getWidget();
- }
- else
- {
- widget = (PDAnnotationWidget)next;
- }
- PDAdditionalActions actions = widget.getActions();
- if( actions != null &&
- actions.getF() != null &&
- widget.getDictionary().getDictionaryObject( "AP" ) ==null)
- {
- //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 )
- {
- appearance = new PDAppearanceDictionary();
- widget.setAppearance( appearance );
- }
-
- Map normalAppearance = appearance.getNormalAppearance();
- PDAppearanceStream appearanceStream = (PDAppearanceStream)normalAppearance.get( "default" );
- if( appearanceStream == null )
- {
- COSStream cosStream = new COSStream( acroForm.getDocument().getDocument().getScratchFile() );
- 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 ))
- {
- 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() );
- insertGeneratedAppearance( widget, output, pdFont, tokens, appearanceStream );
- output.write( " EMC".getBytes() );
- 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 )
- {
- //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++ )
- {
- if( tokens.get( i ) instanceof COSString )
- {
- foundString = true;
- COSString drawnString =((COSString)tokens.get(i));
- drawnString.reset();
- drawnString.append( apValue.getBytes() );
- }
- }
- 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 )
- {
- writer.writeTokens( tokens, 0, bmcIndex+1 );
- }
- else
- {
- writer.writeTokens( tokens );
- }
- output.write( "\n".getBytes() );
- insertGeneratedAppearance( widget, 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
- {
- PrintWriter printWriter = new PrintWriter( output, true );
- float fontSize = 0.0f;
- PDRectangle boundingBox = null;
- boundingBox = appearanceStream.getBoundingBox();
- if( boundingBox == null )
- {
- boundingBox = fieldWidget.getRectangle().createRetranslatedRectangle();
- }
- printWriter.println( "BT" );
- if( defaultAppearance != null )
- {
- String daString = defaultAppearance.getString();
- PDFStreamParser daParser = new PDFStreamParser(new ByteArrayInputStream( daString.getBytes() ), null );
- daParser.parse();
- List daTokens = daParser.getTokens();
- fontSize = calculateFontSize( pdFont, boundingBox, tokens, daTokens );
- int fontIndex = daTokens.indexOf( PDFOperator.getOperator( "Tf" ) );
- if(fontIndex != -1 )
- {
- daTokens.set( fontIndex-1, new COSFloat( fontSize ) );
- }
- ContentStreamWriter daWriter = new ContentStreamWriter(output);
- daWriter.writeTokens( daTokens );
- }
- printWriter.println( getTextPosition( boundingBox, pdFont, fontSize, tokens ) );
- 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 );
- }
- printWriter.println("(" + value + ") Tj");
- printWriter.println("ET" );
- 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( streamResources == null )
- {
- streamResources = new PDResources();
- appearanceStream.setResources( streamResources );
- }
-
- COSString da = getDefaultAppearance();
- if( da != null )
- {
- String data = da.getString();
- PDFStreamParser streamParser = new PDFStreamParser(
- new ByteArrayInputStream( data.getBytes() ), null );
- streamParser.parse();
- tokens = streamParser.getTokens();
- }
-
- 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)formResources.getFonts().get( fontName );
- streamResources.getFonts().put( fontName, retval );
- }
- }
- return retval;
- }
-
- private String convertToMultiLine( String line )
- {
- int currIdx = 0;
- int lastIdx = 0;
- StringBuffer result = new StringBuffer(line.length() + 64);
- while( (currIdx = line.indexOf('\n',lastIdx )) > -1 )
- {
- result.append(value.substring(lastIdx,currIdx));
- result.append(" ) Tj\n0 -13 Td\n(");
- lastIdx = currIdx + 1;
- }
- result.append(line.substring(lastIdx));
- return result.toString();
- }
-
- /**
- * Writes the stream to the actual stream in the COSStream.
- *
- * @throws IOException If there is an error writing to the stream
- */
- private void writeToStream( byte[] data, PDAppearanceStream appearanceStream ) throws IOException
- {
- OutputStream out = appearanceStream.getStream().createUnfilteredStream();
- out.write( data );
- out.flush();
- }
-
-
- /**
- * w in an appearance stream represents the lineWidth.
- * @return the linewidth
- */
- private float getLineWidth( List tokens )
- {
-
- float retval = 1;
- 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) )
- {
- retval = ((COSNumber)tokens.get(wIndex-1)).floatValue();
- }
- }
- return retval;
- }
-
- private PDRectangle getSmallestDrawnRectangle( PDRectangle boundingBox, List tokens )
- {
- PDRectangle smallest = boundingBox;
- for( int i=0; i<tokens.size(); i++ )
- {
- 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 );
- 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() )
- {
- smallest = potentialSmallest;
- }
-
- }
- }
- return smallest;
- }
-
- /**
- * 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.
- */
- private float calculateFontSize( PDFont pdFont, PDRectangle boundingBox, 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();
- }
- }
- 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 );
- float availableWidth = boundingBox.getWidth();
- float perfectFitFontSize = availableWidth / widthAtFontSize1;
- }
- else if( fontSize == 0 )
- {
- float lineWidth = getLineWidth( tokens );
- float stringWidth = pdFont.getStringWidth( value );
- float height = 0;
- if( pdFont instanceof PDSimpleFont )
- {
- height = ((PDSimpleFont)pdFont).getFontDescriptor().getFontBoundingBox().getHeight();
- }
- else
- {
- //now much we can do, so lets assume font is square and use width
- //as the height
- height = pdFont.getAverageFontWidth();
- }
- height = height/1000f;
-
- float availHeight = getAvailableHeight( boundingBox, lineWidth );
- fontSize =(availHeight/height);
- }
- 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.
- */
- private String getTextPosition( PDRectangle boundingBox, PDFont pdFont, float fontSize, List tokens )
- throws IOException
- {
- float lineWidth = getLineWidth( tokens );
- float pos = 0.0f;
- if(parent.isMultiline())
- {
- int rows = (int) (getAvailableHeight( boundingBox, lineWidth ) / ((int) fontSize));
- pos = ((rows)*fontSize)-fontSize;
- }
- else
- {
- 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();
- float bBoxHeight = boundingBox.getHeight();
- float fontHeight = fd.getFontBoundingBox().getHeight() + 2 * fd.getDescent();
- 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" );
- }
- }
- 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 )
- {
- 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 )
- {
- return boundingBox.getHeight() - 2 * lineWidth;
- }
-} \ No newline at end of file