Home » pdfbox-1.1.0-src » org.apache.pdfbox.pdfwriter » [javadoc | source]

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   package org.apache.pdfbox.pdfwriter;
   18   
   19   import java.io.IOException;
   20   import java.io.InputStream;
   21   import java.io.OutputStream;
   22   import java.security.MessageDigest;
   23   import java.security.NoSuchAlgorithmException;
   24   import java.text.DecimalFormat;
   25   import java.text.NumberFormat;
   26   import java.util.ArrayList;
   27   import java.util.Collections;
   28   import java.util.HashSet;
   29   import java.util.Hashtable;
   30   import java.util.Iterator;
   31   import java.util.List;
   32   import java.util.Locale;
   33   import java.util.Map;
   34   import java.util.Set;
   35   
   36   import org.apache.pdfbox.cos.COSArray;
   37   import org.apache.pdfbox.cos.COSBase;
   38   import org.apache.pdfbox.cos.COSBoolean;
   39   import org.apache.pdfbox.cos.COSDictionary;
   40   import org.apache.pdfbox.cos.COSDocument;
   41   import org.apache.pdfbox.cos.COSFloat;
   42   import org.apache.pdfbox.cos.COSInteger;
   43   import org.apache.pdfbox.cos.COSName;
   44   import org.apache.pdfbox.cos.COSNull;
   45   import org.apache.pdfbox.cos.COSObject;
   46   import org.apache.pdfbox.cos.COSStream;
   47   import org.apache.pdfbox.cos.COSString;
   48   import org.apache.pdfbox.cos.ICOSVisitor;
   49   import org.apache.pdfbox.exceptions.COSVisitorException;
   50   import org.apache.pdfbox.exceptions.CryptographyException;
   51   import org.apache.pdfbox.pdmodel.PDDocument;
   52   import org.apache.pdfbox.pdmodel.encryption.SecurityHandler;
   53   import org.apache.pdfbox.persistence.util.COSObjectKey;
   54   
   55   /**
   56    * this class acts on a in-memory representation of a pdf document.
   57    *
   58    * todo no support for incremental updates
   59    * todo single xref section only
   60    * todo no linearization
   61    *
   62    * @author Michael Traut
   63    * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
   64    * @version $Revision: 1.36 $
   65    */
   66   public class COSWriter implements ICOSVisitor
   67   {
   68       /**
   69        * The dictionary open token.
   70        */
   71       public static final byte[] DICT_OPEN = "<<".getBytes();
   72       /**
   73        * The dictionary close token.
   74        */
   75       public static final byte[] DICT_CLOSE = ">>".getBytes();
   76       /**
   77        * space character.
   78        */
   79       public static final byte[] SPACE = " ".getBytes();
   80       /**
   81        * The start to a PDF comment.
   82        */
   83       public static final byte[] COMMENT = "%".getBytes();
   84   
   85       /**
   86        * The output version of the PDF.
   87        */
   88       public static final byte[] VERSION = "PDF-1.4".getBytes();
   89       /**
   90        * Garbage bytes used to create the PDF header.
   91        */
   92       public static final byte[] GARBAGE = new byte[] {(byte)0xf6, (byte)0xe4, (byte)0xfc, (byte)0xdf};
   93       /**
   94        * The EOF constant.
   95        */
   96       public static final byte[] EOF = "%%EOF".getBytes();
   97       // pdf tokens
   98   
   99       /**
  100        * The reference token.
  101        */
  102       public static final byte[] REFERENCE = "R".getBytes();
  103       /**
  104        * The XREF token.
  105        */
  106       public static final byte[] XREF = "xref".getBytes();
  107       /**
  108        * The xref free token.
  109        */
  110       public static final byte[] XREF_FREE = "f".getBytes();
  111       /**
  112        * The xref used token.
  113        */
  114       public static final byte[] XREF_USED = "n".getBytes();
  115       /**
  116        * The trailer token.
  117        */
  118       public static final byte[] TRAILER = "trailer".getBytes();
  119       /**
  120        * The start xref token.
  121        */
  122       public static final byte[] STARTXREF = "startxref".getBytes();
  123       /**
  124        * The starting object token.
  125        */
  126       public static final byte[] OBJ = "obj".getBytes();
  127       /**
  128        * The end object token.
  129        */
  130       public static final byte[] ENDOBJ = "endobj".getBytes();
  131       /**
  132        * The array open token.
  133        */
  134       public static final byte[] ARRAY_OPEN = "[".getBytes();
  135       /**
  136        * The array close token.
  137        */
  138       public static final byte[] ARRAY_CLOSE = "]".getBytes();
  139       /**
  140        * The open stream token.
  141        */
  142       public static final byte[] STREAM = "stream".getBytes();
  143       /**
  144        * The close stream token.
  145        */
  146       public static final byte[] ENDSTREAM = "endstream".getBytes();
  147   
  148       private NumberFormat formatXrefOffset = new DecimalFormat("0000000000");
  149       /**
  150        * The decimal format for the xref object generation number data.
  151        */
  152       private NumberFormat formatXrefGeneration = new DecimalFormat("00000");
  153   
  154       private NumberFormat formatDecimal = NumberFormat.getNumberInstance( Locale.US );
  155   
  156       // the stream where we create the pdf output
  157       private OutputStream output;
  158       // the stream used to write standard cos data
  159       private COSStandardOutputStream standardOutput;
  160   
  161       // the start position of the x ref section
  162       private long startxref = 0;
  163   
  164       // the current object number
  165       private long number = 0;
  166   
  167       // maps the object to the keys generated in the writer
  168       // these are used for indirect refrences in other objects
  169       //A hashtable is used on purpose over a hashmap
  170       //so that null entries will not get added.
  171       private Map objectKeys = new Hashtable();
  172   
  173       // the list of x ref entries to be made so far
  174       private List xRefEntries = new ArrayList();
  175   
  176       //A list of objects to write.
  177       private List objectsToWrite = new ArrayList();
  178   
  179       //a list of objects already written
  180       private Set writtenObjects = new HashSet();
  181       //An 'actual' is any COSBase that is not a COSObject.
  182       //need to keep a list of the actuals that are added
  183       //as well as the objects because there is a problem
  184       //when adding a COSObject and then later adding
  185       //the actual for that object, so we will track
  186       //actuals separately.
  187       private Set actualsAdded = new HashSet();
  188   
  189       private COSObjectKey currentObjectKey = null;
  190   
  191       private PDDocument document = null;
  192   
  193       private boolean willEncrypt = false;
  194   
  195       /**
  196        * COSWriter constructor comment.
  197        *
  198        * @param os The wrapped output stream.
  199        */
  200       public COSWriter(OutputStream os)
  201       {
  202           super();
  203           setOutput(os);
  204           setStandardOutput(new COSStandardOutputStream(getOutput()));
  205           formatDecimal.setMaximumFractionDigits( 10 );
  206           formatDecimal.setGroupingUsed( false );
  207       }
  208       /**
  209        * add an entry in the x ref table for later dump.
  210        *
  211        * @param entry The new entry to add.
  212        */
  213       protected void addXRefEntry(COSWriterXRefEntry entry)
  214       {
  215           getXRefEntries().add(entry);
  216       }
  217   
  218       /**
  219        * This will close the stream.
  220        *
  221        * @throws IOException If the underlying stream throws an exception.
  222        */
  223       public void close() throws IOException
  224       {
  225           if (getStandardOutput() != null)
  226           {
  227               getStandardOutput().close();
  228           }
  229           if (getOutput() != null)
  230           {
  231               getOutput().close();
  232           }
  233       }
  234   
  235       /**
  236        * This will get the current object number.
  237        *
  238        * @return The current object number.
  239        */
  240       protected long getNumber()
  241       {
  242           return number;
  243       }
  244   
  245       /**
  246        * This will get all available object keys.
  247        *
  248        * @return A map of all object keys.
  249        */
  250       public java.util.Map getObjectKeys()
  251       {
  252           return objectKeys;
  253       }
  254   
  255       /**
  256        * This will get the output stream.
  257        *
  258        * @return The output stream.
  259        */
  260       protected java.io.OutputStream getOutput()
  261       {
  262           return output;
  263       }
  264   
  265       /**
  266        * This will get the standard output stream.
  267        *
  268        * @return The standard output stream.
  269        */
  270       protected COSStandardOutputStream getStandardOutput()
  271       {
  272           return standardOutput;
  273       }
  274   
  275       /**
  276        * This will get the current start xref.
  277        *
  278        * @return The current start xref.
  279        */
  280       protected long getStartxref()
  281       {
  282           return startxref;
  283       }
  284       /**
  285        * This will get the xref entries.
  286        *
  287        * @return All available xref entries.
  288        */
  289       protected java.util.List getXRefEntries()
  290       {
  291           return xRefEntries;
  292       }
  293   
  294       /**
  295        * This will set the current object number.
  296        *
  297        * @param newNumber The new object number.
  298        */
  299       protected void setNumber(long newNumber)
  300       {
  301           number = newNumber;
  302       }
  303   
  304       /**
  305        * This will set the output stream.
  306        *
  307        * @param newOutput The new output stream.
  308        */
  309       private void setOutput( OutputStream newOutput )
  310       {
  311           output = newOutput;
  312       }
  313   
  314       /**
  315        * This will set the standard output stream.
  316        *
  317        * @param newStandardOutput The new standard output stream.
  318        */
  319       private void setStandardOutput(COSStandardOutputStream newStandardOutput)
  320       {
  321           standardOutput = newStandardOutput;
  322       }
  323   
  324       /**
  325        * This will set the start xref.
  326        *
  327        * @param newStartxref The new start xref attribute.
  328        */
  329       protected void setStartxref(long newStartxref)
  330       {
  331           startxref = newStartxref;
  332       }
  333   
  334       /**
  335        * This will write the body of the document.
  336        *
  337        * @param doc The document to write the body for.
  338        *
  339        * @throws IOException If there is an error writing the data.
  340        * @throws COSVisitorException If there is an error generating the data.
  341        */
  342       protected void doWriteBody(COSDocument doc) throws IOException, COSVisitorException
  343       {
  344           COSDictionary trailer = doc.getTrailer();
  345           COSDictionary root = (COSDictionary)trailer.getDictionaryObject( COSName.ROOT );
  346           COSDictionary info = (COSDictionary)trailer.getDictionaryObject( COSName.INFO );
  347           COSDictionary encrypt = (COSDictionary)trailer.getDictionaryObject( COSName.ENCRYPT );
  348           if( root != null )
  349           {
  350               addObjectToWrite( root );
  351           }
  352           if( info != null )
  353           {
  354               addObjectToWrite( info );
  355           }
  356   
  357   
  358           while( objectsToWrite.size() > 0 )
  359           {
  360               COSBase nextObject = (COSBase)objectsToWrite.remove( 0 );
  361               doWriteObject( nextObject );
  362           }
  363   
  364   
  365           willEncrypt = false;
  366   
  367           if( encrypt != null )
  368           {
  369               addObjectToWrite( encrypt );
  370           }
  371   
  372           while( objectsToWrite.size() > 0 )
  373           {
  374               COSBase nextObject = (COSBase)objectsToWrite.remove( 0 );
  375               doWriteObject( nextObject );
  376           }
  377   
  378           // write all objects
  379           /**
  380           for (Iterator i = doc.getObjects().iterator(); i.hasNext();)
  381           {
  382               COSObject obj = (COSObject) i.next();
  383               doWriteObject(obj);
  384           }**/
  385       }
  386   
  387       private void addObjectToWrite( COSBase object )
  388       {
  389           COSBase actual = object;
  390           if( actual instanceof COSObject )
  391           {
  392               actual = ((COSObject)actual).getObject();
  393           }
  394   
  395           if( !writtenObjects.contains( object ) &&
  396               !objectsToWrite.contains( object ) &&
  397               !actualsAdded.contains( actual ) )
  398           {
  399               objectsToWrite.add( object );
  400               if( actual != null )
  401               {
  402                   actualsAdded.add( actual );
  403               }
  404           }
  405       }
  406   
  407       /**
  408        * This will write a COS object.
  409        *
  410        * @param obj The object to write.
  411        *
  412        * @throws COSVisitorException If there is an error visiting objects.
  413        */
  414       public void doWriteObject( COSBase obj ) throws COSVisitorException
  415       {
  416           try
  417           {
  418               writtenObjects.add( obj );
  419               // find the physical reference
  420               currentObjectKey = getObjectKey( obj );
  421               // add a x ref entry
  422               addXRefEntry( new COSWriterXRefEntry(getStandardOutput().getPos(), obj, currentObjectKey));
  423               // write the object
  424               getStandardOutput().write(String.valueOf(currentObjectKey.getNumber()).getBytes());
  425               getStandardOutput().write(SPACE);
  426               getStandardOutput().write(String.valueOf(currentObjectKey.getGeneration()).getBytes());
  427               getStandardOutput().write(SPACE);
  428               getStandardOutput().write(OBJ);
  429               getStandardOutput().writeEOL();
  430               obj.accept( this );
  431               getStandardOutput().writeEOL();
  432               getStandardOutput().write(ENDOBJ);
  433               getStandardOutput().writeEOL();
  434           }
  435           catch (IOException e)
  436           {
  437               throw new COSVisitorException(e);
  438           }
  439       }
  440   
  441       /**
  442        * This will write the header to the PDF document.
  443        *
  444        * @param doc The document to get the data from.
  445        *
  446        * @throws IOException If there is an error writing to the stream.
  447        */
  448       protected void doWriteHeader(COSDocument doc) throws IOException
  449       {
  450           getStandardOutput().write( doc.getHeaderString().getBytes() );
  451           getStandardOutput().writeEOL();
  452           getStandardOutput().write(COMMENT);
  453           getStandardOutput().write(GARBAGE);
  454           getStandardOutput().writeEOL();
  455       }
  456   
  457   
  458       /**
  459        * This will write the trailer to the PDF document.
  460        *
  461        * @param doc The document to create the trailer for.
  462        *
  463        * @throws IOException If there is an IOError while writing the document.
  464        * @throws COSVisitorException If there is an error while generating the data.
  465        */
  466       protected void doWriteTrailer(COSDocument doc) throws IOException, COSVisitorException
  467       {
  468           getStandardOutput().write(TRAILER);
  469           getStandardOutput().writeEOL();
  470   
  471           COSDictionary trailer = doc.getTrailer();
  472           //sort xref, needed only if object keys not regenerated
  473           Collections.sort(getXRefEntries());
  474           COSWriterXRefEntry lastEntry = (COSWriterXRefEntry)getXRefEntries().get( getXRefEntries().size()-1);
  475           trailer.setInt(COSName.SIZE, (int)lastEntry.getKey().getNumber()+1);
  476           trailer.removeItem( COSName.PREV );
  477           /**
  478           COSObject catalog = doc.getCatalog();
  479           if (catalog != null)
  480           {
  481               trailer.setItem(COSName.getPDFName("Root"), catalog);
  482           }
  483           */
  484           trailer.accept(this);
  485   
  486           getStandardOutput().write(STARTXREF);
  487           getStandardOutput().writeEOL();
  488           getStandardOutput().write(String.valueOf(getStartxref()).getBytes());
  489           getStandardOutput().writeEOL();
  490           getStandardOutput().write(EOF);
  491       }
  492   
  493       /**
  494        * write the x ref section for the pdf file
  495        *
  496        * currently, the pdf is reconstructed from the scratch, so we write a single section
  497        *
  498        * todo support for incremental writing?
  499        *
  500        * @param doc The document to write the xref from.
  501        *
  502        * @throws IOException If there is an error writing the data to the stream.
  503        */
  504       protected void doWriteXRef(COSDocument doc) throws IOException
  505       {
  506           String offset;
  507           String generation;
  508   
  509           // sort xref, needed only if object keys not regenerated
  510           Collections.sort(getXRefEntries());
  511           COSWriterXRefEntry lastEntry = (COSWriterXRefEntry)getXRefEntries().get( getXRefEntries().size()-1 );
  512   
  513           // remember the position where x ref is written
  514           setStartxref(getStandardOutput().getPos());
  515           //
  516           getStandardOutput().write(XREF);
  517           getStandardOutput().writeEOL();
  518           // write start object number and object count for this x ref section
  519           // we assume starting from scratch
  520           getStandardOutput().write(String.valueOf(0).getBytes());
  521           getStandardOutput().write(SPACE);
  522           getStandardOutput().write(String.valueOf(lastEntry.getKey().getNumber() + 1).getBytes());
  523           getStandardOutput().writeEOL();
  524           // write initial start object with ref to first deleted object and magic generation number
  525           offset = formatXrefOffset.format(0);
  526           generation = formatXrefGeneration.format(65535);
  527           getStandardOutput().write(offset.getBytes());
  528           getStandardOutput().write(SPACE);
  529           getStandardOutput().write(generation.getBytes());
  530           getStandardOutput().write(SPACE);
  531           getStandardOutput().write(XREF_FREE);
  532           getStandardOutput().writeCRLF();
  533           // write entry for every object
  534           long lastObjectNumber = 0;
  535           for (Iterator i = getXRefEntries().iterator(); i.hasNext();)
  536           {
  537               COSWriterXRefEntry entry = (COSWriterXRefEntry) i.next();
  538               while( lastObjectNumber<entry.getKey().getNumber()-1 )
  539               {
  540                   offset = formatXrefOffset.format(0);
  541                   generation = formatXrefGeneration.format(65535);
  542                   getStandardOutput().write(offset.getBytes());
  543                   getStandardOutput().write(SPACE);
  544                   getStandardOutput().write(generation.getBytes());
  545                   getStandardOutput().write(SPACE);
  546                   getStandardOutput().write(XREF_FREE);
  547                   getStandardOutput().writeCRLF();
  548                   lastObjectNumber++;
  549               }
  550               lastObjectNumber = entry.getKey().getNumber();
  551               offset = formatXrefOffset.format(entry.getOffset());
  552               generation = formatXrefGeneration.format(entry.getKey().getGeneration());
  553               getStandardOutput().write(offset.getBytes());
  554               getStandardOutput().write(SPACE);
  555               getStandardOutput().write(generation.getBytes());
  556               getStandardOutput().write(SPACE);
  557               getStandardOutput().write(entry.isFree() ? XREF_FREE : XREF_USED);
  558               getStandardOutput().writeCRLF();
  559           }
  560       }
  561   
  562       /**
  563        * This will get the object key for the object.
  564        *
  565        * @param obj The object to get the key for.
  566        *
  567        * @return The object key for the object.
  568        */
  569       private COSObjectKey getObjectKey( COSBase obj )
  570       {
  571           COSBase actual = obj;
  572           if( actual instanceof COSObject )
  573           {
  574               actual = ((COSObject)obj).getObject();
  575           }
  576           COSObjectKey key = null;
  577           if( actual != null )
  578           {
  579               key = (COSObjectKey)objectKeys.get(actual);
  580           }
  581           if( key == null )
  582           {
  583               key = (COSObjectKey)objectKeys.get(obj);
  584           }
  585           if (key == null)
  586           {
  587               setNumber(getNumber()+1);
  588               key = new COSObjectKey(getNumber(),0);
  589               objectKeys.put(obj, key);
  590               if( actual != null )
  591               {
  592                   objectKeys.put(actual, key);
  593               }
  594           }
  595           return key;
  596       }
  597   
  598       /**
  599        * visitFromArray method comment.
  600        *
  601        * @param obj The object that is being visited.
  602        *
  603        * @throws COSVisitorException If there is an exception while visiting this object.
  604        *
  605        * @return null
  606        */
  607       public Object visitFromArray( COSArray obj ) throws COSVisitorException
  608       {
  609           try
  610           {
  611               int count = 0;
  612               getStandardOutput().write(ARRAY_OPEN);
  613               for (Iterator i = obj.iterator(); i.hasNext();)
  614               {
  615                   COSBase current = (COSBase) i.next();
  616                   if( current instanceof COSDictionary )
  617                   {
  618                       addObjectToWrite( current );
  619                       writeReference( current );
  620                   }
  621                   else if( current instanceof COSObject )
  622                   {
  623                       COSBase subValue = ((COSObject)current).getObject();
  624                       if( subValue instanceof COSDictionary || subValue == null )
  625                       {
  626                           addObjectToWrite( current );
  627                           writeReference( current );
  628                       }
  629                       else
  630                       {
  631                           subValue.accept( this );
  632                       }
  633                   }
  634                   else if( current == null )
  635                   {
  636                       COSNull.NULL.accept( this );
  637                   }
  638                   else
  639                   {
  640                       current.accept(this);
  641                   }
  642                   count++;
  643                   if (i.hasNext())
  644                   {
  645                       if (count % 10 == 0)
  646                       {
  647                           getStandardOutput().writeEOL();
  648                       }
  649                       else
  650                       {
  651                           getStandardOutput().write(SPACE);
  652                       }
  653                   }
  654               }
  655               getStandardOutput().write(ARRAY_CLOSE);
  656               getStandardOutput().writeEOL();
  657               return null;
  658           }
  659           catch (IOException e)
  660           {
  661               throw new COSVisitorException(e);
  662           }
  663       }
  664   
  665       /**
  666        * visitFromBoolean method comment.
  667        *
  668        * @param obj The object that is being visited.
  669        *
  670        * @throws COSVisitorException If there is an exception while visiting this object.
  671        *
  672        * @return null
  673        */
  674       public Object visitFromBoolean(COSBoolean obj) throws COSVisitorException
  675       {
  676   
  677           try
  678           {
  679               obj.writePDF( getStandardOutput() );
  680               return null;
  681           }
  682           catch (IOException e)
  683           {
  684               throw new COSVisitorException(e);
  685           }
  686       }
  687   
  688       /**
  689        * visitFromDictionary method comment.
  690        *
  691        * @param obj The object that is being visited.
  692        *
  693        * @throws COSVisitorException If there is an exception while visiting this object.
  694        *
  695        * @return null
  696        */
  697       public Object visitFromDictionary(COSDictionary obj) throws COSVisitorException
  698       {
  699           try
  700           {
  701               getStandardOutput().write(DICT_OPEN);
  702               getStandardOutput().writeEOL();
  703               for (Map.Entry<COSName, COSBase> entry : obj.entrySet())
  704               {
  705                   COSBase value = entry.getValue();
  706                   if (value != null)
  707                   {
  708                       entry.getKey().accept(this);
  709                       getStandardOutput().write(SPACE);
  710                       if( value instanceof COSDictionary )
  711                       {
  712                           addObjectToWrite( value );
  713                           writeReference( value );
  714                       }
  715                       else if( value instanceof COSObject )
  716                       {
  717                           COSBase subValue = ((COSObject)value).getObject();
  718                           if( subValue instanceof COSDictionary || subValue == null )
  719                           {
  720                               addObjectToWrite( value );
  721                               writeReference( value );
  722                           }
  723                           else
  724                           {
  725                               subValue.accept( this );
  726                           }
  727                       }
  728                       else
  729                       {
  730                           value.accept(this);
  731                       }
  732                       getStandardOutput().writeEOL();
  733   
  734                   }
  735                   else
  736                   {
  737                       //then we won't write anything, there are a couple cases
  738                       //were the value of an entry in the COSDictionary will
  739                       //be a dangling reference that points to nothing
  740                       //so we will just not write out the entry if that is the case
  741                   }
  742               }
  743               getStandardOutput().write(DICT_CLOSE);
  744               getStandardOutput().writeEOL();
  745               return null;
  746           }
  747           catch( IOException e )
  748           {
  749               throw new COSVisitorException(e);
  750           }
  751       }
  752   
  753       /**
  754        * The visit from document method.
  755        *
  756        * @param doc The object that is being visited.
  757        *
  758        * @throws COSVisitorException If there is an exception while visiting this object.
  759        *
  760        * @return null
  761        */
  762       public Object visitFromDocument(COSDocument doc) throws COSVisitorException
  763       {
  764           try
  765           {
  766               doWriteHeader(doc);
  767               doWriteBody(doc);
  768               doWriteXRef(doc);
  769               doWriteTrailer(doc);
  770               return null;
  771           }
  772           catch (IOException e)
  773           {
  774               throw new COSVisitorException(e);
  775           }
  776       }
  777   
  778       /**
  779        * visitFromFloat method comment.
  780        *
  781        * @param obj The object that is being visited.
  782        *
  783        * @throws COSVisitorException If there is an exception while visiting this object.
  784        *
  785        * @return null
  786        */
  787       public Object visitFromFloat(COSFloat obj) throws COSVisitorException
  788       {
  789   
  790           try
  791           {
  792               obj.writePDF( getStandardOutput() );
  793               return null;
  794           }
  795           catch (IOException e)
  796           {
  797               throw new COSVisitorException(e);
  798           }
  799       }
  800   
  801       /**
  802        * visitFromFloat method comment.
  803        *
  804        * @param obj The object that is being visited.
  805        *
  806        * @throws COSVisitorException If there is an exception while visiting this object.
  807        *
  808        * @return null
  809        */
  810       public Object visitFromInt(COSInteger obj) throws COSVisitorException
  811       {
  812           try
  813           {
  814               obj.writePDF( getStandardOutput() );
  815               return null;
  816           }
  817           catch (IOException e)
  818           {
  819               throw new COSVisitorException(e);
  820           }
  821       }
  822   
  823       /**
  824        * visitFromName method comment.
  825        *
  826        * @param obj The object that is being visited.
  827        *
  828        * @throws COSVisitorException If there is an exception while visiting this object.
  829        *
  830        * @return null
  831        */
  832       public Object visitFromName(COSName obj) throws COSVisitorException
  833       {
  834           try
  835           {
  836               obj.writePDF( getStandardOutput() );
  837               return null;
  838           }
  839           catch (IOException e)
  840           {
  841               throw new COSVisitorException(e);
  842           }
  843       }
  844   
  845       /**
  846        * visitFromNull method comment.
  847        *
  848        * @param obj The object that is being visited.
  849        *
  850        * @throws COSVisitorException If there is an exception while visiting this object.
  851        *
  852        * @return null
  853        */
  854       public Object visitFromNull(COSNull obj) throws COSVisitorException
  855       {
  856           try
  857           {
  858               obj.writePDF( getStandardOutput() );
  859               return null;
  860           }
  861           catch (IOException e)
  862           {
  863               throw new COSVisitorException(e);
  864           }
  865       }
  866   
  867       /**
  868        * visitFromObjRef method comment.
  869        *
  870        * @param obj The object that is being visited.
  871        *
  872        * @throws COSVisitorException If there is an exception while visiting this object.
  873        */
  874       public void writeReference(COSBase obj) throws COSVisitorException
  875       {
  876           try
  877           {
  878               COSObjectKey  key = getObjectKey(obj);
  879               getStandardOutput().write(String.valueOf(key.getNumber()).getBytes());
  880               getStandardOutput().write(SPACE);
  881               getStandardOutput().write(String.valueOf(key.getGeneration()).getBytes());
  882               getStandardOutput().write(SPACE);
  883               getStandardOutput().write(REFERENCE);
  884           }
  885           catch (IOException e)
  886           {
  887               throw new COSVisitorException(e);
  888           }
  889       }
  890   
  891       /**
  892        * visitFromStream method comment.
  893        *
  894        * @param obj The object that is being visited.
  895        *
  896        * @throws COSVisitorException If there is an exception while visiting this object.
  897        *
  898        * @return null
  899        */
  900       public Object visitFromStream(COSStream obj) throws COSVisitorException
  901       {
  902           try
  903           {
  904               if(willEncrypt)
  905               {
  906                   document.getSecurityHandler().decryptStream(
  907                       obj,
  908                       currentObjectKey.getNumber(),
  909                       currentObjectKey.getGeneration());
  910               }
  911   
  912               InputStream input = obj.getFilteredStream();
  913               // set the length of the stream and write stream dictionary
  914               COSObject lengthObject = new COSObject( null );
  915   
  916               obj.setItem(COSName.LENGTH, lengthObject);
  917               //obj.accept(this);
  918               // write the stream content
  919               visitFromDictionary( obj );
  920               getStandardOutput().write(STREAM);
  921               getStandardOutput().writeCRLF();
  922               byte[] buffer = new byte[1024];
  923               int amountRead = 0;
  924               int totalAmountWritten = 0;
  925               while( (amountRead = input.read(buffer,0,1024)) != -1 )
  926               {
  927                   getStandardOutput().write( buffer, 0, amountRead );
  928                   totalAmountWritten += amountRead;
  929               }
  930               lengthObject.setObject( COSInteger.get( totalAmountWritten ) );
  931               getStandardOutput().writeCRLF();
  932               getStandardOutput().write(ENDSTREAM);
  933               getStandardOutput().writeEOL();
  934               return null;
  935           }
  936           catch( Exception e )
  937           {
  938               throw new COSVisitorException(e);
  939           }
  940       }
  941   
  942       /**
  943        * visitFromString method comment.
  944        *
  945        * @param obj The object that is being visited.
  946        *
  947        * @return null
  948        *
  949        * @throws COSVisitorException If there is an exception while visiting this object.
  950        */
  951       public Object visitFromString(COSString obj) throws COSVisitorException
  952       {
  953           try
  954           {
  955               if(willEncrypt)
  956               {
  957                   document.getSecurityHandler().decryptString(
  958                       obj,
  959                       currentObjectKey.getNumber(),
  960                       currentObjectKey.getGeneration());
  961               }
  962   
  963               obj.writePDF( getStandardOutput() );
  964           }
  965           catch (Exception e)
  966           {
  967               throw new COSVisitorException(e);
  968           }
  969           return null;
  970       }
  971   
  972       /**
  973        * This will write the pdf document.
  974        *
  975        * @param doc The document to write.
  976        *
  977        * @throws COSVisitorException If an error occurs while generating the data.
  978        */
  979       public void write(COSDocument doc) throws COSVisitorException
  980       {
  981           PDDocument pdDoc = new PDDocument( doc );
  982           write( pdDoc );
  983       }
  984   
  985       /**
  986        * This will write the pdf document.
  987        *
  988        * @param doc The document to write.
  989        *
  990        * @throws COSVisitorException If an error occurs while generating the data.
  991        */
  992       public void write(PDDocument doc) throws COSVisitorException
  993       {
  994           document = doc;
  995           
  996           // if the document says we should remove encryption, then we shouldn't encrypt
  997           if(doc.isAllSecurityToBeRemoved())
  998           {
  999               this.willEncrypt = false;
 1000               // also need to get rid of the "Encrypt" in the trailer so readers 
 1001               // don't try to decrypt a document which is not encrypted
 1002               COSDocument cosDoc = doc.getDocument();
 1003               COSDictionary trailer = cosDoc.getTrailer();
 1004               trailer.removeItem(COSName.ENCRYPT);
 1005           }
 1006           else
 1007           {
 1008               SecurityHandler securityHandler = document.getSecurityHandler();
 1009               if(securityHandler != null)
 1010               {
 1011                   try
 1012                   {
 1013                       securityHandler.prepareDocumentForEncryption(document);
 1014                       this.willEncrypt = true;
 1015                   }
 1016                   catch(IOException e)
 1017                   {
 1018                       throw new COSVisitorException( e );
 1019                   }
 1020                   catch(CryptographyException e)
 1021                   {
 1022                       throw new COSVisitorException( e );
 1023                   }
 1024               }
 1025               else
 1026               {
 1027                       this.willEncrypt = false;
 1028               }        
 1029           }
 1030   
 1031           COSDocument cosDoc = document.getDocument();
 1032           COSDictionary trailer = cosDoc.getTrailer();
 1033           COSArray idArray = (COSArray)trailer.getDictionaryObject( COSName.ID );
 1034           if( idArray == null )
 1035           {
 1036               try
 1037               {
 1038   
 1039                   //algothim says to use time/path/size/values in doc to generate
 1040                   //the id.  We don't have path or size, so do the best we can
 1041                   MessageDigest md = MessageDigest.getInstance( "MD5" );
 1042                   md.update( Long.toString( System.currentTimeMillis()).getBytes() );
 1043                   COSDictionary info = (COSDictionary)trailer.getDictionaryObject( COSName.INFO );
 1044                   if( info != null )
 1045                   {
 1046                       Iterator values = info.getValues().iterator();
 1047                       while( values.hasNext() )
 1048                       {
 1049                           md.update( values.next().toString().getBytes() );
 1050                       }
 1051                   }
 1052                   idArray = new COSArray();
 1053                   COSString id = new COSString( md.digest() );
 1054                   idArray.add( id );
 1055                   idArray.add( id );
 1056                   trailer.setItem( COSName.ID, idArray );
 1057               }
 1058               catch( NoSuchAlgorithmException e )
 1059               {
 1060                   throw new COSVisitorException( e );
 1061               }
 1062           }
 1063   
 1064           /*
 1065           List objects = doc.getObjects();
 1066           Iterator iter = objects.iterator();
 1067           long maxNumber = 0;
 1068           while( iter.hasNext() )
 1069           {
 1070               COSObject object = (COSObject)iter.next();
 1071               if( object.getObjectNumber() != null &&
 1072                   object.getGenerationNumber() != null )
 1073               {
 1074                   COSObjectKey key = new COSObjectKey( object.getObjectNumber().longValue(),
 1075                                                        object.getGenerationNumber().longValue() );
 1076                   objectKeys.put( object.getObject(), key );
 1077                   objectKeys.put( object, key );
 1078                   maxNumber = Math.max( key.getNumber(), maxNumber );
 1079                   setNumber( maxNumber );
 1080               }
 1081           }*/
 1082           cosDoc.accept(this);
 1083       }
 1084   }

Home » pdfbox-1.1.0-src » org.apache.pdfbox.pdfwriter » [javadoc | source]