jeremias    2003/03/27 03:15:27

  Modified:    src/java/org/apache/fop/pdf PDFDocument.java
  Log:
  Switch for disabling on-the-fly stream output (if something goes wrong. was tested 
with Acrobat and GhostScript)
  Factory methods moved to new PDFFactory class.
  More uniform and centralized registration of PDFObjects (a lot less redundancy!)
  Logging via Avalon Logger (Child PDFObjects get their logger via 
getDocumentSafely().getLogger()).
  
  Revision  Changes    Path
  1.4       +310 -1366 xml-fop/src/java/org/apache/fop/pdf/PDFDocument.java
  
  Index: PDFDocument.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/java/org/apache/fop/pdf/PDFDocument.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- PDFDocument.java  15 Mar 2003 16:54:26 -0000      1.3
  +++ PDFDocument.java  27 Mar 2003 11:15:27 -0000      1.4
  @@ -50,30 +50,18 @@
    */ 
   package org.apache.fop.pdf;
   
  -import org.apache.fop.util.StreamUtilities;
  -
  -import org.apache.avalon.framework.logger.AbstractLogEnabled;
  -import org.apache.fop.fonts.CIDFont;
  -import org.apache.fop.fonts.CustomFont;
  -import org.apache.fop.fonts.Font;
  -import org.apache.fop.fonts.FontDescriptor;
  -import org.apache.fop.fonts.FontMetrics;
  -import org.apache.fop.fonts.FontType;
  -import org.apache.fop.fonts.LazyFont;
  -import org.apache.fop.fonts.MultiByteFont;
  -import org.apache.fop.fonts.truetype.FontFileReader;
  -import org.apache.fop.fonts.truetype.TTFSubSetFile;
  -import org.apache.fop.fonts.type1.PFBData;
  -import org.apache.fop.fonts.type1.PFBParser;
  -
   // Java
   import java.io.IOException;
   import java.io.InputStream;
   import java.io.OutputStream;
  +import java.io.UnsupportedEncodingException;
   import java.util.List;
   import java.util.Map;
   import java.util.Iterator;
  -import java.awt.geom.Rectangle2D;
  +
  +// Avalon
  +import org.apache.avalon.framework.logger.LogEnabled;
  +import org.apache.avalon.framework.logger.Logger;
   
   /* image support modified from work of BoBoGi */
   /* font support based on work by Takayuki Takeuchi */
  @@ -99,7 +87,7 @@
    * the object list; enhanced trailer output; cleanups.
    *
    */
  -public class PDFDocument extends AbstractLogEnabled {
  +public class PDFDocument implements LogEnabled {
       
       private static final Integer LOCATION_PLACEHOLDER = new Integer(0);
       /**
  @@ -112,6 +100,8 @@
        */
       public static final String ENCODING = "ISO-8859-1";
   
  +    private Logger logger;
  +
       /**
        * the current character position
        */
  @@ -133,7 +123,7 @@
       /**
        * the objects themselves
        */
  -    protected List objects = new java.util.ArrayList();
  +    protected List objects = new java.util.LinkedList();
   
       /**
        * character position of xref table
  @@ -243,6 +233,10 @@
       protected List gotos = new java.util.ArrayList();
   
   
  +    private PDFFactory factory;
  +    
  +    private boolean encodingOnTheFly = true;
  +
       /**
        * creates an empty PDF document <p>
        *
  @@ -257,17 +251,68 @@
        */
       public PDFDocument(String prod) {
   
  +        this.factory = new PDFFactory(this);
  +
           /* create the /Root, /Info and /Resources objects */
  -        this.pages = makePages();
  +        this.pages = getFactory().makePages();
   
           // Create the Root object
  -        this.root = makeRoot(pages);
  +        this.root = getFactory().makeRoot(pages);
   
           // Create the Resources object
  -        this.resources = makeResources();
  +        this.resources = getFactory().makeResources();
   
           // Make the /Info record
  -        this.info = makeInfo(prod);
  +        this.info = getFactory().makeInfo(prod);
  +    }
  +
  +    /**
  +     * Returns the factory for PDF objects.
  +     * @return PDFFactory the factory
  +     */
  +    public PDFFactory getFactory() {
  +        return this.factory;
  +    }
  +
  +    /**
  +     * Indicates whether stream encoding on-the-fly is enabled. If enabled
  +     * stream can be serialized without the need for a buffer to merely
  +     * calculate the stream length.
  +     * @return boolean true if on-the-fly encoding is enabled
  +     */
  +    public boolean isEncodingOnTheFly() {
  +        return this.encodingOnTheFly;
  +    }
  +
  +    /**
  +     * @see org.apache.avalon.framework.logger.LogEnabled#enableLogging(Logger)
  +     */
  +    public void enableLogging(Logger logger) {
  +        this.logger = logger;
  +    }
  +
  +    /**
  +     * Helper method to allow sub-classes to aquire logger.
  +     * 
  +     * <p>There is no performance penalty as this is a final method
  +     * and will be inlined by the JVM.</p>
  +     * @return the Logger
  +     */
  +    protected final Logger getLogger() {
  +        return this.logger;
  +    }
  +
  +    /**
  +     * Converts text to a byte array for writing to a PDF file.
  +     * @param text text to convert/encode
  +     * @return byte[] the resulting byte array
  +     */
  +    public static byte[] encode(String text) {
  +        try {
  +            return text.getBytes(ENCODING);
  +        } catch (UnsupportedEncodingException uee) {
  +            return text.getBytes();
  +        }       
       }
   
       /**
  @@ -294,7 +339,7 @@
        * @param map the map of filter lists for each stream type
        */
       public void setFilterMap(Map map) {
  -        filterMap = map;
  +        this.filterMap = map;
       }
   
       /**
  @@ -303,58 +348,15 @@
        * @return the map of filters being used
        */
       public Map getFilterMap() {
  -        return filterMap;
  -    }
  -
  -    /**
  -     * Apply the encryption filter to a PDFStream if encryption is enabled.
  -     * @param stream PDFStream to encrypt
  -     */
  -    public void applyEncryption(PDFStream stream) {
  -        if (isEncryptionActive()) {
  -            this.encryption.applyFilter(stream);
  -        }
  +        return this.filterMap;
       }
   
       /**
  -     * Enables PDF encryption.
  -     * @param params The encryption parameters for the pdf file
  -     */
  -    public void setEncryption(PDFEncryptionParams params) {
  -        this.encryption = PDFEncryptionManager.newInstance(++this.objectcount, 
params);
  -        if (encryption != null) {
  -            /[EMAIL PROTECTED] this cast is ugly. PDFObject should be transformed 
to an interface. */
  -            addTrailerObject((PDFObject)this.encryption);
  -        } else {
  -            getLogger().warn("PDF encryption is unavailable. PDF will be "
  -                + "generated without encryption.");
  -        }
  -    }
  -    
  -    
  -    /**
  -     * Indicates whether encryption is active for this PDF or not.
  -     * @return boolean True if encryption is active
  -     */
  -    public boolean isEncryptionActive() {
  -        return this.encryption != null;
  -    }
  -    
  -    /**
  -     * Make a /Catalog (Root) object. This object is written in
  -     * the trailer.
  -     *
  -     * @param pages the pages pdf object that the root points to
  -     * @return the new pdf root object for this document
  +     * Returns the PDFPages object associated with the root object.
  +     * @return the PDFPages object
        */
  -    public PDFRoot makeRoot(PDFPages pages) {
  -
  -        /*
  -        * Make a /Pages object. This object is written in the trailer.
  -        */
  -        PDFRoot pdfRoot = new PDFRoot(++this.objectcount, pages);
  -        addTrailerObject(pdfRoot);
  -        return pdfRoot;
  +    public PDFPages getPages() {
  +        return this.pages;
       }
   
       /**
  @@ -367,183 +369,162 @@
       }
   
       /**
  -     * Make a /Pages object. This object is written in the trailer.
  +     * Get the pdf info object for this document.
        *
  -     * @return a new PDF Pages object for adding pages to
  +     * @return the PDF Info object for this document
        */
  -    public PDFPages makePages() {
  -        PDFPages pdfPages = new PDFPages(++this.objectcount);
  -        addTrailerObject(pdfPages);
  -        return pdfPages;
  +    public PDFInfo getInfo() {
  +        return info;
       }
   
       /**
  -     * Make a /Resources object. This object is written in the trailer.
  -     *
  -     * @return a new PDF resources object
  -     */
  -    public PDFResources makeResources() {
  -        PDFResources pdfResources = new PDFResources(++this.objectcount);
  -        addTrailerObject(pdfResources);
  -        return pdfResources;
  +     * Registers a PDFObject in this PDF document. The PDF is assigned a new 
  +     * object number.
  +     * @param obj PDFObject to add
  +     * @return PDFObject the PDFObject added (its object number set)
  +     */
  +    public PDFObject registerObject(PDFObject obj) {
  +        assignObjectNumber(obj);
  +        addObject(obj);
  +        return obj;
       }
   
       /**
  -     * make an /Info object
  -     *
  -     * @param prod string indicating application producing the PDF
  -     * @return the created /Info object
  +     * Assigns the PDFObject a object number and sets the parent of the 
  +     * PDFObject to this PDFDocument.
  +     * @param obj PDFObject to assign a number to
  +     */
  +    public void assignObjectNumber(PDFObject obj) {
  +        if (obj == null) {
  +            throw new NullPointerException("obj must not be null");
  +        }
  +        if (obj.hasObjectNumber()) {
  +            throw new IllegalStateException("Error registering a PDFObject: "
  +                + "PDFObject already has an object number");
  +        }
  +        PDFDocument currentParent = obj.getDocument();
  +        if (currentParent != null && currentParent != this) {
  +            throw new IllegalStateException("Error registering a PDFObject: "
  +                + "PDFObject already has a parent PDFDocument");
  +        }
  +        
  +        obj.setObjectNumber(++this.objectcount);
  +
  +        if (currentParent == null) {
  +            obj.setDocument(this);
  +        }
  +    }
  +
  +    /**
  +     * Adds an PDFObject to this document. The object must have a object number
  +     * assigned.
  +     * @param obj PDFObject to add
        */
  -    protected PDFInfo makeInfo(String prod) {
  +    public void addObject(PDFObject obj) {
  +        if (obj == null) {
  +            throw new NullPointerException("obj must not be null");
  +        }
  +        if (!obj.hasObjectNumber()) {
  +            throw new IllegalStateException("Error adding a PDFObject: "
  +                + "PDFObject doesn't have an object number");
  +        }
   
  -        /*
  -         * create a PDFInfo with the next object number and add to
  -         * list of objects
  -         */
  -        PDFInfo pdfInfo = new PDFInfo(++this.objectcount);
  -        // set the default producer
  -        pdfInfo.setProducer(prod);
  -        this.objects.add(pdfInfo);
  -        return pdfInfo;
  +        //Add object to list
  +        this.objects.add(obj);
  +        
  +        //System.out.println("Registering: "+obj);
  +        
  +        //Add object to special lists where necessary
  +        if (obj instanceof PDFFunction) {
  +            this.functions.add(obj);
  +        }
  +        if (obj instanceof PDFShading) {
  +            final String shadingName = "Sh" + (++this.shadingCount);
  +            ((PDFShading)obj).setName(shadingName);
  +            this.shadings.add(obj);
  +        }
  +        if (obj instanceof PDFPattern) {
  +            final String patternName = "Pa" + (++this.patternCount);
  +            ((PDFPattern)obj).setName(patternName);
  +            this.patterns.add(obj);
  +        }
  +        if (obj instanceof PDFFont) {
  +            final PDFFont font = (PDFFont)obj;
  +            this.fontMap.put(font.getName(), font);
  +        }
  +        if (obj instanceof PDFGState) {
  +            this.gstates.add(obj);
  +        }
  +        if (obj instanceof PDFPage) {
  +            this.pages.notifyKidRegistered((PDFPage)obj);
  +        }
  +        if (obj instanceof PDFLink) {
  +            this.links.add(obj);
  +        }            
  +        if (obj instanceof PDFFileSpec) {
  +            this.filespecs.add(obj);
  +        }
  +        if (obj instanceof PDFGoToRemote) {
  +            this.gotoremotes.add(obj);
  +        }
       }
   
  +
       /**
  -     * Get the pdf info object for this document.
  +     * Add trailer object.
  +     * Adds an object to the list of trailer objects.
        *
  -     * @return the PDF Info object for this document
  +     * @param obj the PDF object to add
        */
  -    public PDFInfo getInfo() {
  -        return info;
  +    public void addTrailerObject(PDFObject obj) {
  +        this.trailerObjects.add(obj);
  +        
  +        if (obj instanceof PDFGoTo) {
  +            this.gotos.add(obj);
  +        }            
       }
   
       /**
  -     * Make a Type 0 sampled function
  -     *
  -     * @param theDomain List objects of Double objects.
  -     * This is the domain of the function.
  -     * See page 264 of the PDF 1.3 Spec.
  -     * @param theRange List objects of Double objects.
  -     * This is the Range of the function.
  -     * See page 264 of the PDF 1.3 Spec.
  -     * @param theSize A List object of Integer objects.
  -     * This is the number of samples in each input dimension.
  -     * I can't imagine there being more or less than two input dimensions,
  -     * so maybe this should be an array of length 2.
  -     *
  -     * See page 265 of the PDF 1.3 Spec.
  -     * @param theBitsPerSample An int specifying the number of bits user
  -     *                    to represent each sample value.
  -     * Limited to 1,2,4,8,12,16,24 or 32.
  -     * See page 265 of the 1.3 PDF Spec.
  -     * @param theOrder The order of interpolation between samples.
  -     *                 Default is 1 (one). Limited
  -     * to 1 (one) or 3, which means linear or cubic-spline interpolation.
  -     *
  -     * This attribute is optional.
  -     *
  -     * See page 265 in the PDF 1.3 spec.
  -     * @param theEncode List objects of Double objects.
  -     * This is the linear mapping of input values intop the domain
  -     * of the function's sample table. Default is hard to represent in
  -     * ascii, but basically [0 (Size0 1) 0 (Size1 1)...].
  -     * This attribute is optional.
  -     *
  -     * See page 265 in the PDF 1.3 spec.
  -     * @param theDecode List objects of Double objects.
  -     * This is a linear mapping of sample values into the range.
  -     * The default is just the range.
  -     *
  -     * This attribute is optional.
  -     * Read about it on page 265 of the PDF 1.3 spec.
  -     * @param theFunctionDataStream The sample values that specify
  -     *                        the function are provided in a stream.
  -     *
  -     * This is optional, but is almost always used.
  -     *
  -     * Page 265 of the PDF 1.3 spec has more.
  -     * @param theFilter This is a vector of String objects which
  -     *                  are the various filters that have are to be
  -     *                  applied to the stream to make sense of it.
  -     *                  Order matters, so watch out.
  -     *
  -     * This is not documented in the Function section of the PDF 1.3 spec,
  -     * it was deduced from samples that this is sometimes used, even if we may never
  -     * use it in FOP. It is added for completeness sake.
  -     * @param theFunctionType This is the type of function (0,2,3, or 4).
  -     * It should be 0 as this is the constructor for sampled functions.
  -     * @return the PDF function that was created
  -     */
  -    public PDFFunction makeFunction(int theFunctionType, List theDomain,
  -                                    List theRange, List theSize,
  -                                    int theBitsPerSample, int theOrder,
  -                                    List theEncode, List theDecode,
  -                                    StringBuffer theFunctionDataStream,
  -                                    List theFilter) {
  -        // Type 0 function
  -        PDFFunction function = new PDFFunction(++this.objectcount,
  -                                               theFunctionType, theDomain,
  -                                               theRange, theSize,
  -                                               theBitsPerSample, theOrder,
  -                                               theEncode, theDecode,
  -                                               theFunctionDataStream,
  -                                               theFilter);
  -
  -        PDFFunction oldfunc = findFunction(function);
  -        if (oldfunc == null) {
  -            functions.add(function);
  -            this.objects.add(function);
  -        } else {
  -            this.objectcount--;
  -            function = oldfunc;
  +     * Apply the encryption filter to a PDFStream if encryption is enabled.
  +     * @param stream PDFStream to encrypt
  +     */
  +    public void applyEncryption(AbstractPDFStream stream) {
  +        if (isEncryptionActive()) {
  +            this.encryption.applyFilter(stream);
           }
  -
  -        return (function);
       }
   
       /**
  -     * make a type Exponential interpolation function
  -     * (for shading usually)
  -     *
  -     * @param theDomain List objects of Double objects.
  -     * This is the domain of the function.
  -     * See page 264 of the PDF 1.3 Spec.
  -     * @param theRange List of Doubles that is the Range of the function.
  -     * See page 264 of the PDF 1.3 Spec.
  -     * @param theCZero This is a vector of Double objects which defines the 
function result
  -     * when x=0.
  -     *
  -     * This attribute is optional.
  -     * It's described on page 268 of the PDF 1.3 spec.
  -     * @param theCOne This is a vector of Double objects which defines the function 
result
  -     * when x=1.
  -     *
  -     * This attribute is optional.
  -     * It's described on page 268 of the PDF 1.3 spec.
  -     * @param theInterpolationExponentN This is the inerpolation exponent.
  -     *
  -     * This attribute is required.
  -     * PDF Spec page 268
  -     * @param theFunctionType The type of the function, which should be 2.
  -     * @return the PDF function that was created
  -     */
  -    public PDFFunction makeFunction(int theFunctionType, List theDomain,
  -                                    List theRange, List theCZero,
  -                                    List theCOne,
  -                                    double theInterpolationExponentN) {    // type 2
  -        PDFFunction function = new PDFFunction(++this.objectcount,
  -                                               theFunctionType, theDomain,
  -                                               theRange, theCZero, theCOne,
  -                                               theInterpolationExponentN);
  -        PDFFunction oldfunc = findFunction(function);
  -        if (oldfunc == null) {
  -            functions.add(function);
  -            this.objects.add(function);
  +     * Enables PDF encryption.
  +     * @param params The encryption parameters for the pdf file
  +     */
  +    public void setEncryption(PDFEncryptionParams params) {
  +        this.encryption = PDFEncryptionManager.newInstance(++this.objectcount, 
params);
  +        ((PDFObject)this.encryption).setDocument(this);
  +        if (encryption != null) {
  +            /[EMAIL PROTECTED] this cast is ugly. PDFObject should be transformed 
to an interface. */
  +            addTrailerObject((PDFObject)this.encryption);
           } else {
  -            this.objectcount--;
  -            function = oldfunc;
  +            getLogger().warn("PDF encryption is unavailable. PDF will be "
  +                + "generated without encryption.");
           }
  -
  -        return (function);
  +    }
  +    
  +    
  +    /**
  +     * Indicates whether encryption is active for this PDF or not.
  +     * @return boolean True if encryption is active
  +     */
  +    public boolean isEncryptionActive() {
  +        return this.encryption != null;
  +    }
  +    
  +    /**
  +     * Returns the active Encryption object.
  +     * @return the Encryption object
  +     */
  +    public PDFEncryption getEncryption() {
  +        return encryption;
       }
   
       private Object findPDFObject(List list, PDFObject compare) {
  @@ -556,11 +537,23 @@
           return null;
       }
   
  -    private PDFFunction findFunction(PDFFunction compare) {
  +    /**
  +     * Looks through the registered functions to see if one that is equal to
  +     * a reference object exists
  +     * @param compare reference object
  +     * @return the function if it was found, null otherwise
  +     */
  +    protected PDFFunction findFunction(PDFFunction compare) {
           return (PDFFunction)findPDFObject(functions, compare);
       }
   
  -    private PDFShading findShading(PDFShading compare) {
  +    /**
  +     * Looks through the registered shadings to see if one that is equal to
  +     * a reference object exists
  +     * @param compare reference object
  +     * @return the shading if it was found, null otherwise
  +     */
  +    protected PDFShading findShading(PDFShading compare) {
           return (PDFShading)findPDFObject(shadings, compare);
       }
   
  @@ -569,429 +562,86 @@
        * The problem with this is for tiling patterns the pattern
        * data stream is stored and may use up memory, usually this
        * would only be a small amount of data.
  +     * @param compare reference object
  +     * @return the shading if it was found, null otherwise
        */
  -    private PDFPattern findPattern(PDFPattern compare) {
  +    protected PDFPattern findPattern(PDFPattern compare) {
           return (PDFPattern)findPDFObject(patterns, compare);
       }
   
       /**
  -     * Make a Type 3 Stitching function
  -     *
  -     * @param theDomain List objects of Double objects.
  -     * This is the domain of the function.
  -     * See page 264 of the PDF 1.3 Spec.
  -     * @param theRange List objects of Double objects.
  -     * This is the Range of the function.
  -     * See page 264 of the PDF 1.3 Spec.
  -     * @param theFunctions An List of the PDFFunction objects
  -     *                     that the stitching function stitches.
  -     *
  -     * This attributed is required.
  -     * It is described on page 269 of the PDF spec.
  -     * @param theBounds This is a vector of Doubles representing
  -     *                  the numbers that, in conjunction with Domain
  -     *                  define the intervals to which each function from
  -     *                  the 'functions' object applies. It must be in
  -     *                  order of increasing magnitude, and each must be
  -     *                  within Domain.
  -     *
  -     * It basically sets how much of the gradient each function handles.
  -     *
  -     * This attributed is required.
  -     * It's described on page 269 of the PDF 1.3 spec.
  -     * @param theEncode List objects of Double objects.
  -     * This is the linear mapping of input values intop the domain
  -     * of the function's sample table. Default is hard to represent in
  -     * ascii, but basically [0 (Size0 1) 0 (Size1 1)...].
  -     * This attribute is required.
  -     *
  -     * See page 270 in the PDF 1.3 spec.
  -     * @param theFunctionType This is the function type. It should be 3,
  -     * for a stitching function.
  -     * @return the PDF function that was created
  -     */
  -    public PDFFunction makeFunction(int theFunctionType, List theDomain,
  -                                    List theRange, List theFunctions,
  -                                    List theBounds,
  -                                    List theEncode) {
  -        // Type 3
  -
  -        PDFFunction function = new PDFFunction(++this.objectcount,
  -                                               theFunctionType, theDomain,
  -                                               theRange, theFunctions,
  -                                               theBounds, theEncode);
  -
  -        PDFFunction oldfunc = findFunction(function);
  -        if (oldfunc == null) {
  -            functions.add(function);
  -            this.objects.add(function);
  -        } else {
  -            this.objectcount--;
  -            function = oldfunc;
  -        }
  -
  -        return (function);
  -    }
  -
  -    /**
  -     * make a postscript calculator function
  -     *
  -     * @param theNumber the PDF object number
  -     * @param theFunctionType the type of function to make
  -     * @param theDomain the domain values
  -     * @param theRange the range values of the function
  -     * @param theFunctionDataStream a string containing the pdf drawing
  -     * @return the PDF function that was created
  +     * Finds a font.
  +     * @param fontname name of the font
  +     * @return PDFFont the requested font, null if it wasn't found
        */
  -    public PDFFunction makeFunction(int theNumber, int theFunctionType,
  -                                    List theDomain, List theRange,
  -                                    StringBuffer theFunctionDataStream) {
  -        // Type 4
  -        PDFFunction function = new PDFFunction(++this.objectcount,
  -                                               theFunctionType, theDomain,
  -                                               theRange,
  -                                               theFunctionDataStream);
  -
  -        PDFFunction oldfunc = findFunction(function);
  -        if (oldfunc == null) {
  -            functions.add(function);
  -            this.objects.add(function);
  -        } else {
  -            this.objectcount--;
  -            function = oldfunc;
  -        }
  -
  -        return (function);
  -
  +    protected PDFFont findFont(String fontname) {
  +        return (PDFFont)fontMap.get(fontname);
       }
   
       /**
  -     * make a function based shading object
  -     *
  -     * @param res the PDF resource context to add the shading, may be null
  -     * @param theShadingType The type of shading object, which should be 1 for 
function
  -     * based shading.
  -     * @param theColorSpace The colorspace is 'DeviceRGB' or something similar.
  -     * @param theBackground An array of color components appropriate to the
  -     * colorspace key specifying a single color value.
  -     * This key is used by the f operator buy ignored by the sh operator.
  -     * @param theBBox List of double's representing a rectangle
  -     * in the coordinate space that is current at the
  -     * time of shading is imaged. Temporary clipping
  -     * boundary.
  -     * @param theAntiAlias Whether or not to anti-alias.
  -     * @param theDomain Optional vector of Doubles specifying the domain.
  -     * @param theMatrix List of Doubles specifying the matrix.
  -     * If it's a pattern, then the matrix maps it to pattern space.
  -     * If it's a shading, then it maps it to current user space.
  -     * It's optional, the default is the identity matrix
  -     * @param theFunction The PDF Function that maps an (x,y) location to a color
  -     * @return the PDF shading that was created
  -     */
  -    public PDFShading makeShading(PDFResourceContext res, int theShadingType,
  -                                  PDFColorSpace theColorSpace,
  -                                  List theBackground, List theBBox,
  -                                  boolean theAntiAlias, List theDomain,
  -                                  List theMatrix,
  -                                  PDFFunction theFunction) {
  -        // make Shading of Type 1
  -        String theShadingName = new String("Sh" + (++this.shadingCount));
  -
  -        PDFShading shading = new PDFShading(++this.objectcount,
  -                                            theShadingName, theShadingType,
  -                                            theColorSpace, theBackground,
  -                                            theBBox, theAntiAlias, theDomain,
  -                                            theMatrix, theFunction);
  -
  -        PDFShading oldshad = findShading(shading);
  -        if (oldshad == null) {
  -            shadings.add(shading);
  -            this.objects.add(shading);
  -        } else {
  -            this.objectcount--;
  -            this.shadingCount--;
  -            shading = oldshad;
  -        }
  -
  -        // add this shading to resources
  -        if (res != null) {
  -            res.getPDFResources().addShading(shading);
  -        } else {
  -            this.resources.addShading(shading);
  -        }
  -
  -        return (shading);
  +     * Finds a link.
  +     * @param compare reference object to use as search template
  +     * @return the link if found, null otherwise
  +     */
  +    protected PDFLink findLink(PDFLink compare) {
  +        return (PDFLink)findPDFObject(links, compare);
       }
   
       /**
  -     * Make an axial or radial shading object.
  -     *
  -     * @param res the PDF resource context to add the shading, may be null
  -     * @param theShadingType 2 or 3 for axial or radial shading
  -     * @param theColorSpace "DeviceRGB" or similar.
  -     * @param theBackground theBackground An array of color components appropriate 
to the
  -     * colorspace key specifying a single color value.
  -     * This key is used by the f operator buy ignored by the sh operator.
  -     * @param theBBox List of double's representing a rectangle
  -     * in the coordinate space that is current at the
  -     * time of shading is imaged. Temporary clipping
  -     * boundary.
  -     * @param theAntiAlias Default is false
  -     * @param theCoords List of four (type 2) or 6 (type 3) Double
  -     * @param theDomain List of Doubles specifying the domain
  -     * @param theFunction the Stitching (PDFfunction type 3) function,
  -     *                    even if it's stitching a single function
  -     * @param theExtend List of Booleans of whether to extend the
  -     *                  start and end colors past the start and end points
  -     * The default is [false, false]
  -     * @return the PDF shading that was created
  -     */
  -    public PDFShading makeShading(PDFResourceContext res, int theShadingType,
  -                                  PDFColorSpace theColorSpace,
  -                                  List theBackground, List theBBox,
  -                                  boolean theAntiAlias, List theCoords,
  -                                  List theDomain, PDFFunction theFunction,
  -                                  List theExtend) {
  -        // make Shading of Type 2 or 3
  -        String theShadingName = new String("Sh" + (++this.shadingCount));
  -
  -        PDFShading shading = new PDFShading(++this.objectcount,
  -                                            theShadingName, theShadingType,
  -                                            theColorSpace, theBackground,
  -                                            theBBox, theAntiAlias, theCoords,
  -                                            theDomain, theFunction,
  -                                            theExtend);
  -
  -        PDFShading oldshad = findShading(shading);
  -        if (oldshad == null) {
  -            shadings.add(shading);
  -            this.objects.add(shading);
  -        } else {
  -            this.objectcount--;
  -            this.shadingCount--;
  -            shading = oldshad;
  -        }
  -
  -        if (res != null) {
  -            res.getPDFResources().addShading(shading);
  -        } else {
  -            this.resources.addShading(shading);
  -        }
  -
  -        return (shading);
  +     * Finds a file spec.
  +     * @param compare reference object to use as search template
  +     * @return the file spec if found, null otherwise
  +     */
  +    protected PDFFileSpec findFileSpec(PDFFileSpec compare) {
  +        return (PDFFileSpec)findPDFObject(filespecs, compare);
       }
   
       /**
  -     * Make a free-form gouraud shaded triangle mesh, coons patch mesh, or tensor 
patch mesh
  -     * shading object
  -     *
  -     * @param res the PDF resource context to add the shading, may be null
  -     * @param theShadingType 4, 6, or 7 depending on whether it's
  -     * Free-form gouraud-shaded triangle meshes, coons patch meshes,
  -     * or tensor product patch meshes, respectively.
  -     * @param theColorSpace "DeviceRGB" or similar.
  -     * @param theBackground theBackground An array of color components appropriate 
to the
  -     * colorspace key specifying a single color value.
  -     * This key is used by the f operator buy ignored by the sh operator.
  -     * @param theBBox List of double's representing a rectangle
  -     * in the coordinate space that is current at the
  -     * time of shading is imaged. Temporary clipping
  -     * boundary.
  -     * @param theAntiAlias Default is false
  -     * @param theBitsPerCoordinate 1,2,4,8,12,16,24 or 32.
  -     * @param theBitsPerComponent 1,2,4,8,12, and 16
  -     * @param theBitsPerFlag 2,4,8.
  -     * @param theDecode List of Doubles see PDF 1.3 spec pages 303 to 312.
  -     * @param theFunction the PDFFunction
  -     * @return the PDF shading that was created
  -     */
  -    public PDFShading makeShading(PDFResourceContext res, int theShadingType,
  -                                  PDFColorSpace theColorSpace,
  -                                  List theBackground, List theBBox,
  -                                  boolean theAntiAlias,
  -                                  int theBitsPerCoordinate,
  -                                  int theBitsPerComponent,
  -                                  int theBitsPerFlag, List theDecode,
  -                                  PDFFunction theFunction) {
  -        // make Shading of type 4,6 or 7
  -        String theShadingName = new String("Sh" + (++this.shadingCount));
  -
  -        PDFShading shading = new PDFShading(++this.objectcount,
  -                                            theShadingName, theShadingType,
  -                                            theColorSpace, theBackground,
  -                                            theBBox, theAntiAlias,
  -                                            theBitsPerCoordinate,
  -                                            theBitsPerComponent,
  -                                            theBitsPerFlag, theDecode,
  -                                            theFunction);
  -
  -        PDFShading oldshad = findShading(shading);
  -        if (oldshad == null) {
  -            shadings.add(shading);
  -            this.objects.add(shading);
  -        } else {
  -            this.objectcount--;
  -            this.shadingCount--;
  -            shading = oldshad;
  -        }
  -
  -        if (res != null) {
  -            res.getPDFResources().addShading(shading);
  -        } else {
  -            this.resources.addShading(shading);
  -        }
  -
  -        return (shading);
  +     * Finds a goto remote.
  +     * @param compare reference object to use as search template
  +     * @return the goto remote if found, null otherwise
  +     */
  +    protected PDFGoToRemote findGoToRemote(PDFGoToRemote compare) {
  +        return (PDFGoToRemote)findPDFObject(gotoremotes, compare);
       }
   
       /**
  -     * make a Lattice-Form Gouraud mesh shading object
  -     *
  -     * @param res the PDF resource context to add the shading, may be null
  -     * @param theShadingType 5 for lattice-Form Gouraud shaded-triangle mesh
  -     * without spaces. "Shading1" or "Sh1" are good examples.
  -     * @param theColorSpace "DeviceRGB" or similar.
  -     * @param theBackground theBackground An array of color components appropriate 
to the
  -     * colorspace key specifying a single color value.
  -     * This key is used by the f operator buy ignored by the sh operator.
  -     * @param theBBox List of double's representing a rectangle
  -     * in the coordinate space that is current at the
  -     * time of shading is imaged. Temporary clipping
  -     * boundary.
  -     * @param theAntiAlias Default is false
  -     * @param theBitsPerCoordinate 1,2,4,8,12,16, 24, or 32
  -     * @param theBitsPerComponent 1,2,4,8,12,24,32
  -     * @param theDecode List of Doubles. See page 305 in PDF 1.3 spec.
  -     * @param theVerticesPerRow number of vertices in each "row" of the lattice.
  -     * @param theFunction The PDFFunction that's mapped on to this shape
  -     * @return the PDF shading that was created
  -     */
  -    public PDFShading makeShading(PDFResourceContext res, int theShadingType,
  -                                  PDFColorSpace theColorSpace,
  -                                  List theBackground, List theBBox,
  -                                  boolean theAntiAlias,
  -                                  int theBitsPerCoordinate,
  -                                  int theBitsPerComponent, List theDecode,
  -                                  int theVerticesPerRow,
  -                                  PDFFunction theFunction) {
  -        // make shading of Type 5
  -        String theShadingName = new String("Sh" + (++this.shadingCount));
  -
  -        PDFShading shading = new PDFShading(++this.objectcount,
  -                                            theShadingName, theShadingType,
  -                                            theColorSpace, theBackground,
  -                                            theBBox, theAntiAlias,
  -                                            theBitsPerCoordinate,
  -                                            theBitsPerComponent, theDecode,
  -                                            theVerticesPerRow, theFunction);
  -
  -        PDFShading oldshad = findShading(shading);
  -        if (oldshad == null) {
  -            shadings.add(shading);
  -            this.objects.add(shading);
  -        } else {
  -            this.objectcount--;
  -            this.shadingCount--;
  -            shading = oldshad;
  -        }
  -
  -        if (res != null) {
  -            res.getPDFResources().addShading(shading);
  -        } else {
  -            this.resources.addShading(shading);
  -        }
  -
  -        return (shading);
  +     * Finds a goto.
  +     * @param compare reference object to use as search template
  +     * @return the goto if found, null otherwise
  +     */
  +    protected PDFGoTo findGoTo(PDFGoTo compare) {
  +        return (PDFGoTo)findPDFObject(gotos, compare);
       }
  -
  +    
       /**
  -     * Make a tiling pattern
  -     *
  -     * @param res the PDF resource context to add the shading, may be null
  -     * @param thePatternType the type of pattern, which is 1 for tiling.
  -     * @param theResources the resources associated with this pattern
  -     * @param thePaintType 1 or 2, colored or uncolored.
  -     * @param theTilingType 1, 2, or 3, constant spacing, no distortion, or faster 
tiling
  -     * @param theBBox List of Doubles: The pattern cell bounding box
  -     * @param theXStep horizontal spacing
  -     * @param theYStep vertical spacing
  -     * @param theMatrix Optional List of Doubles transformation matrix
  -     * @param theXUID Optional vector of Integers that uniquely identify the pattern
  -     * @param thePatternDataStream The stream of pattern data to be tiled.
  -     * @return the PDF pattern that was created
  -     */
  -    public PDFPattern makePattern(PDFResourceContext res, int thePatternType,    // 
1
  -                                  PDFResources theResources, int thePaintType, int 
theTilingType,
  -                                  List theBBox, double theXStep,
  -                                  double theYStep, List theMatrix,
  -                                  List theXUID, StringBuffer thePatternDataStream) {
  -        String thePatternName = new String("Pa" + (++this.patternCount));
  -        // int theNumber, String thePatternName,
  -        // PDFResources theResources
  -        PDFPattern pattern = new PDFPattern(++this.objectcount,
  -                                            thePatternName, theResources, 1,
  -                                            thePaintType, theTilingType,
  -                                            theBBox, theXStep, theYStep,
  -                                            theMatrix, theXUID,
  -                                            thePatternDataStream);
  -
  -        PDFPattern oldpatt = findPattern(pattern);
  -        if (oldpatt == null) {
  -            patterns.add(pattern);
  -            this.objects.add(pattern);
  -        } else {
  -            this.objectcount--;
  -            this.patternCount--;
  -            pattern = oldpatt;
  -        }
  -
  -        if (res != null) {
  -            res.getPDFResources().addPattern(pattern);
  -        } else {
  -            this.resources.addPattern(pattern);
  +     * Looks for an existing GState to use
  +     * @param wanted requested features
  +     * @param current currently active features
  +     * @return PDFGState the GState if found, null otherwise
  +     */
  +    protected PDFGState findGState(PDFGState wanted, PDFGState current) {
  +        PDFGState poss;
  +        Iterator iter = gstates.iterator();
  +        while (iter.hasNext()) {
  +            PDFGState avail = (PDFGState)iter.next();
  +            poss = new PDFGState();
  +            poss.addValues(current);
  +            poss.addValues(avail);
  +            if (poss.equals(wanted)) {
  +                return avail;
  +            }
           }
  -
  -        return (pattern);
  +        return null;
       }
   
       /**
  -     * Make a smooth shading pattern
  +     * Get the PDF color space object.
        *
  -     * @param res the PDF resource context to add the shading, may be null
  -     * @param thePatternType the type of the pattern, which is 2, smooth shading
  -     * @param theShading the PDF Shading object that comprises this pattern
  -     * @param theXUID optional:the extended unique Identifier if used.
  -     * @param theExtGState optional: the extended graphics state, if used.
  -     * @param theMatrix Optional:List of Doubles that specify the matrix.
  -     * @return the PDF pattern that was created       
  +     * @return the color space
        */
  -    public PDFPattern makePattern(PDFResourceContext res,
  -                                  int thePatternType, PDFShading theShading,
  -                                  List theXUID, StringBuffer theExtGState,
  -                                  List theMatrix) {
  -        String thePatternName = new String("Pa" + (++this.patternCount));
  -
  -        PDFPattern pattern = new PDFPattern(++this.objectcount,
  -                                            thePatternName, 2, theShading,
  -                                            theXUID, theExtGState, theMatrix);
  -
  -        PDFPattern oldpatt = findPattern(pattern);
  -        if (oldpatt == null) {
  -            patterns.add(pattern);
  -            this.objects.add(pattern);
  -        } else {
  -            this.objectcount--;
  -            this.patternCount--;
  -            pattern = oldpatt;
  -        }
  -
  -        if (res != null) {
  -            res.getPDFResources().addPattern(pattern);
  -        } else {
  -            this.resources.addPattern(pattern);
  -        }
  -
  -        return (pattern);
  +    public PDFColorSpace getPDFColorSpace() {
  +        return this.colorspace;
       }
   
       /**
  @@ -1000,7 +650,7 @@
        * @return the color space
        */
       public int getColorSpace() {
  -        return (this.colorspace.getColorSpace());
  +        return getPDFColorSpace().getColorSpace();
       }
   
       /**
  @@ -1015,132 +665,6 @@
       }
   
       /**
  -     * Make a gradient
  -     *  
  -     * @param res the PDF resource context to add the shading, may be null
  -     * @param radial if true a radial gradient will be created
  -     * @param theColorspace the colorspace of the gradient
  -     * @param theColors the list of colors for the gradient
  -     * @param theBounds the list of bounds associated with the colors
  -     * @param theCoords the coordinates for the gradient
  -     * @return the PDF pattern that was created
  -     */
  -    public PDFPattern createGradient(PDFResourceContext res, boolean radial,
  -                                     PDFColorSpace theColorspace,
  -                                     List theColors, List theBounds,
  -                                     List theCoords) {
  -        PDFShading myShad;
  -        PDFFunction myfunky;
  -        PDFFunction myfunc;
  -        List theCzero;
  -        List theCone;
  -        PDFPattern myPattern;
  -        //PDFColorSpace theColorSpace;
  -        double interpolation = (double)1.000;
  -        List theFunctions = new java.util.ArrayList();
  -
  -        int currentPosition;
  -        int lastPosition = theColors.size() - 1;
  -
  -
  -        // if 5 elements, the penultimate element is 3.
  -        // do not go beyond that, because you always need
  -        // to have a next color when creating the function.
  -
  -        for (currentPosition = 0; currentPosition < lastPosition;
  -                currentPosition++) {    // for every consecutive color pair
  -            PDFColor currentColor =
  -                (PDFColor)theColors.get(currentPosition);
  -            PDFColor nextColor = (PDFColor)theColors.get(currentPosition
  -                                 + 1);
  -            // colorspace must be consistant
  -            if (this.colorspace.getColorSpace()
  -                    != currentColor.getColorSpace()) {
  -                currentColor.setColorSpace(this.colorspace.getColorSpace());
  -            }
  -
  -            if (this.colorspace.getColorSpace() != nextColor.getColorSpace()) {
  -                nextColor.setColorSpace(this.colorspace.getColorSpace());
  -            }
  -
  -            theCzero = currentColor.getVector();
  -            theCone = nextColor.getVector();
  -
  -            myfunc = this.makeFunction(2, null, null, theCzero, theCone,
  -                                       interpolation);
  -
  -            theFunctions.add(myfunc);
  -
  -        }                               // end of for every consecutive color pair
  -
  -        myfunky = this.makeFunction(3, null, null, theFunctions, theBounds,
  -                                    null);
  -
  -        if (radial) {
  -            if (theCoords.size() == 6) {
  -                myShad = this.makeShading(res, 3, this.colorspace, null, null,
  -                                          false, theCoords, null, myfunky,
  -                                          null);
  -            } else {    // if the center x, center y, and radius specifiy
  -                // the gradient, then assume the same center x, center y,
  -                // and radius of zero for the other necessary component
  -                List newCoords = new java.util.ArrayList();
  -                newCoords.add(theCoords.get(0));
  -                newCoords.add(theCoords.get(1));
  -                newCoords.add(theCoords.get(2));
  -                newCoords.add(theCoords.get(0));
  -                newCoords.add(theCoords.get(1));
  -                newCoords.add(new Double(0.0));
  -
  -                myShad = this.makeShading(res, 3, this.colorspace, null, null,
  -                                          false, newCoords, null, myfunky,
  -                                          null);
  -
  -            }
  -        } else {
  -            myShad = this.makeShading(res, 2, this.colorspace, null, null, false,
  -                                      theCoords, null, myfunky, null);
  -
  -        }
  -
  -        myPattern = this.makePattern(res, 2, myShad, null, null, null);
  -
  -        return (myPattern);
  -    }
  -
  -
  -    /**
  -     * make a /Encoding object
  -     *
  -     * @param encodingName character encoding scheme name
  -     * @return the created /Encoding object
  -     */
  -    public PDFEncoding makeEncoding(String encodingName) {
  -
  -        /*
  -         * create a PDFEncoding with the next object number and add to the
  -         * list of objects
  -         */
  -        PDFEncoding encoding = new PDFEncoding(++this.objectcount,
  -                                               encodingName);
  -        this.objects.add(encoding);
  -        return encoding;
  -    }
  -
  -    /**
  -     * Create a PDFICCStream
  -     * @see PDFXObject
  -     * @see org.apache.fop.image.JpegImage
  -     * @see org.apache.fop.pdf.PDFColorSpace
  -     * @return the new PDF ICC stream object
  -     */
  -    public PDFICCStream makePDFICCStream() {
  -        PDFICCStream iccStream = new PDFICCStream(++this.objectcount);
  -        this.objects.add(iccStream);
  -        return iccStream;
  -    }
  -
  -    /**
        * Get the font map for this document.
        *
        * @return the map of fonts used in this document
  @@ -1150,149 +674,6 @@
       }
   
       /**
  -     * make a Type1 /Font object
  -     *
  -     * @param fontname internal name to use for this font (eg "F1")
  -     * @param basefont name of the base font (eg "Helvetica")
  -     * @param encoding character encoding scheme used by the font
  -     * @param metrics additional information about the font
  -     * @param descriptor additional information about the font
  -     * @return the created /Font object
  -     */
  -    public PDFFont makeFont(String fontname, String basefont,
  -                            String encoding, FontMetrics metrics,
  -                            FontDescriptor descriptor) {
  -        if (fontMap.containsKey(fontname)) {
  -            return (PDFFont)fontMap.get(fontname);
  -        }
  -
  -        /*
  -         * create a PDFFont with the next object number and add to the
  -         * list of objects
  -         */
  -        if (descriptor == null) {
  -            PDFFont font = new PDFFont(++this.objectcount, fontname,
  -                                       FontType.TYPE1, basefont, encoding);
  -            this.objects.add(font);
  -            fontMap.put(fontname, font);
  -            return font;
  -        } else {
  -            FontType fonttype = metrics.getFontType();
  -
  -            PDFFontDescriptor pdfdesc = makeFontDescriptor(descriptor);
  -
  -            PDFFontNonBase14 font = null;
  -            if (fonttype == FontType.TYPE0) {
  -                /*
  -                 * Temporary commented out - customized CMaps
  -                 * isn't needed until /ToUnicode support is added
  -                 * PDFCMap cmap = new PDFCMap(++this.objectcount,
  -                 * "fop-ucs-H",
  -                 * new PDFCIDSystemInfo("Adobe",
  -                 * "Identity",
  -                 * 0));
  -                 * cmap.addContents();
  -                 * this.objects.add(cmap);
  -                 */
  -                font =
  -                    (PDFFontNonBase14)PDFFont.createFont(++this.objectcount,
  -                                                         fontname, fonttype,
  -                                                         basefont,
  -                                                         "Identity-H");
  -            } else {
  -
  -                font =
  -                    (PDFFontNonBase14)PDFFont.createFont(++this.objectcount,
  -                                                         fontname, fonttype,
  -                                                         basefont, encoding);
  -            }
  -            this.objects.add(font);
  -
  -            font.setDescriptor(pdfdesc);
  -
  -            if (fonttype == FontType.TYPE0) {
  -                CIDFont cidMetrics;
  -                if (metrics instanceof LazyFont) {
  -                    cidMetrics = (CIDFont)((LazyFont) metrics).getRealFont();
  -                } else {
  -                    cidMetrics = (CIDFont)metrics;
  -                }
  -                PDFCIDSystemInfo sysInfo =
  -                    new PDFCIDSystemInfo(cidMetrics.getRegistry(),
  -                                         cidMetrics.getOrdering(),
  -                                         cidMetrics.getSupplement());
  -                PDFCIDFont cidFont =
  -                    new PDFCIDFont(++this.objectcount, basefont,
  -                                   cidMetrics.getCIDType(),
  -                                   cidMetrics.getDefaultWidth(),
  -                                   cidMetrics.getWidths(), sysInfo,
  -                                   (PDFCIDFontDescriptor)pdfdesc);
  -                this.objects.add(cidFont);
  -
  -                ((PDFFontType0)font).setDescendantFonts(cidFont);
  -            } else {
  -                int firstChar = 0;
  -                int lastChar = 255;
  -                if (metrics instanceof CustomFont) {
  -                    CustomFont cf = (CustomFont)metrics;
  -                    firstChar = cf.getFirstChar();
  -                    lastChar = cf.getLastChar();
  -                }
  -                font.setWidthMetrics(firstChar,
  -                                     lastChar,
  -                                     makeArray(metrics.getWidths()));
  -            }
  -
  -            fontMap.put(fontname, font);
  -
  -            return font;
  -        }
  -    }
  -
  -
  -    /**
  -     * make a /FontDescriptor object
  -     *
  -     * @param desc the font descriptor
  -     * @return the new PDF font descriptor
  -     */
  -    public PDFFontDescriptor makeFontDescriptor(FontDescriptor desc) {
  -        PDFFontDescriptor font = null;
  -
  -        if (desc.getFontType() == FontType.TYPE0) {
  -            // CID Font
  -            font = new PDFCIDFontDescriptor(++this.objectcount,
  -                                            desc.getFontName(),
  -                                            desc.getFontBBox(),
  -                                            desc.getCapHeight(), desc.getFlags(),
  -                                            desc.getItalicAngle(),
  -                                            desc.getStemV(), null);
  -        } else {
  -            // Create normal FontDescriptor
  -            font = new PDFFontDescriptor(++this.objectcount, desc.getFontName(),
  -                                         desc.getAscender(),
  -                                         desc.getDescender(),
  -                                         desc.getCapHeight(),
  -                                         desc.getFlags(),
  -                                         new PDFRectangle(desc.getFontBBox()),
  -                                         desc.getStemV(),
  -                                         desc.getItalicAngle());
  -        }
  -        this.objects.add(font);
  -
  -        // Check if the font is embeddable
  -        if (desc.isEmbeddable()) {
  -            PDFStream stream = makeFontFile(this.objectcount + 1, desc);
  -            if (stream != null) {
  -                this.objectcount++;
  -                font.setFontFile(desc.getFontType(), stream);
  -                this.objects.add(stream);
  -            }
  -        }
  -        return font;
  -    }
  -
  -    /**
        * Resolve a URI.
        *
        * @param uri the uri to resolve
  @@ -1310,167 +691,6 @@
           }
       }
   
  -    /**
  -     * Embeds a font.
  -     * @param obj PDF object number to use
  -     * @param desc FontDescriptor of the font.
  -     * @return PDFStream The embedded font file
  -     */
  -    public PDFStream makeFontFile(int obj, FontDescriptor desc) {
  -        if (desc.getFontType() == FontType.OTHER) {
  -            throw new IllegalArgumentException("Trying to embed unsupported font 
type: "
  -                                                + desc.getFontType());
  -        } 
  -        
  -        Font tempFont;
  -        if (desc instanceof LazyFont) {
  -            tempFont = ((LazyFont)desc).getRealFont();
  -        } else {
  -            tempFont = (Font)desc;
  -        }
  -        if (!(tempFont instanceof CustomFont)) {
  -            throw new IllegalArgumentException(
  -                      "FontDescriptor must be instance of CustomFont, but is a "
  -                       + desc.getClass().getName());
  -        }
  -        CustomFont font = (CustomFont)tempFont;
  -        
  -        InputStream in = null;
  -        try {
  -            // Get file first
  -            if (font.getEmbedFileName() != null) {
  -                try {
  -                    in = resolveURI(font.getEmbedFileName());
  -                } catch (Exception e) {
  -                    getLogger().error("Failed to embed fontfile: "
  -                                       + font.getEmbedFileName() 
  -                                       + "(" + e.getMessage() + ")");
  -                }
  -            }
  -    
  -            // Get resource
  -            if (in == null && font.getEmbedResourceName() != null) {
  -                try {
  -                    in = new java.io.BufferedInputStream(
  -                            
this.getClass().getResourceAsStream(font.getEmbedResourceName()));
  -                } catch (Exception e) {
  -                    getLogger().error("Failed to embed fontresource: "
  -                                       + font.getEmbedResourceName()
  -                                       + "(" + e.getMessage() + ")");
  -                }
  -            }
  -    
  -            if (in == null) {
  -                return null;
  -            } else {
  -                try {
  -                    PDFStream embeddedFont;
  -                    if (desc.getFontType() == FontType.TYPE0) {
  -                        MultiByteFont mbfont = (MultiByteFont)font;
  -                        FontFileReader reader = new FontFileReader(in);
  -
  -                        TTFSubSetFile subset = new TTFSubSetFile();
  -                        setupLogger(subset);
  -        
  -                        byte[] subsetFont = subset.readFont(reader,
  -                                             mbfont.getTTCName(), 
mbfont.getUsedGlyphs());
  -                        // Only TrueType CID fonts are supported now
  -
  -                        embeddedFont = new PDFTTFStream(obj, subsetFont.length);
  -                        ((PDFTTFStream)embeddedFont).setData(subsetFont, 
subsetFont.length);
  -                    } else if (desc.getFontType() == FontType.TYPE1) {
  -                        PFBParser parser = new PFBParser();
  -                        PFBData pfb = parser.parsePFB(in);
  -                        embeddedFont = new PDFT1Stream(obj);
  -                        ((PDFT1Stream)embeddedFont).setData(pfb);
  -                    } else {
  -                        byte[] file = StreamUtilities.toByteArray(in, 128000);
  -                        embeddedFont = new PDFTTFStream(obj, file.length);
  -                        ((PDFTTFStream)embeddedFont).setData(file, file.length);
  -                    }
  -                    embeddedFont.addFilter("flate");
  -                    if (isEncryptionActive()) {
  -                        this.encryption.applyFilter(embeddedFont);
  -                    } else {
  -                        embeddedFont.addFilter("ascii-85");
  -                    }
  -                    
  -                    return embeddedFont;
  -                } finally {
  -                    in.close();
  -                }
  -            }
  -        } catch (IOException ioe) {
  -            //log.error("Failed to embed font [" + obj + "] "
  -            //                       + fontName + ": " + ioe.getMessage());
  -            return (PDFStream) null;
  -        }
  -    }
  -
  -/*
  -    public PDFStream getFontFile(int i) {
  -        PDFStream embeddedFont = null;
  -
  -
  -        return (PDFStream)embeddedFont;
  -    }
  -
  -
  -    public PDFStream getFontFile(int i) {
  -    }
  -
  -*/
  -    
  -
  -    /**
  -     * make an Array object (ex. Widths array for a font)
  -     *
  -     * @param values the int array values
  -     * @return the PDF Array with the int values
  -     */
  -    public PDFArray makeArray(int[] values) {
  -        PDFArray array = new PDFArray(++this.objectcount, values);
  -        this.objects.add(array);
  -        return array;
  -    }
  -
  -    /**
  -     * make an ExtGState for extra graphics options
  -     * This tries to find a GState that will setup the correct values
  -     * for the current context. If there is no suitable GState it will
  -     * create a new one.
  -     *
  -     * @param settings the settings required by the caller
  -     * @param current the current GState of the current PDF context
  -     * @return a PDF GState, either an existing GState or a new one
  -     */
  -    public PDFGState makeGState(Map settings, PDFGState current) {
  -
  -        // try to locate a gstate that has all the settings
  -        // or will inherit from the current gstate
  -        // compare "DEFAULT + settings" with "current + each gstate"
  -
  -        PDFGState wanted = new PDFGState(0);
  -        wanted.addValues(PDFGState.DEFAULT);
  -        wanted.addValues(settings);
  -
  -        PDFGState poss;
  -        for (Iterator iter = gstates.iterator(); iter.hasNext();) {
  -            PDFGState avail = (PDFGState)iter.next();
  -            poss = new PDFGState(0);
  -            poss.addValues(current);
  -            poss.addValues(avail);
  -            if (poss.equals(wanted)) {
  -                return avail;
  -            }
  -        }
  -
  -        PDFGState gstate = new PDFGState(++this.objectcount);
  -        gstate.addValues(settings);
  -        this.objects.add(gstate);
  -        gstates.add(gstate);
  -        return gstate;
  -    }
   
       /**
        * Get an image from the image map.
  @@ -1507,9 +727,9 @@
           // setup image
           img.setup(this);
           // create a new XObject
  -        xObject = new PDFXObject(++this.objectcount, ++this.xObjectCount,
  +        xObject = new PDFXObject(++this.xObjectCount,
                                    img);
  -        this.objects.add(xObject);
  +        registerObject(xObject);
           this.resources.addXObject(xObject);
           if (res != null) {
               res.getPDFResources().addXObject(xObject);
  @@ -1533,9 +753,9 @@
       public PDFFormXObject addFormXObject(PDFResourceContext res, PDFStream cont,
                                            PDFResources formres, String key) {
           PDFFormXObject xObject;
  -        xObject = new PDFFormXObject(++this.objectcount, ++this.xObjectCount,
  +        xObject = new PDFFormXObject(++this.xObjectCount,
                                    cont, formres.referencePDF());
  -        this.objects.add(xObject);
  +        registerObject(xObject);
           this.resources.addXObject(xObject);
           if (res != null) {
               res.getPDFResources().addXObject(xObject);
  @@ -1544,270 +764,6 @@
       }
   
       /**
  -     * make a /Page object
  -     *
  -     * @param resources resources object to use
  -     * @param pagewidth width of the page in points
  -     * @param pageheight height of the page in points
  -     *
  -     * @return the created /Page object
  -     */
  -    public PDFPage makePage(PDFResources resources,
  -                            int pagewidth, int pageheight) {
  -
  -        /*
  -         * create a PDFPage with the next object number, the given
  -         * resources, contents and dimensions
  -         */
  -        PDFPage page = new PDFPage(this, ++this.objectcount, resources,
  -                                   pagewidth, pageheight);
  -
  -        /* add it to the list of objects */
  -        pages.addPage(page);
  -        return page;
  -    }
  -
  -    /**
  -     * Add a completed page to the PDF document.
  -     * The page is added to the object list.
  -     *
  -     * @param page the page to add
  -     */
  -    public void addPage(PDFPage page) {
  -        /* add it to the list of objects */
  -        this.objects.add(page);
  -    }
  -
  -    private PDFLink findLink(PDFLink compare) {
  -        return (PDFLink)findPDFObject(links, compare);
  -    }
  -
  -    private PDFFileSpec findFileSpec(PDFFileSpec compare) {
  -        return (PDFFileSpec)findPDFObject(filespecs, compare);
  -    }
  -
  -    private PDFGoToRemote findGoToRemote(PDFGoToRemote compare) {
  -        return (PDFGoToRemote)findPDFObject(gotoremotes, compare);
  -    }
  -
  -    private PDFGoTo findGoTo(PDFGoTo compare) {
  -        return (PDFGoTo)findPDFObject(gotos, compare);
  -    }
  -
  -    /**
  -     * make a link object
  -     *
  -     * @param rect   the clickable rectangle
  -     * @param destination  the destination file
  -     * @param linkType the link type
  -     * @param yoffset the yoffset on the page for an internal link
  -     * @return the PDFLink object created
  -     */
  -    public PDFLink makeLink(Rectangle2D rect, String destination,
  -                            int linkType, float yoffset) {
  -
  -        //PDFLink linkObject;
  -        int index;
  -
  -        PDFLink link = new PDFLink(++this.objectcount, rect);
  -
  -        if (linkType == PDFLink.EXTERNAL) {
  -            // check destination
  -            if (destination.startsWith("http://";)) {
  -                PDFUri uri = new PDFUri(destination);
  -                link.setAction(uri);
  -            } else if (destination.endsWith(".pdf")) {    // FileSpec
  -                PDFGoToRemote remote = getGoToPDFAction(destination, null, -1);
  -                link.setAction(remote);
  -            } else if ((index = destination.indexOf(".pdf#page=")) > 0) {
  -                //String file = destination.substring(0, index + 4);
  -                int page = Integer.parseInt(destination.substring(index + 10));
  -                PDFGoToRemote remote = getGoToPDFAction(destination, null, page);
  -                link.setAction(remote);
  -            } else if ((index = destination.indexOf(".pdf#dest=")) > 0) {
  -                //String file = destination.substring(0, index + 4);
  -                String dest = destination.substring(index + 10);
  -                PDFGoToRemote remote = getGoToPDFAction(destination, dest, -1);
  -                link.setAction(remote);
  -            } else {                               // URI
  -                PDFUri uri = new PDFUri(destination);
  -                link.setAction(uri);
  -            }
  -        } else {
  -            // linkType is internal
  -            String goToReference = getGoToReference(destination, yoffset);
  -            PDFInternalLink internalLink = new PDFInternalLink(goToReference);
  -            link.setAction(internalLink);
  -        }
  -
  -        PDFLink oldlink = findLink(link);
  -        if (oldlink == null) {
  -            links.add(link);
  -            this.objects.add(link);
  -        } else {
  -            this.objectcount--;
  -            link = oldlink;
  -        }
  -
  -        return link;
  -    }
  -
  -    /**
  -     * Create and return a goto pdf document action.
  -     * This creates a pdf files spec and pdf goto remote action.
  -     * It also checks available pdf objects so it will not create an
  -     * object if it already exists.
  -     *
  -     * @param file the pdf file name
  -     * @param dest the remote name destination, may be null
  -     * @param page the remote page number, -1 means not specified
  -     * @return the pdf goto remote object
  -     */
  -    private PDFGoToRemote getGoToPDFAction(String file, String dest, int page) {
  -        PDFFileSpec fileSpec = new PDFFileSpec(++this.objectcount, file);
  -        PDFFileSpec oldspec = findFileSpec(fileSpec);
  -        if (oldspec == null) {
  -            filespecs.add(fileSpec);
  -            this.objects.add(fileSpec);
  -        } else {
  -            this.objectcount--;
  -            fileSpec = oldspec;
  -        }
  -        PDFGoToRemote remote;
  -
  -        if (dest == null && page == -1) {
  -            remote = new PDFGoToRemote(++this.objectcount, fileSpec);
  -        } else if (dest != null) {
  -            remote = new PDFGoToRemote(++this.objectcount, fileSpec, dest);
  -        } else {
  -            remote = new PDFGoToRemote(++this.objectcount, fileSpec, page);
  -        }
  -        PDFGoToRemote oldremote = findGoToRemote(remote);
  -        if (oldremote == null) {
  -            gotoremotes.add(remote);
  -            this.objects.add(remote);
  -        } else {
  -            this.objectcount--;
  -            remote = oldremote;
  -        }
  -        return remote;
  -    }
  -
  -    private String getGoToReference(String destination, float yoffset) {
  -        String goToReference = null;
  -        PDFGoTo gt = new PDFGoTo(++this.objectcount, destination);
  -        gt.setYPosition(yoffset);
  -        PDFGoTo oldgt = findGoTo(gt);
  -        if (oldgt == null) {
  -            gotos.add(gt);
  -            addTrailerObject(gt);
  -        } else {
  -            this.objectcount--;
  -            gt = oldgt;
  -        }
  -
  -        goToReference = gt.referencePDF();
  -        return goToReference;
  -    }
  -
  -    /**
  -     * Add trailer object.
  -     * Adds an object to the list of trailer objects.
  -     *
  -     * @param object the PDF object to add
  -     */
  -    public void addTrailerObject(PDFObject object) {
  -        this.trailerObjects.add(object);
  -    }
  -
  -    /**
  -     * Make an internal link.
  -     *
  -     * @param rect the hotspot position in absolute coordinates
  -     * @param page the target page reference value
  -     * @param dest the position destination
  -     * @return the new PDF link object
  -     */
  -    public PDFLink makeLink(Rectangle2D rect, String page, String dest) {
  -        PDFLink link = new PDFLink(++this.objectcount, rect);
  -        this.objects.add(link);
  -
  -        PDFGoTo gt = new PDFGoTo(++this.objectcount, page);
  -        gt.setDestination(dest);
  -        addTrailerObject(gt);
  -        PDFInternalLink internalLink = new PDFInternalLink(gt.referencePDF());
  -        link.setAction(internalLink);
  -
  -        return link;
  -    }
  -
  -    /**
  -     * Ensure there is room in the locations xref for the number of
  -     * objects that have been created.
  -     */
  -    private void prepareLocations() {
  -        while (location.size() < objectcount) {
  -            location.add(LOCATION_PLACEHOLDER);
  -        }
  -    }
  -
  -    /**
  -     * Make a stream object
  -     *
  -     * @param type the type of stream to be created
  -     * @param add if true then the stream will be added immediately
  -     * @return the stream object created
  -     */
  -    public PDFStream makeStream(String type, boolean add) {
  -
  -        // create a PDFStream with the next object number 
  -        // and add it to the list of objects
  -        PDFStream obj = new PDFStream(++this.objectcount);
  -        obj.addDefaultFilters(filterMap, type);
  -        if (isEncryptionActive()) {
  -            this.encryption.applyFilter(obj);
  -        }
  -
  -        if (add) {
  -            this.objects.add(obj);
  -        }
  -        return obj;
  -    }
  -
  -    /**
  -     * add a stream object
  -     *
  -     * @param obj the PDF Stream to add to this document
  -     */
  -    public void addStream(PDFStream obj) {
  -        this.objects.add(obj);
  -    }
  -
  -    /**
  -     * make an annotation list object
  -     *
  -     * @return the annotation list object created
  -     */
  -    public PDFAnnotList makeAnnotList() {
  -
  -        /*
  -         * create a PDFAnnotList with the next object number and add it
  -         * to the list of objects
  -         */
  -        PDFAnnotList obj = new PDFAnnotList(++this.objectcount);
  -        return obj;
  -    }
  -
  -    /**
  -     * Add an annotation list object to the pdf document
  -     *
  -     * @param obj the annotation list to add
  -     */
  -    public void addAnnotList(PDFAnnotList obj) {
  -        this.objects.add(obj);
  -    }
  -
  -    /**
        * Get the root Outlines object. This method does not write
        * the outline to the PDF document, it simply creates a
        * reference for later.
  @@ -1819,35 +775,14 @@
               return outlineRoot;
           }
   
  -        outlineRoot = new PDFOutline(++this.objectcount, null, null);
  +        outlineRoot = new PDFOutline(null, null);
  +        assignObjectNumber(outlineRoot);
           addTrailerObject(outlineRoot);
           root.setRootOutline(outlineRoot);
           return outlineRoot;
       }
   
       /**
  -     * Make an outline object and add it to the given outline
  -     *
  -     * @param parent parent PDFOutline object which may be null
  -     * @param label the title for the new outline object
  -     * @param destination the reference string for the action to go to
  -     * @param yoffset the yoffset on the destination page
  -     * @return the new PDF outline object
  -     */
  -    public PDFOutline makeOutline(PDFOutline parent, String label,
  -                                  String destination, float yoffset) {
  -        String goToRef = getGoToReference(destination, yoffset);
  -
  -        PDFOutline obj = new PDFOutline(++this.objectcount, label, goToRef);
  -
  -        if (parent != null) {
  -            parent.addOutline(obj);
  -        }
  -        this.objects.add(obj);
  -        return obj;
  -    }
  -
  -    /**
        * get the /Resources object for the document
        *
        * @return the /Resources object
  @@ -1857,25 +792,34 @@
       }
   
       /**
  +     * Ensure there is room in the locations xref for the number of
  +     * objects that have been created.
  +     */
  +    private void setLocation(int objidx, int position) {
  +        while (location.size() <= objidx) {
  +            location.add(LOCATION_PLACEHOLDER);
  +        }
  +        location.set(objidx, new Integer(position));
  +    }
  +
  +    /**
        * write the entire document out
        *
        * @param stream the OutputStream to output the document to
        * @throws IOException if there is an exception writing to the output stream
        */
       public void output(OutputStream stream) throws IOException {
  -
  -        prepareLocations();
  -
  -        for (int count = 0; count < this.objects.size(); count++) {
  -            /* retrieve the object with the current number */
  -            PDFObject object = (PDFObject)this.objects.get(count);
  -
  +        //Write out objects until the list is empty. This approach (used with a
  +        //LinkedList) allows for output() methods to create and register objects
  +        //on the fly even during serialization.
  +        while (this.objects.size() > 0) {
  +            /* Retrieve first */
  +            PDFObject object = (PDFObject)this.objects.remove(0);
               /*
                * add the position of this object to the list of object
                * locations
                */
  -            location.set(object.getNumber() - 1,
  -                         new Integer(this.position));
  +            setLocation(object.getObjectNumber() - 1, this.position);
   
               /*
                * output the object and increment the character position
  @@ -1884,7 +828,8 @@
               this.position += object.output(stream);
           }
   
  -        this.objects.clear();
  +        //Clear all objects written to the file
  +        //this.objects.clear();
       }
   
       /**
  @@ -1924,7 +869,7 @@
           output(stream);
           for (int count = 0; count < trailerObjects.size(); count++) {
               PDFObject o = (PDFObject) trailerObjects.get(count);
  -            this.location.set(o.getNumber() - 1,
  +            this.location.set(o.getObjectNumber() - 1,
                                 new Integer(this.position));
               this.position += o.output(stream);
           }
  @@ -1941,16 +886,14 @@
           /* construct the trailer */
           String pdf = "trailer\n" + "<<\n"
                        + "/Size " + (this.objectcount + 1) + "\n"
  -                     + "/Root " + this.root.number + " "
  -                     + this.root.generation + " R\n" 
  -                     + "/Info " + this.info.number + " " 
  -                     + this.info.generation + " R\n" 
  +                     + "/Root " + this.root.referencePDF() + "\n" 
  +                     + "/Info " + this.info.referencePDF() + "\n" 
                        + encryptEntry
                        + ">>\n" + "startxref\n" + this.xref
                        + "\n" + "%%EOF\n";
   
           /* write the trailer */
  -        stream.write(pdf.getBytes());
  +        stream.write(encode(pdf));
       }
   
       /**
  @@ -1965,9 +908,10 @@
           this.xref = this.position;
   
           /* construct initial part of xref */
  -        StringBuffer pdf = new StringBuffer("xref\n0 "
  -                                            + (this.objectcount + 1)
  -                                            + "\n0000000000 65535 f \n");
  +        StringBuffer pdf = new StringBuffer(128);
  +        pdf.append("xref\n0 "
  +            + (this.objectcount + 1)
  +            + "\n0000000000 65535 f \n");
   
           for (int count = 0; count < this.location.size(); count++) {
               String x = this.location.get(count).toString();
  @@ -1981,7 +925,7 @@
           }
   
           /* write the xref table and return the character length */
  -        byte[] pdfBytes = pdf.toString().getBytes();
  +        byte[] pdfBytes = encode(pdf.toString());
           stream.write(pdfBytes);
           return pdfBytes.length;
       }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to