You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-commits@xmlgraphics.apache.org by vh...@apache.org on 2012/04/05 18:20:17 UTC

svn commit: r1309921 [27/42] - in /xmlgraphics/fop/branches/Temp_TrueTypeInPostScript: ./ examples/embedding/ examples/embedding/java/embedding/ examples/embedding/java/embedding/atxml/ examples/embedding/java/embedding/tools/ examples/plan/src/org/apa...

Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFDocument.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFDocument.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFDocument.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFDocument.java Thu Apr  5 16:19:19 2012
@@ -24,12 +24,9 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
@@ -41,6 +38,10 @@ import java.util.Map;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
+import org.apache.fop.pdf.xref.CrossReferenceStream;
+import org.apache.fop.pdf.xref.CrossReferenceTable;
+import org.apache.fop.pdf.xref.TrailerDictionary;
+
 /* image support modified from work of BoBoGi */
 /* font support based on work by Takayuki Takeuchi */
 
@@ -67,40 +68,31 @@ import org.apache.commons.logging.LogFac
  */
 public class PDFDocument {
 
-    private static final Integer LOCATION_PLACEHOLDER = new Integer(0);
-
-    /** Integer constant to represent PDF 1.3 */
-    public static final int PDF_VERSION_1_3 = 3;
-
-    /** Integer constant to represent PDF 1.4 */
-    public static final int PDF_VERSION_1_4 = 4;
-
     /** the encoding to use when converting strings to PDF commands */
     public static final String ENCODING = "ISO-8859-1";
 
     /** the counter for object numbering */
-    protected int objectcount = 0;
+    protected int objectcount;
 
     /** the logger instance */
     private Log log = LogFactory.getLog("org.apache.fop.pdf");
 
     /** the current character position */
-    private int position = 0;
-
-    /** character position of xref table */
-    private int xref;
+    private long position;
 
     /** the character position of each object */
-    private List location = new ArrayList();
+    private List<Long> indirectObjectOffsets = new ArrayList<Long>();
+
+    private Collection<PDFStructElem> structureTreeElements;
 
     /** List of objects to write in the trailer */
-    private List trailerObjects = new ArrayList();
+    private List<PDFObject> trailerObjects = new ArrayList<PDFObject>();
 
     /** the objects themselves */
-    private List objects = new LinkedList();
+    private List<PDFObject> objects = new LinkedList<PDFObject>();
 
-    /** Indicates what PDF version is active */
-    private int pdfVersion = PDF_VERSION_1_4;
+    /** Controls the PDF version of this document */
+    private VersionController versionController;
 
     /** Indicates which PDF profiles are active (PDF/A, PDF/X etc.) */
     private PDFProfile pdfProfile = new PDFProfile(this);
@@ -109,7 +101,7 @@ public class PDFDocument {
     private PDFRoot root;
 
     /** The root outline object */
-    private PDFOutline outlineRoot = null;
+    private PDFOutline outlineRoot;
 
     /** The /Pages object (mark-fop@inomial.com) */
     private PDFPages pages;
@@ -128,63 +120,46 @@ public class PDFDocument {
         = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
 
     /** the counter for Pattern name numbering (e.g. 'Pattern1') */
-    private int patternCount = 0;
+    private int patternCount;
 
     /** the counter for Shading name numbering */
-    private int shadingCount = 0;
+    private int shadingCount;
 
     /** the counter for XObject numbering */
-    private int xObjectCount = 0;
+    private int xObjectCount;
 
-    /** the {@link PDFXObject}s map */
     /* TODO: Should be modified (works only for image subtype) */
-    private Map xObjectsMap = new HashMap();
+    private Map<String, PDFXObject> xObjectsMap = new HashMap<String, PDFXObject>();
 
-    /** The {@link PDFFont} map */
-    private Map fontMap = new HashMap();
+    private Map<String, PDFFont> fontMap = new HashMap<String, PDFFont>();
 
-    /** The {@link PDFFilter} map */
-    private Map filterMap = new HashMap();
+    private Map<String, List<String>> filterMap = new HashMap<String, List<String>>();
 
-    /** List of {@link PDFGState}s. */
-    private List gstates = new ArrayList();
+    private List<PDFGState> gstates = new ArrayList<PDFGState>();
 
-    /** List of {@link PDFFunction}s. */
-    private List functions = new ArrayList();
+    private List<PDFFunction> functions = new ArrayList<PDFFunction>();
 
-    /** List of {@link PDFShading}s. */
-    private List shadings = new ArrayList();
+    private List<PDFShading> shadings = new ArrayList<PDFShading>();
 
-    /** List of {@link PDFPattern}s. */
-    private List patterns = new ArrayList();
+    private List<PDFPattern> patterns = new ArrayList<PDFPattern>();
 
-    /** List of {@link PDFLink}s. */
-    private List links = new ArrayList();
+    private List<PDFLink> links = new ArrayList<PDFLink>();
 
-    /** List of {@link PDFDestination}s. */
-    private List destinations;
+    private List<PDFDestination> destinations;
 
-    /** List of {@link PDFFileSpec}s. */
-    private List filespecs = new ArrayList();
+    private List<PDFFileSpec> filespecs = new ArrayList<PDFFileSpec>();
 
-    /** List of {@link PDFGoToRemote}s. */
-    private List gotoremotes = new ArrayList();
+    private List<PDFGoToRemote> gotoremotes = new ArrayList<PDFGoToRemote>();
 
-    /** List of {@link PDFGoTo}s. */
-    private List gotos = new ArrayList();
+    private List<PDFGoTo> gotos = new ArrayList<PDFGoTo>();
 
-    /** List of {@link PDFLaunch}es. */
-    private List launches = new ArrayList();
-
-    /**
-     * The PDFDests object for the name dictionary.
-     * Note: This object is not a list.
-     */
-    private PDFDests dests;
+    private List<PDFLaunch> launches = new ArrayList<PDFLaunch>();
 
     private PDFFactory factory;
 
-    private boolean encodingOnTheFly = true;
+    private FileIDGenerator fileIDGenerator;
+
+    private boolean accessibilityEnabled;
 
     /**
      * Creates an empty PDF document.
@@ -199,6 +174,24 @@ public class PDFDocument {
      * @param prod the name of the producer of this pdf document
      */
     public PDFDocument(String prod) {
+        this(prod, null);
+        versionController = VersionController.getDynamicVersionController(Version.V1_4, this);
+    }
+
+    /**
+     * Creates an empty PDF document.
+     *
+     * The constructor creates a /Root and /Pages object to
+     * track the document but does not write these objects until
+     * the trailer is written. Note that the object ID of the
+     * pages object is determined now, and the xref table is
+     * updated later. This allows Pages to refer to their
+     * Parent before we write it out.
+     *
+     * @param prod the name of the producer of this pdf document
+     * @param versionController the version controller of this PDF document
+     */
+    public PDFDocument(String prod, VersionController versionController) {
 
         this.factory = new PDFFactory(this);
 
@@ -213,26 +206,32 @@ public class PDFDocument {
 
         // Make the /Info record
         this.info = getFactory().makeInfo(prod);
+
+        this.versionController = versionController;
+    }
+
+    /**
+     * Returns the current PDF version.
+     *
+     * @return returns the PDF version
+     */
+    public Version getPDFVersion() {
+        return versionController.getPDFVersion();
     }
 
     /**
-     * @return the integer representing the active PDF version
-     *          (one of PDFDocument.PDF_VERSION_*)
+     * Sets the PDF version of this document.
+     *
+     * @param version the PDF version
+     * @throws IllegalStateException if the version of this PDF is not allowed to change.
      */
-    public int getPDFVersion() {
-        return this.pdfVersion;
+    public void setPDFVersion(Version version) {
+        versionController.setPDFVersion(version);
     }
 
-    /** @return the String representing the active PDF version */
+    /** @return the String representing the current PDF version */
     public String getPDFVersionString() {
-        switch (getPDFVersion()) {
-        case PDF_VERSION_1_3:
-            return "1.3";
-        case PDF_VERSION_1_4:
-            return "1.4";
-        default:
-            throw new IllegalStateException("Unsupported PDF version selected");
-        }
+        return versionController.getPDFVersion().toString();
     }
 
     /** @return the PDF profile currently active. */
@@ -250,17 +249,6 @@ public class PDFDocument {
     }
 
     /**
-     * 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 <code>true</code> if on-the-fly encoding is enabled
-     */
-    public boolean isEncodingOnTheFly() {
-        return this.encodingOnTheFly;
-    }
-
-    /**
      * Converts text to a byte array for writing to a PDF file.
      *
      * @param text text to convert/encode
@@ -275,20 +263,17 @@ public class PDFDocument {
     }
 
     /**
-     * Creates and returns a Writer object wrapping the given OutputStream. The Writer is
-     * buffered to reduce the number of calls to the encoding converter so don't forget
-     * to <code>flush()</code> the Writer after use or before writing directly to the
-     * underlying OutputStream.
-     *
-     * @param out the OutputStream to write to
-     * @return the requested Writer
-     */
-    public static Writer getWriterFor(OutputStream out) {
-        try {
-            return new java.io.BufferedWriter(new java.io.OutputStreamWriter(out, ENCODING));
-        } catch (UnsupportedEncodingException uee) {
-            throw new Error("JVM doesn't support " + ENCODING + " encoding!");
-        }
+     * Flushes the given text buffer to an output stream with the right encoding and resets
+     * the text buffer. This is used to efficiently switch between outputting text and binary
+     * content.
+     * @param textBuffer the text buffer
+     * @param out the output stream to flush the text content to
+     * @throws IOException if an I/O error occurs while writing to the output stream
+     */
+    public static void flushTextBuffer(StringBuilder textBuffer, OutputStream out)
+            throws IOException {
+        out.write(encode(textBuffer.toString()));
+        textBuffer.setLength(0);
     }
 
     /**
@@ -323,7 +308,7 @@ public class PDFDocument {
      *
      * @param map the map of filter lists for each stream type
      */
-    public void setFilterMap(Map map) {
+    public void setFilterMap(Map<String, List<String>> map) {
         this.filterMap = map;
     }
 
@@ -332,7 +317,7 @@ public class PDFDocument {
      *
      * @return the map of filters being used
      */
-    public Map getFilterMap() {
+    public Map<String, List<String>> getFilterMap() {
         return this.filterMap;
     }
 
@@ -355,22 +340,34 @@ public class PDFDocument {
     }
 
     /**
-     * Makes sure a Lang entry has been set on the document catalog, setting it
-     * to a default value if necessary. When accessibility is enabled the
-     * language must be specified for any text element in the document.
-     */
-    public void enforceLanguageOnRoot() {
-        if (root.getLanguage() == null) {
-            String fallbackLanguage;
-            if (getProfile().getPDFAMode().isPDFA1LevelA()) {
-                //According to Annex B of ISO-19005-1:2005(E), section B.2
-                fallbackLanguage = "x-unknown";
-            } else {
-                //No language has been set on the first page-sequence, so fall back to "en".
-                fallbackLanguage = "en";
-            }
-            root.setLanguage(fallbackLanguage);
-        }
+     * Creates and returns a StructTreeRoot object.
+     *
+     * @param parentTree the value of the ParenTree entry
+     * @return the structure tree root
+     */
+    public PDFStructTreeRoot makeStructTreeRoot(PDFParentTree parentTree) {
+        PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(parentTree);
+        assignObjectNumber(structTreeRoot);
+        addTrailerObject(structTreeRoot);
+        root.setStructTreeRoot(structTreeRoot);
+        structureTreeElements = new ArrayList<PDFStructElem>();
+        return structTreeRoot;
+    }
+
+    /**
+     * Creates and returns a structure element.
+     *
+     * @param structureType the structure type of the new element (value for the
+     * S entry)
+     * @param parent the parent of the new structure element in the structure
+     * hierarchy
+     * @return a dictionary of type StructElem
+     */
+    public PDFStructElem makeStructureElement(PDFName structureType, PDFObject parent) {
+        PDFStructElem structElem = new PDFStructElem(parent, structureType);
+        assignObjectNumber(structElem);
+        structureTreeElements.add(structElem);
+        return structElem;
     }
 
     /**
@@ -445,39 +442,39 @@ public class PDFDocument {
 
         //Add object to special lists where necessary
         if (obj instanceof PDFFunction) {
-            this.functions.add(obj);
+            this.functions.add((PDFFunction) obj);
         }
         if (obj instanceof PDFShading) {
             final String shadingName = "Sh" + (++this.shadingCount);
             ((PDFShading)obj).setName(shadingName);
-            this.shadings.add(obj);
+            this.shadings.add((PDFShading) obj);
         }
         if (obj instanceof PDFPattern) {
             final String patternName = "Pa" + (++this.patternCount);
             ((PDFPattern)obj).setName(patternName);
-            this.patterns.add(obj);
+            this.patterns.add((PDFPattern) obj);
         }
         if (obj instanceof PDFFont) {
             final PDFFont font = (PDFFont)obj;
             this.fontMap.put(font.getName(), font);
         }
         if (obj instanceof PDFGState) {
-            this.gstates.add(obj);
+            this.gstates.add((PDFGState) obj);
         }
         if (obj instanceof PDFPage) {
             this.pages.notifyKidRegistered((PDFPage)obj);
         }
         if (obj instanceof PDFLaunch) {
-            this.launches.add(obj);
+            this.launches.add((PDFLaunch) obj);
         }
         if (obj instanceof PDFLink) {
-            this.links.add(obj);
+            this.links.add((PDFLink) obj);
         }
         if (obj instanceof PDFFileSpec) {
-            this.filespecs.add(obj);
+            this.filespecs.add((PDFFileSpec) obj);
         }
         if (obj instanceof PDFGoToRemote) {
-            this.gotoremotes.add(obj);
+            this.gotoremotes.add((PDFGoToRemote) obj);
         }
     }
 
@@ -491,7 +488,7 @@ public class PDFDocument {
         this.trailerObjects.add(obj);
 
         if (obj instanceof PDFGoTo) {
-            this.gotos.add(obj);
+            this.gotos.add((PDFGoTo) obj);
         }
     }
 
@@ -513,10 +510,10 @@ public class PDFDocument {
      */
     public void setEncryption(PDFEncryptionParams params) {
         getProfile().verifyEncryptionAllowed();
-        this.encryption = PDFEncryptionManager.newInstance(++this.objectcount, params);
+        fileIDGenerator = FileIDGenerator.getRandomFileIDGenerator();
+        this.encryption = PDFEncryptionManager.newInstance(++this.objectcount, params, this);
         if (this.encryption != null) {
             PDFObject pdfObject = (PDFObject)this.encryption;
-            pdfObject.setDocument(this);
             addTrailerObject(pdfObject);
         } else {
             log.warn(
@@ -543,9 +540,8 @@ public class PDFDocument {
         return this.encryption;
     }
 
-    private Object findPDFObject(List list, PDFObject compare) {
-        for (Iterator iter = list.iterator(); iter.hasNext();) {
-            PDFObject obj = (PDFObject) iter.next();
+    private Object findPDFObject(List<? extends PDFObject> list, PDFObject compare) {
+        for (PDFObject obj : list) {
             if (compare.contentEquals(obj)) {
                 return obj;
             }
@@ -595,7 +591,7 @@ public class PDFDocument {
      * @return PDFFont the requested font, null if it wasn't found
      */
     protected PDFFont findFont(String fontname) {
-        return (PDFFont)this.fontMap.get(fontname);
+        return this.fontMap.get(fontname);
     }
 
     /**
@@ -607,7 +603,7 @@ public class PDFDocument {
     protected PDFDestination findDestination(PDFDestination compare) {
         int index = getDestinationList().indexOf(compare);
         if (index >= 0) {
-            return (PDFDestination)getDestinationList().get(index);
+            return getDestinationList().get(index);
         } else {
             return null;
         }
@@ -672,9 +668,9 @@ public class PDFDocument {
      */
     protected PDFGState findGState(PDFGState wanted, PDFGState current) {
         PDFGState poss;
-        Iterator iter = this.gstates.iterator();
+        Iterator<PDFGState> iter = this.gstates.iterator();
         while (iter.hasNext()) {
-            PDFGState avail = (PDFGState)iter.next();
+            PDFGState avail = iter.next();
             poss = new PDFGState();
             poss.addValues(current);
             poss.addValues(avail);
@@ -718,7 +714,7 @@ public class PDFDocument {
      *
      * @return the map of fonts used in this document
      */
-    public Map getFontMap() {
+    public Map<String, PDFFont> getFontMap() {
         return this.fontMap;
     }
 
@@ -747,6 +743,7 @@ public class PDFDocument {
      * @return the image or PDFXObject for the key if found
      * @deprecated Use getXObject instead (so forms are treated in the same way)
      */
+    @Deprecated
     public PDFImageXObject getImage(String key) {
         return (PDFImageXObject)this.xObjectsMap.get(key);
     }
@@ -758,16 +755,7 @@ public class PDFDocument {
      * @return the PDFXObject for the key if found
      */
     public PDFXObject getXObject(String key) {
-        return (PDFXObject)this.xObjectsMap.get(key);
-    }
-
-    /**
-     * Gets the PDFDests object (which represents the /Dests entry).
-     *
-     * @return the PDFDests object (which represents the /Dests entry).
-     */
-    public PDFDests getDests() {
-        return this.dests;
+        return this.xObjectsMap.get(key);
     }
 
     /**
@@ -776,7 +764,7 @@ public class PDFDocument {
      */
     public void addDestination(PDFDestination destination) {
         if (this.destinations == null) {
-            this.destinations = new ArrayList();
+            this.destinations = new ArrayList<PDFDestination>();
         }
         this.destinations.add(destination);
     }
@@ -786,11 +774,11 @@ public class PDFDocument {
      *
      * @return the list of named destinations.
      */
-    public List getDestinationList() {
+    public List<PDFDestination> getDestinationList() {
         if (hasDestinations()) {
             return this.destinations;
         } else {
-            return Collections.EMPTY_LIST;
+            return Collections.emptyList();
         }
     }
 
@@ -905,17 +893,8 @@ public class PDFDocument {
         return this.resources;
     }
 
-    /**
-     * Ensure there is room in the locations xref for the number of
-     * objects that have been created.
-     * @param objidx    the object's index
-     * @param position  the position
-     */
-    private void setLocation(int objidx, int position) {
-        while (this.location.size() <= objidx) {
-            this.location.add(LOCATION_PLACEHOLDER);
-        }
-        this.location.set(objidx, new Integer(position));
+    public void enableAccessibility(boolean enableAccessibility) {
+        this.accessibilityEnabled = enableAccessibility;
     }
 
     /**
@@ -929,23 +908,50 @@ public class PDFDocument {
         //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
-             */
-            setLocation(object.getObjectNumber() - 1, this.position);
-
-            /*
-             * output the object and increment the character position
-             * by the object's length
-             */
-            this.position += object.output(stream);
+            PDFObject object = this.objects.remove(0);
+            streamIndirectObject(object, stream);
+        }
+    }
+
+    private void streamIndirectObject(PDFObject o, OutputStream stream) throws IOException {
+        recordObjectOffset(o);
+        this.position += outputIndirectObject(o, stream);
+    }
+
+    private void streamIndirectObjects(Collection<? extends PDFObject> objects, OutputStream stream)
+            throws IOException {
+        for (PDFObject o : objects) {
+            streamIndirectObject(o, stream);
+        }
+    }
+
+    private void recordObjectOffset(PDFObject object) {
+        int index = object.getObjectNumber() - 1;
+        while (indirectObjectOffsets.size() <= index) {
+            indirectObjectOffsets.add(null);
         }
+        indirectObjectOffsets.set(index, position);
+    }
 
-        //Clear all objects written to the file
-        //this.objects.clear();
+    /**
+     * Outputs the given object, wrapped by obj/endobj, to the given stream.
+     *
+     * @param object an indirect object, as described in Section 3.2.9 of the PDF 1.5
+     * Reference.
+     * @param stream the stream to which the object must be output
+     * @throws IllegalArgumentException if the object is not an indirect object
+     */
+    public static int outputIndirectObject(PDFObject object, OutputStream stream)
+            throws IOException {
+        if (!object.hasObjectNumber()) {
+            throw new IllegalArgumentException("Not an indirect object");
+        }
+        byte[] obj = encode(object.getObjectID());
+        stream.write(obj);
+        int length = object.output(stream);
+        byte[] endobj = encode("\nendobj\n");
+        stream.write(endobj);
+        return obj.length + length + endobj.length;
     }
 
     /**
@@ -978,27 +984,6 @@ public class PDFDocument {
         this.position += bin.length;
     }
 
-    /** @return the "ID" entry for the file trailer */
-    protected String getIDEntry() {
-        try {
-            MessageDigest digest = MessageDigest.getInstance("MD5");
-            DateFormat df = new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS");
-            digest.update(encode(df.format(new Date())));
-            //Ignoring the filename here for simplicity even though it's recommended by the PDF spec
-            digest.update(encode(String.valueOf(this.position)));
-            digest.update(getInfo().toPDF());
-            byte[] res = digest.digest();
-            String s = PDFText.toHex(res);
-            return "/ID [" + s + " " + s + "]";
-        } catch (NoSuchAlgorithmException e) {
-            if (getProfile().isIDEntryRequired()) {
-                throw new UnsupportedOperationException("MD5 not available: " + e.getMessage());
-            } else {
-                return ""; //Entry is optional if PDF/A or PDF/X are not active
-            }
-        }
-    }
-
     /**
      * Write the trailer
      *
@@ -1006,83 +991,116 @@ public class PDFDocument {
      * @throws IOException if there is an exception writing to the output stream
      */
     public void outputTrailer(OutputStream stream) throws IOException {
+        createDestinations();
+        output(stream);
+        outputTrailerObjectsAndXref(stream);
+    }
+
+    private void createDestinations() {
         if (hasDestinations()) {
             Collections.sort(this.destinations, new DestinationComparator());
-            this.dests = getFactory().makeDests(this.destinations);
+            PDFDests dests = getFactory().makeDests(this.destinations);
             if (this.root.getNames() == null) {
                 this.root.setNames(getFactory().makeNames());
             }
             this.root.getNames().setDests(dests);
         }
-        output(stream);
-        for (int count = 0; count < this.trailerObjects.size(); count++) {
-            PDFObject o = (PDFObject)this.trailerObjects.get(count);
-            setLocation(o.getObjectNumber() - 1, this.position);
-            this.position += o.output(stream);
-        }
-        /* output the xref table and increment the character position
-          by the table's length */
-        this.position += outputXref(stream);
-
-        /* construct the trailer */
-        StringBuffer pdf = new StringBuffer(128);
-        pdf.append("trailer\n<<\n/Size ")
-                .append(this.objectcount + 1)
-                .append("\n/Root ")
-                .append(this.root.referencePDF())
-                .append("\n/Info ")
-                .append(this.info.referencePDF())
-                .append('\n');
+    }
 
-        if (this.isEncryptionActive()) {
-            pdf.append(this.encryption.getTrailerEntry());
-        } else {
-            pdf.append(this.getIDEntry());
+    private void outputTrailerObjectsAndXref(OutputStream stream) throws IOException {
+        TrailerOutputHelper trailerOutputHelper = mayCompressStructureTreeElements()
+                ? new CompressedTrailerOutputHelper()
+                : new UncompressedTrailerOutputHelper();
+        if (structureTreeElements != null) {
+            trailerOutputHelper.outputStructureTreeElements(stream);
+        }
+        streamIndirectObjects(trailerObjects, stream);
+        TrailerDictionary trailerDictionary = createTrailerDictionary();
+        long startxref = trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary);
+        String trailer = "startxref\n" + startxref + "\n%%EOF\n";
+        stream.write(encode(trailer));
+    }
+
+    private boolean mayCompressStructureTreeElements() {
+        return accessibilityEnabled
+                && versionController.getPDFVersion().compareTo(Version.V1_5) >= 0;
+    }
+
+    private TrailerDictionary createTrailerDictionary() {
+        FileIDGenerator gen = getFileIDGenerator();
+        TrailerDictionary trailerDictionary = new TrailerDictionary(this)
+                .setRoot(root)
+                .setInfo(info)
+                .setFileID(gen.getOriginalFileID(), gen.getUpdatedFileID());
+        if (isEncryptionActive()) {
+            trailerDictionary.setEncryption(encryption);
         }
+        return trailerDictionary;
+    }
+
+    private interface TrailerOutputHelper {
 
-        pdf.append("\n>>\nstartxref\n")
-                .append(this.xref)
-                .append("\n%%EOF\n");
+        void outputStructureTreeElements(OutputStream stream) throws IOException;
 
-        /* write the trailer */
-        stream.write(encode(pdf.toString()));
+        /**
+         * @return the offset of the cross-reference object (the value of startxref)
+         */
+        long outputCrossReferenceObject(OutputStream stream, TrailerDictionary trailerDictionary)
+                throws IOException;
     }
 
-    /**
-     * Write the xref table
-     *
-     * @param stream the OutputStream to write the xref table to
-     * @return the number of characters written
-     * @throws IOException in case of an error writing the result to
-     *          the parameter stream
-     */
-    private int outputXref(OutputStream stream) throws IOException {
+    private class UncompressedTrailerOutputHelper implements TrailerOutputHelper {
 
-        /* remember position of xref table */
-        this.xref = this.position;
+        public void outputStructureTreeElements(OutputStream stream)
+                throws IOException {
+            streamIndirectObjects(structureTreeElements, stream);
+        }
 
-        /* construct initial part of xref */
-        StringBuffer pdf = new StringBuffer(128);
-        pdf.append("xref\n0 ");
-        pdf.append(this.objectcount + 1);
-        pdf.append("\n0000000000 65535 f \n");
+        public long outputCrossReferenceObject(OutputStream stream,
+                TrailerDictionary trailerDictionary) throws IOException {
+            new CrossReferenceTable(trailerDictionary, position,
+                    indirectObjectOffsets).output(stream);
+            return position;
+        }
+    }
 
-        String s, loc;
-        for (int count = 0; count < this.location.size(); count++) {
-            final String padding = "0000000000";
-            s = this.location.get(count).toString();
+    private class CompressedTrailerOutputHelper implements TrailerOutputHelper {
 
-            /* contruct xref entry for object */
-            loc = padding.substring(s.length()) + s;
+        private ObjectStreamManager structureTreeObjectStreams;
 
-            /* append to xref table */
-            pdf = pdf.append(loc).append(" 00000 n \n");
+        public void outputStructureTreeElements(OutputStream stream)
+                throws IOException {
+            assert structureTreeElements.size() > 0;
+            structureTreeObjectStreams = new ObjectStreamManager(PDFDocument.this);
+            for (PDFStructElem structElem : structureTreeElements) {
+                structureTreeObjectStreams.add(structElem);
+            }
         }
 
-        /* write the xref table and return the character length */
-        byte[] pdfBytes = encode(pdf.toString());
-        stream.write(pdfBytes);
-        return pdfBytes.length;
+        public long outputCrossReferenceObject(OutputStream stream,
+                TrailerDictionary trailerDictionary) throws IOException {
+            // Outputting the object streams should not have created new indirect objects
+            assert objects.isEmpty();
+            new CrossReferenceStream(PDFDocument.this, ++objectcount, trailerDictionary, position,
+                    indirectObjectOffsets,
+                    structureTreeObjectStreams.getCompressedObjectReferences())
+                    .output(stream);
+            return position;
+        }
     }
 
+    long getCurrentFileSize() {
+        return position;
+    }
+
+    FileIDGenerator getFileIDGenerator() {
+        if (fileIDGenerator == null) {
+            try {
+                fileIDGenerator = FileIDGenerator.getDigestFileIDGenerator(this);
+            } catch (NoSuchAlgorithmException e) {
+                fileIDGenerator = FileIDGenerator.getRandomFileIDGenerator();
+            }
+        }
+        return fileIDGenerator;
+    }
 }

Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java Thu Apr  5 16:19:19 2012
@@ -34,7 +34,7 @@ public class PDFEmbeddedFile extends PDF
         super();
         put("Type", new PDFName("EmbeddedFile"));
         PDFDictionary params = new PDFDictionary();
-        params.put("CreationDate", params.formatDateTime(new Date()));
+        params.put("CreationDate", PDFInfo.formatDateTime(new Date()));
         put("Params", params);
     }
 

Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFiles.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFiles.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFiles.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEmbeddedFiles.java Thu Apr  5 16:19:19 2012
@@ -21,7 +21,6 @@ package org.apache.fop.pdf;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.Writer;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.SortedMap;
@@ -39,9 +38,9 @@ public class PDFEmbeddedFiles extends PD
     }
 
     /** {@inheritDoc} */
-    protected void writeDictionary(OutputStream out, Writer writer) throws IOException {
+    protected void writeDictionary(OutputStream out, StringBuilder textBuffer) throws IOException {
         sortNames(); //Sort the names before writing them out
-        super.writeDictionary(out, writer);
+        super.writeDictionary(out, textBuffer);
     }
 
     private void sortNames() {

Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryption.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryption.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryption.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryption.java Thu Apr  5 16:19:19 2012
@@ -25,18 +25,6 @@ package org.apache.fop.pdf;
 public interface PDFEncryption {
 
     /**
-     * Returns the encryption parameters.
-     * @return the encryption parameters
-     */
-    PDFEncryptionParams getParams();
-
-    /**
-     * Sets the encryption parameters.
-     * @param params The parameterss to set
-     */
-    void setParams(PDFEncryptionParams params);
-
-    /**
      * Adds a PDFFilter to the PDFStream object
      * @param stream the stream to add an encryption filter to
      */
@@ -52,8 +40,10 @@ public interface PDFEncryption {
     byte[] encrypt(byte[] data, PDFObject refObj);
 
     /**
-     * Returns the trailer entry for encryption.
-     * @return the trailer entry
+     * Returns the /Encrypt entry in the file trailer dictionary.
+     *
+     * @return the string "/Encrypt n g R\n" where n and g are the number and generation
+     * of the document's encryption dictionary
      */
     String getTrailerEntry();
 }

Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java Thu Apr  5 16:19:19 2012
@@ -19,13 +19,12 @@
 
 package org.apache.fop.pdf;
 
-// Java
 import java.io.IOException;
 import java.io.OutputStream;
 import java.security.InvalidKeyException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-import java.util.Random;
+import java.util.Arrays;
 
 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
@@ -35,310 +34,426 @@ import javax.crypto.NoSuchPaddingExcepti
 import javax.crypto.spec.SecretKeySpec;
 
 /**
- * class representing a /Filter /Standard object.
- *
+ * An implementation of the Standard Security Handler.
  */
-public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
+public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
 
-    private class EncryptionFilter extends PDFFilter {
-        private PDFEncryptionJCE encryption;
-        private int number;
-        private int generation;
+    private final MessageDigest digest;
 
-        /**
-         * The constructor for the internal PDFEncryptionJCE filter
-         * @param encryption The encryption object to use
-         * @param number The number of the object to be encrypted
-         * @param generation The generation of the object to be encrypted
-         */
-        public EncryptionFilter(PDFEncryptionJCE encryption,
-                                int number, int generation) {
-            super();
-            this.encryption = encryption;
-            this.number  = number;
-            this.generation = generation;
-            log.debug("new encryption filter for number "
-                + number + " and generation " + generation);
+    private byte[] encryptionKey;
+
+    private String encryptionDictionary;
+
+    private class EncryptionInitializer {
+
+        private final PDFEncryptionParams encryptionParams;
+
+        private int encryptionLength;
+
+        private int version;
+
+        private int revision;
+
+        EncryptionInitializer(PDFEncryptionParams params) {
+            this.encryptionParams = new PDFEncryptionParams(params);
+        }
+
+        void init() {
+            encryptionLength = encryptionParams.getEncryptionLengthInBits();
+            determineEncryptionAlgorithm();
+            int permissions = Permission.computePermissions(encryptionParams);
+            EncryptionSettings encryptionSettings = new EncryptionSettings(
+                    encryptionLength, permissions,
+                    encryptionParams.getUserPassword(), encryptionParams.getOwnerPassword());
+            InitializationEngine initializationEngine = (revision == 2)
+                    ? new Rev2Engine(encryptionSettings)
+                    : new Rev3Engine(encryptionSettings);
+            initializationEngine.run();
+            encryptionDictionary = createEncryptionDictionary(permissions,
+                    initializationEngine.oValue,
+                    initializationEngine.uValue);
         }
 
+        private void determineEncryptionAlgorithm() {
+            if (isVersion1Revision2Algorithm()) {
+                version = 1;
+                revision = 2;
+            } else {
+                version = 2;
+                revision = 3;
+            }
+        }
+
+        private boolean isVersion1Revision2Algorithm() {
+            return encryptionLength == 40
+                    && encryptionParams.isAllowFillInForms()
+                    && encryptionParams.isAllowAccessContent()
+                    && encryptionParams.isAllowAssembleDocument()
+                    && encryptionParams.isAllowPrintHq();
+        }
+
+        private String createEncryptionDictionary(final int permissions, final byte[] oValue,
+                final byte[] uValue) {
+            return "<< /Filter /Standard\n"
+                    + "/V " + version + "\n"
+                    + "/R " + revision + "\n"
+                    + "/Length " + encryptionLength + "\n"
+                    + "/P "  + permissions + "\n"
+                    + "/O " + PDFText.toHex(oValue) + "\n"
+                    + "/U " + PDFText.toHex(uValue) + "\n"
+                    + ">>";
+        }
+
+    }
+
+    private static enum Permission {
+
+        PRINT(3),
+        EDIT_CONTENT(4),
+        COPY_CONTENT(5),
+        EDIT_ANNOTATIONS(6),
+        FILL_IN_FORMS(9),
+        ACCESS_CONTENT(10),
+        ASSEMBLE_DOCUMENT(11),
+        PRINT_HQ(12);
+
+        private final int mask;
+
         /**
-         * Return a PDF string representation of the filter. In this
-         * case no filter name is passed.
-         * @return The filter name, blank in this case
+         * Creates a new permission.
+         *
+         * @param bit bit position for this permission, 1-based to match the PDF Reference
          */
-        public String getName() {
-            return "";
+        private Permission(int bit) {
+            mask = 1 << (bit - 1);
+        }
+
+        private int removeFrom(int permissions) {
+            return permissions - mask;
+        }
+
+        static int computePermissions(PDFEncryptionParams encryptionParams) {
+            int permissions = -4;
+
+            if (!encryptionParams.isAllowPrint()) {
+                permissions = PRINT.removeFrom(permissions);
+            }
+            if (!encryptionParams.isAllowCopyContent()) {
+                permissions = COPY_CONTENT.removeFrom(permissions);
+            }
+            if (!encryptionParams.isAllowEditContent()) {
+                permissions = EDIT_CONTENT.removeFrom(permissions);
+            }
+            if (!encryptionParams.isAllowEditAnnotations()) {
+                permissions = EDIT_ANNOTATIONS.removeFrom(permissions);
+            }
+            if (!encryptionParams.isAllowFillInForms()) {
+                permissions = FILL_IN_FORMS.removeFrom(permissions);
+            }
+            if (!encryptionParams.isAllowAccessContent()) {
+                permissions = ACCESS_CONTENT.removeFrom(permissions);
+            }
+            if (!encryptionParams.isAllowAssembleDocument()) {
+                permissions = ASSEMBLE_DOCUMENT.removeFrom(permissions);
+            }
+            if (!encryptionParams.isAllowPrintHq()) {
+                permissions = PRINT_HQ.removeFrom(permissions);
+            }
+            return permissions;
+        }
+    }
+
+    private static final class EncryptionSettings {
+
+        final int encryptionLength; // CSOK: VisibilityModifier
+
+        final int permissions; // CSOK: VisibilityModifier
+
+        final String userPassword; // CSOK: VisibilityModifier
+
+        final String ownerPassword; // CSOK: VisibilityModifier
+
+        EncryptionSettings(int encryptionLength, int permissions,
+                String userPassword, String ownerPassword) {
+            this.encryptionLength = encryptionLength;
+            this.permissions = permissions;
+            this.userPassword = userPassword;
+            this.ownerPassword = ownerPassword;
+        }
+
+    }
+
+    private abstract class InitializationEngine {
+
+        /** Padding for passwords. */
+        protected final byte[] padding = new byte[] {
+                (byte) 0x28, (byte) 0xBF, (byte) 0x4E, (byte) 0x5E,
+                (byte) 0x4E, (byte) 0x75, (byte) 0x8A, (byte) 0x41,
+                (byte) 0x64, (byte) 0x00, (byte) 0x4E, (byte) 0x56,
+                (byte) 0xFF, (byte) 0xFA, (byte) 0x01, (byte) 0x08,
+                (byte) 0x2E, (byte) 0x2E, (byte) 0x00, (byte) 0xB6,
+                (byte) 0xD0, (byte) 0x68, (byte) 0x3E, (byte) 0x80,
+                (byte) 0x2F, (byte) 0x0C, (byte) 0xA9, (byte) 0xFE,
+                (byte) 0x64, (byte) 0x53, (byte) 0x69, (byte) 0x7A};
+
+        protected final int encryptionLengthInBytes;
+
+        private final int permissions;
+
+        private byte[] oValue;
+
+        private byte[] uValue;
+
+        private final byte[] preparedUserPassword;
+
+        protected final String ownerPassword;
+
+        InitializationEngine(EncryptionSettings encryptionSettings) {
+            this.encryptionLengthInBytes = encryptionSettings.encryptionLength / 8;
+            this.permissions = encryptionSettings.permissions;
+            this.preparedUserPassword = preparePassword(encryptionSettings.userPassword);
+            this.ownerPassword = encryptionSettings.ownerPassword;
+        }
+
+        void run() {
+            oValue = computeOValue();
+            createEncryptionKey();
+            uValue = computeUValue();
         }
 
         /**
-         * Return a parameter dictionary for this filter, or null
-         * @return The parameter dictionary. In this case, null.
+         * Applies Algorithm 3.3 Page 79 of the PDF 1.4 Reference.
+         *
+         * @return the O value
          */
-        public PDFObject getDecodeParms() {
-            return null;
+        private byte[] computeOValue() {
+            // Step 1
+            byte[] md5Input = prepareMD5Input();
+            // Step 2
+            digest.reset();
+            byte[] hash = digest.digest(md5Input);
+            // Step 3
+            hash = computeOValueStep3(hash);
+            // Step 4
+            byte[] key = new byte[encryptionLengthInBytes];
+            System.arraycopy(hash, 0, key, 0, encryptionLengthInBytes);
+            // Steps 5, 6
+            byte[] encryptionResult = encryptWithKey(key, preparedUserPassword);
+            // Step 7
+            encryptionResult = computeOValueStep7(key, encryptionResult);
+            // Step 8
+            return encryptionResult;
         }
 
         /**
-         * {@inheritDoc}
+         * Applies Algorithm 3.2 Page 78 of the PDF 1.4 Reference.
          */
-        public OutputStream applyFilter(OutputStream out) throws IOException {
-            return new CipherOutputStream(out,
-                    encryption.initCipher(number, generation));
+        private void createEncryptionKey() {
+            // Steps 1, 2
+            digest.reset();
+            digest.update(preparedUserPassword);
+            // Step 3
+            digest.update(oValue);
+            // Step 4
+            digest.update((byte) (permissions >>> 0));
+            digest.update((byte) (permissions >>> 8));
+            digest.update((byte) (permissions >>> 16));
+            digest.update((byte) (permissions >>> 24));
+            // Step 5
+            digest.update(getDocumentSafely().getFileIDGenerator().getOriginalFileID());
+            byte[] hash = digest.digest();
+            // Step 6
+            hash = createEncryptionKeyStep6(hash);
+            // Step 7
+            encryptionKey = new byte[encryptionLengthInBytes];
+            System.arraycopy(hash, 0, encryptionKey, 0, encryptionLengthInBytes);
         }
 
-    }
+        protected abstract byte[] computeUValue();
 
-    private static final char [] PAD
-                             = {0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41,
-                                0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
-                                0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80,
-                                0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A};
-
-    /** Value of PRINT permission */
-    public static final int PERMISSION_PRINT            =  4;
-    /** Value of content editting permission */
-    public static final int PERMISSION_EDIT_CONTENT     =  8;
-    /** Value of content extraction permission */
-    public static final int PERMISSION_COPY_CONTENT     = 16;
-    /** Value of annotation editting permission */
-    public static final int PERMISSION_EDIT_ANNOTATIONS = 32;
-
-    // Encryption tools
-    private MessageDigest digest = null;
-    //private Cipher cipher = null;
-    private Random random = new Random();
-    // Control attributes
-    private PDFEncryptionParams params;
-    // Output attributes
-    private byte[] fileID = null;
-    private byte[] encryptionKey = null;
-    private String dictionary = null;
+        /**
+         * Adds padding to the password as directed in page 78 of the PDF 1.4 Reference.
+         *
+         * @param password the password
+         * @return the password with additional padding if necessary
+         */
+        private byte[] preparePassword(String password) {
+            int finalLength = 32;
+            byte[] preparedPassword = new byte[finalLength];
+            byte[] passwordBytes = password.getBytes();
+            System.arraycopy(passwordBytes, 0, preparedPassword, 0, passwordBytes.length);
+            System.arraycopy(padding, 0, preparedPassword, passwordBytes.length,
+                    finalLength - passwordBytes.length);
+            return preparedPassword;
+        }
 
-    /**
-     * Create a /Filter /Standard object.
-     *
-     * @param objnum the object's number
-     */
-    public PDFEncryptionJCE(int objnum) {
-        /* generic creation of object */
-        super();
-        setObjectNumber(objnum);
-        try {
-            digest = MessageDigest.getInstance("MD5");
-            //cipher = Cipher.getInstance("RC4");
-        } catch (NoSuchAlgorithmException e) {
-            throw new UnsupportedOperationException(e.getMessage());
-        /*} catch (NoSuchPaddingException e) {
-            throw new UnsupportedOperationException(e.getMessage());*/
+        private byte[] prepareMD5Input() {
+            if (ownerPassword.length() != 0) {
+                return preparePassword(ownerPassword);
+            } else {
+                return preparedUserPassword;
+            }
         }
-    }
 
-    /**
-     * Local factory method.
-     * @param objnum PDF object number for the encryption object
-     * @param params PDF encryption parameters
-     * @return PDFEncryption the newly created PDFEncryption object
-     */
-    public static PDFEncryption make(int objnum, PDFEncryptionParams params) {
-        PDFEncryptionJCE impl = new PDFEncryptionJCE(objnum);
-        impl.setParams(params);
-        impl.init();
-        return impl;
-    }
+        protected abstract byte[] computeOValueStep3(byte[] hash);
 
+        protected abstract byte[] computeOValueStep7(byte[] key, byte[] encryptionResult);
 
-    /**
-     * Returns the encryption parameters.
-     * @return the encryption parameters
-     */
-    public PDFEncryptionParams getParams() {
-        return this.params;
-    }
+        protected abstract byte[] createEncryptionKeyStep6(byte[] hash);
 
-    /**
-     * Sets the encryption parameters.
-     * @param params The parameterss to set
-     */
-    public void setParams(PDFEncryptionParams params) {
-        this.params = params;
     }
 
-    // Internal procedures
+    private class Rev2Engine extends InitializationEngine {
 
-    private byte[] prepPassword(String password) {
-        byte[] obuffer = new byte[32];
-        byte[] pbuffer = password.getBytes();
+        Rev2Engine(EncryptionSettings encryptionSettings) {
+            super(encryptionSettings);
+        }
 
-        int i = 0;
-        int j = 0;
+        @Override
+        protected byte[] computeOValueStep3(byte[] hash) {
+            return hash;
+        }
+
+        @Override
+        protected byte[] computeOValueStep7(byte[] key, byte[] encryptionResult) {
+            return encryptionResult;
+        }
 
-        while (i < obuffer.length && i < pbuffer.length) {
-            obuffer[i] = pbuffer[i];
-            i++;
+        @Override
+        protected byte[] createEncryptionKeyStep6(byte[] hash) {
+            return hash;
         }
-        while (i < obuffer.length) {
-            obuffer[i++] = (byte) PAD[j++];
+
+        @Override
+        protected byte[] computeUValue() {
+            return encryptWithKey(encryptionKey, padding);
         }
 
-        return obuffer;
     }
 
-    /**
-     * Returns the document file ID
-     * @return The file ID
-     */
-    public byte[] getFileID() {
-        if (fileID == null) {
-            fileID = new byte[16];
-            random.nextBytes(fileID);
+    private class Rev3Engine extends InitializationEngine {
+
+        Rev3Engine(EncryptionSettings encryptionSettings) {
+            super(encryptionSettings);
         }
 
-        return fileID;
-    }
+        @Override
+        protected byte[] computeOValueStep3(byte[] hash) {
+            for (int i = 0; i < 50; i++) {
+                hash = digest.digest(hash);
+            }
+            return hash;
+        }
 
-    /**
-     * This method returns the indexed file ID
-     * @param index The index to access the file ID
-     * @return The file ID
-     */
-    public String getFileID(int index) {
-        if (index == 1) {
-            return PDFText.toHex(getFileID());
+        @Override
+        protected byte[] computeOValueStep7(byte[] key, byte[] encryptionResult) {
+            return xorKeyAndEncrypt19Times(key, encryptionResult);
         }
 
-        byte[] id = new byte[16];
-        random.nextBytes(id);
-        return PDFText.toHex(id);
-    }
+        @Override
+        protected byte[] createEncryptionKeyStep6(byte[] hash) {
+            for (int i = 0; i < 50; i++) {
+                digest.update(hash, 0, encryptionLengthInBytes);
+                hash = digest.digest();
+            }
+            return hash;
+        }
 
-    private byte[] encryptWithKey(byte[] data, byte[] key) {
-        try {
-            final Cipher c = initCipher(key);
-            return c.doFinal(data);
-        } catch (IllegalBlockSizeException e) {
-            throw new IllegalStateException(e.getMessage());
-        } catch (BadPaddingException e) {
-            throw new IllegalStateException(e.getMessage());
+        @Override
+        protected byte[] computeUValue() {
+            // Step 1 is encryptionKey
+            // Step 2
+            digest.reset();
+            digest.update(padding);
+            // Step 3
+            digest.update(getDocumentSafely().getFileIDGenerator().getOriginalFileID());
+            // Step 4
+            byte[] encryptionResult = encryptWithKey(encryptionKey, digest.digest());
+            // Step 5
+            encryptionResult = xorKeyAndEncrypt19Times(encryptionKey, encryptionResult);
+            // Step 6
+            byte[] uValue = new byte[32];
+            System.arraycopy(encryptionResult, 0, uValue, 0, 16);
+            // Add the arbitrary padding
+            Arrays.fill(uValue, 16, 32, (byte) 0);
+            return uValue;
         }
-    }
 
-    private Cipher initCipher(byte[] key) {
-        try {
-            Cipher c = Cipher.getInstance("RC4");
-            SecretKeySpec keyspec = new SecretKeySpec(key, "RC4");
-            c.init(Cipher.ENCRYPT_MODE, keyspec);
-            return c;
-        } catch (InvalidKeyException e) {
-            throw new IllegalStateException(e.getMessage());
-        } catch (NoSuchAlgorithmException e) {
-            throw new UnsupportedOperationException(e.getMessage());
-        } catch (NoSuchPaddingException e) {
-            throw new UnsupportedOperationException(e.getMessage());
+        private byte[] xorKeyAndEncrypt19Times(byte[] key, byte[] input) {
+            byte[] result = input;
+            byte[] encryptionKey = new byte[key.length];
+            for (int i = 1; i <= 19; i++) {
+                for (int j = 0; j < key.length; j++) {
+                    encryptionKey[j] = (byte) (key[j] ^ i);
+                }
+                result = encryptWithKey(encryptionKey, result);
+            }
+            return result;
         }
-    }
 
-    private Cipher initCipher(int number, int generation) {
-        byte[] hash = calcHash(number, generation);
-        int size = hash.length;
-        hash = digest.digest(hash);
-        byte[] key = calcKey(hash, size);
-        return initCipher(key);
     }
 
-    private byte[] encryptWithHash(byte[] data, byte[] hash, int size) {
-        hash = digest.digest(hash);
+    private class EncryptionFilter extends PDFFilter {
 
-        byte[] key = calcKey(hash, size);
+        private int streamNumber;
 
-        return encryptWithKey(data, key);
-    }
+        private int streamGeneration;
 
-    private byte[] calcKey(byte[] hash, int size) {
-        byte[] key = new byte[size];
+        EncryptionFilter(int streamNumber, int streamGeneration) {
+            this.streamNumber  = streamNumber;
+            this.streamGeneration = streamGeneration;
+        }
 
-        for (int i = 0; i < size; i++) {
-            key[i] = hash[i];
+        /**
+         * Returns a PDF string representation of this filter.
+         *
+         * @return the empty string
+         */
+        public String getName() {
+            return "";
         }
-        return key;
+
+        /**
+         * Returns a parameter dictionary for this filter.
+         *
+         * @return null, this filter has no parameters
+         */
+        public PDFObject getDecodeParms() {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        public OutputStream applyFilter(OutputStream out) throws IOException {
+            byte[] key = createEncryptionKey(streamNumber, streamGeneration);
+            Cipher cipher = initCipher(key);
+            return new CipherOutputStream(out, cipher);
+        }
+
     }
 
-    /**
-     * This method initializes the encryption algorithms and values
-     */
-    public void init() {
-        // Generate the owner value
-        byte[] oValue;
-        if (params.getOwnerPassword().length() > 0) {
-            oValue = encryptWithHash(
-                    prepPassword(params.getUserPassword()),
-                    prepPassword(params.getOwnerPassword()), 5);
-        } else {
-            oValue = encryptWithHash(
-                    prepPassword(params.getUserPassword()),
-                    prepPassword(params.getUserPassword()), 5);
-        }
-
-        // Generate permissions value
-        int permissions = -4;
-
-        if (!params.isAllowPrint()) {
-            permissions -= PERMISSION_PRINT;
-        }
-        if (!params.isAllowCopyContent()) {
-            permissions -= PERMISSION_COPY_CONTENT;
-        }
-        if (!params.isAllowEditContent()) {
-            permissions -= PERMISSION_EDIT_CONTENT;
-        }
-        if (!params.isAllowEditAnnotations()) {
-            permissions -= PERMISSION_EDIT_ANNOTATIONS;
-        }
-
-        // Create the encrption key
-        digest.update(prepPassword(params.getUserPassword()));
-        digest.update(oValue);
-        digest.update((byte) (permissions >>> 0));
-        digest.update((byte) (permissions >>> 8));
-        digest.update((byte) (permissions >>> 16));
-        digest.update((byte) (permissions >>> 24));
-        digest.update(getFileID());
-
-        byte [] hash = digest.digest();
-        this.encryptionKey = new byte[5];
-
-        for (int i = 0; i < 5; i++) {
-            this.encryptionKey[i] = hash[i];
-        }
-
-        // Create the user value
-        byte[] uValue = encryptWithKey(prepPassword(""), this.encryptionKey);
-
-        // Create the dictionary
-        this.dictionary = getObjectID()
-                        + "<< /Filter /Standard\n"
-                        + "/V 1\n"
-                        + "/R 2\n"
-                        + "/Length 40\n"
-                        + "/P "  + permissions + "\n"
-                        + "/O " + PDFText.toHex(oValue) + "\n"
-                        + "/U " + PDFText.toHex(uValue) + "\n"
-                        + ">>\n"
-                        + "endobj\n";
+    private PDFEncryptionJCE(int objectNumber, PDFEncryptionParams params, PDFDocument pdf) {
+        setObjectNumber(objectNumber);
+        try {
+            digest = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            throw new UnsupportedOperationException(e.getMessage());
+        }
+        setDocument(pdf);
+        EncryptionInitializer encryptionInitializer = new EncryptionInitializer(params);
+        encryptionInitializer.init();
     }
 
     /**
-     * This method encrypts the passed data using the generated keys.
-     * @param data The data to be encrypted
-     * @param number The block number
-     * @param generation The block generation
-     * @return The encrypted data
-     */
-    public byte[] encryptData(byte[] data, int number, int generation) {
-        if (this.encryptionKey == null) {
-            throw new IllegalStateException("PDF Encryption has not been initialized");
-        }
-        byte[] hash = calcHash(number, generation);
-        return encryptWithHash(data, hash, hash.length);
+     * Creates and returns an encryption object.
+     *
+     * @param objectNumber the object number for the encryption dictionary
+     * @param params the encryption parameters
+     * @param pdf the PDF document to be encrypted
+     * @return the newly created encryption object
+     */
+    public static PDFEncryption make(
+            int objectNumber, PDFEncryptionParams params, PDFDocument pdf) {
+        return new PDFEncryptionJCE(objectNumber, params, pdf);
     }
 
     /** {@inheritDoc} */
@@ -350,63 +465,88 @@ public class PDFEncryptionJCE extends PD
         if (o == null) {
             throw new IllegalStateException("No object number could be obtained for a PDF object");
         }
-        return encryptData(data, o.getObjectNumber(), o.getGeneration());
-    }
-
-    private byte[] calcHash(int number, int generation) {
-        byte[] hash = new byte[this.encryptionKey.length + 5];
-
-        int i = 0;
-        while (i < this.encryptionKey.length) {
-            hash[i] = this.encryptionKey[i]; i++;
-        }
-
-        hash[i++] = (byte) (number >>> 0);
-        hash[i++] = (byte) (number >>> 8);
-        hash[i++] = (byte) (number >>> 16);
-        hash[i++] = (byte) (generation >>> 0);
-        hash[i++] = (byte) (generation >>> 8);
-        return hash;
-    }
-
-    /**
-     * Creates PDFFilter for the encryption object
-     * @param number The object number
-     * @param generation The objects generation
-     * @return The resulting filter
-     */
-    public PDFFilter makeFilter(int number, int generation) {
-        return new EncryptionFilter(this, number, generation);
+        byte[] key = createEncryptionKey(o.getObjectNumber(), o.getGeneration());
+        return encryptWithKey(key, data);
     }
 
-    /**
-     * Adds a PDFFilter to the PDFStream object
-     * @param stream the stream to add an encryption filter to
-     */
+    /** {@inheritDoc} */
     public void applyFilter(AbstractPDFStream stream) {
         stream.getFilterList().addFilter(
-                this.makeFilter(stream.getObjectNumber(), stream.getGeneration()));
+                new EncryptionFilter(stream.getObjectNumber(), stream.getGeneration()));
     }
 
     /**
-     * Represent the object in PDF
+     *  Prepares the encryption dictionary for output to a PDF file.
      *
-     * @return the PDF
+     *  @return the encryption dictionary as a byte array
      */
     public byte[] toPDF() {
-        if (this.dictionary == null) {
-            throw new IllegalStateException("PDF Encryption has not been initialized");
+        assert encryptionDictionary != null;
+        return encode(this.encryptionDictionary);
+    }
+
+    /** {@inheritDoc} */
+    public String getTrailerEntry() {
+        return "/Encrypt " + getObjectNumber() + " " + getGeneration() + " R\n";
+    }
+
+    private static byte[] encryptWithKey(byte[] key, byte[] data) {
+        try {
+            final Cipher c = initCipher(key);
+            return c.doFinal(data);
+        } catch (IllegalBlockSizeException e) {
+            throw new IllegalStateException(e.getMessage());
+        } catch (BadPaddingException e) {
+            throw new IllegalStateException(e.getMessage());
         }
+    }
 
-        return encode(this.dictionary);
+    private static Cipher initCipher(byte[] key) {
+        try {
+            Cipher c = Cipher.getInstance("RC4");
+            SecretKeySpec keyspec = new SecretKeySpec(key, "RC4");
+            c.init(Cipher.ENCRYPT_MODE, keyspec);
+            return c;
+        } catch (InvalidKeyException e) {
+            throw new IllegalStateException(e);
+        } catch (NoSuchAlgorithmException e) {
+            throw new UnsupportedOperationException(e);
+        } catch (NoSuchPaddingException e) {
+            throw new UnsupportedOperationException(e);
+        }
     }
 
     /**
-     * {@inheritDoc}
-     */
-    public String getTrailerEntry() {
-        return "/Encrypt " + getObjectNumber() + " "
-                    + getGeneration() + " R\n"
-                    + "/ID[" + getFileID(1) + getFileID(2) + "]\n";
+     * Applies Algorithm 3.1 from the PDF 1.4 Reference.
+     *
+     * @param objectNumber the object number
+     * @param generationNumber the generation number
+     * @return the key to use for encryption
+     */
+    private byte[] createEncryptionKey(int objectNumber, int generationNumber) {
+        // Step 1 passed in
+        // Step 2
+        byte[] md5Input = prepareMD5Input(objectNumber, generationNumber);
+        // Step 3
+        digest.reset();
+        byte[] hash = digest.digest(md5Input);
+        // Step 4
+        int keyLength = Math.min(16, md5Input.length);
+        byte[] key = new byte[keyLength];
+        System.arraycopy(hash, 0, key, 0, keyLength);
+        return key;
     }
+
+    private byte[] prepareMD5Input(int objectNumber, int generationNumber) {
+        byte[] md5Input = new byte[encryptionKey.length + 5];
+        System.arraycopy(encryptionKey, 0, md5Input, 0, encryptionKey.length);
+        int i = encryptionKey.length;
+        md5Input[i++] = (byte) (objectNumber >>> 0);
+        md5Input[i++] = (byte) (objectNumber >>> 8);
+        md5Input[i++] = (byte) (objectNumber >>> 16);
+        md5Input[i++] = (byte) (generationNumber >>> 0);
+        md5Input[i++] = (byte) (generationNumber >>> 8);
+        return md5Input;
+    }
+
 }

Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionManager.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionManager.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionManager.java Thu Apr  5 16:19:19 2012
@@ -109,16 +109,18 @@ public final class PDFEncryptionManager 
      * Creates a new PDFEncryption instance if PDF encryption is available.
      * @param objnum PDF object number
      * @param params PDF encryption parameters
+     * @param pdf the PDF document to encrypt
      * @return PDFEncryption the newly created instance, null if PDF encryption
      * is unavailable.
      */
-    public static PDFEncryption newInstance(int objnum, PDFEncryptionParams params) {
+    public static PDFEncryption newInstance(int objnum, PDFEncryptionParams params,
+            PDFDocument pdf) {
         try {
-            Class clazz = Class.forName("org.apache.fop.pdf.PDFEncryptionJCE");
+            Class<?> clazz = Class.forName("org.apache.fop.pdf.PDFEncryptionJCE");
             Method makeMethod = clazz.getMethod("make",
-                        new Class[] {int.class, PDFEncryptionParams.class});
+                        new Class[] {int.class, PDFEncryptionParams.class, PDFDocument.class});
             Object obj = makeMethod.invoke(null,
-                        new Object[] {new Integer(objnum), params});
+                        new Object[] {new Integer(objnum), params, pdf});
             return (PDFEncryption)obj;
         } catch (ClassNotFoundException e) {
             if (checkAvailableAlgorithms()) {

Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionParams.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionParams.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionParams.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFEncryptionParams.java Thu Apr  5 16:19:19 2012
@@ -26,10 +26,17 @@ public class PDFEncryptionParams {
 
     private String userPassword = ""; //May not be null
     private String ownerPassword = ""; //May not be null
+
     private boolean allowPrint = true;
     private boolean allowCopyContent = true;
     private boolean allowEditContent = true;
     private boolean allowEditAnnotations = true;
+    private boolean allowFillInForms = true;
+    private boolean allowAccessContent = true;
+    private boolean allowAssembleDocument = true;
+    private boolean allowPrintHq = true;
+
+    private int encryptionLengthInBits = 40;
 
     /**
      * Creates a new instance.
@@ -61,6 +68,25 @@ public class PDFEncryptionParams {
     }
 
     /**
+     * Creates a copy of the given encryption parameters.
+     *
+     * @param source source encryption parameters
+     */
+    public PDFEncryptionParams(PDFEncryptionParams source) {
+        setUserPassword(source.getUserPassword());
+        setOwnerPassword(source.getOwnerPassword());
+        setAllowPrint(source.isAllowPrint());
+        setAllowCopyContent(source.isAllowCopyContent());
+        setAllowEditContent(source.isAllowEditContent());
+        setAllowEditAnnotations(source.isAllowEditAnnotations());
+        setAllowAssembleDocument(source.isAllowAssembleDocument());
+        setAllowAccessContent(source.isAllowAccessContent());
+        setAllowFillInForms(source.isAllowFillInForms());
+        setAllowPrintHq(source.isAllowPrintHq());
+        setEncryptionLengthInBits(source.getEncryptionLengthInBits());
+    }
+
+    /**
      * Indicates whether copying content is allowed.
      * @return true if copying is allowed
      */
@@ -93,6 +119,38 @@ public class PDFEncryptionParams {
     }
 
     /**
+     * Indicates whether revision 3 filling in forms is allowed.
+     * @return true if revision 3 filling in forms is allowed
+     */
+    public boolean isAllowFillInForms() {
+        return allowFillInForms;
+    }
+
+    /**
+     * Indicates whether revision 3 extracting text and graphics is allowed.
+     * @return true if revision 3 extracting text and graphics is allowed
+     */
+    public boolean isAllowAccessContent() {
+        return allowAccessContent;
+    }
+
+    /**
+     * Indicates whether revision 3 assembling document is allowed.
+     * @return true if revision 3 assembling document is allowed
+     */
+    public boolean isAllowAssembleDocument() {
+        return allowAssembleDocument;
+    }
+
+    /**
+     * Indicates whether revision 3 printing to high quality is allowed.
+     * @return true if revision 3 printing to high quality is allowed
+     */
+    public boolean isAllowPrintHq() {
+        return allowPrintHq;
+    }
+
+    /**
      * Returns the owner password.
      * @return the owner password, an empty string if no password applies
      */
@@ -133,7 +191,7 @@ public class PDFEncryptionParams {
     }
 
     /**
-     * Sets the persmission for printing.
+     * Sets the permission for printing.
      * @param allowPrint true if printing is allowed
      */
     public void setAllowPrint(boolean allowPrint) {
@@ -141,6 +199,38 @@ public class PDFEncryptionParams {
     }
 
     /**
+     * Sets whether revision 3 filling in forms is allowed.
+     * @param allowFillInForms true if revision 3 filling in forms is allowed.
+     */
+    public void setAllowFillInForms(boolean allowFillInForms) {
+        this.allowFillInForms = allowFillInForms;
+    }
+
+    /**
+     * Sets whether revision 3 extracting text and graphics is allowed.
+     * @param allowAccessContent true if revision 3 extracting text and graphics is allowed
+     */
+    public void setAllowAccessContent(boolean allowAccessContent) {
+        this.allowAccessContent = allowAccessContent;
+    }
+
+    /**
+     * Sets whether revision 3 assembling document is allowed.
+     * @param allowAssembleDocument true if revision 3 assembling document is allowed
+     */
+    public void setAllowAssembleDocument(boolean allowAssembleDocument) {
+        this.allowAssembleDocument = allowAssembleDocument;
+    }
+
+    /**
+     * Sets whether revision 3 printing to high quality is allowed.
+     * @param allowPrintHq true if revision 3 printing to high quality is allowed
+     */
+    public void setAllowPrintHq(boolean allowPrintHq) {
+        this.allowPrintHq = allowPrintHq;
+    }
+
+    /**
      * Sets the owner password.
      * @param ownerPassword The owner password to set, null or an empty String
      * if no password is applicable
@@ -166,4 +256,21 @@ public class PDFEncryptionParams {
         }
     }
 
+    /**
+     * Returns the encryption length.
+     * @return the encryption length
+     */
+    public int getEncryptionLengthInBits() {
+        return encryptionLengthInBits;
+    }
+
+    /**
+     * Sets the encryption length.
+     *
+     * @param encryptionLength the encryption length
+     */
+    public void setEncryptionLengthInBits(int encryptionLength) {
+        this.encryptionLengthInBits = encryptionLength;
+    }
+
 }

Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFactory.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFactory.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFactory.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFactory.java Thu Apr  5 16:19:19 2012
@@ -20,13 +20,16 @@
 package org.apache.fop.pdf;
 
 // Java
+import java.awt.Color;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
+import java.text.DecimalFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Iterator;
 import java.util.List;
@@ -39,6 +42,11 @@ import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.output.ByteArrayOutputStream;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.java2d.color.ColorUtil;
+import org.apache.xmlgraphics.java2d.color.NamedColorSpace;
+import org.apache.xmlgraphics.xmp.Metadata;
+
 import org.apache.fop.fonts.CIDFont;
 import org.apache.fop.fonts.CIDSubset;
 import org.apache.fop.fonts.CodePointMapping;
@@ -70,6 +78,8 @@ public class PDFFactory {
 
     private Log log = LogFactory.getLog(PDFFactory.class);
 
+    private int subsetFontCounter = -1;
+
     /**
      * Creates a new PDFFactory.
      * @param document the parent PDFDocument needed to register the generated
@@ -777,23 +787,23 @@ public class PDFFactory {
 
         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 (getDocument().getColorSpace() != currentColor.getColorSpace()) {
-                currentColor.setColorSpace(
-                    getDocument().getColorSpace());
-            }
+            Color currentColor = (Color)theColors.get(currentPosition);
+            Color nextColor = (Color)theColors.get(currentPosition + 1);
 
-            if (getDocument().getColorSpace()
-                    != nextColor.getColorSpace()) {
-                nextColor.setColorSpace(
-                    getDocument().getColorSpace());
+            // colorspace must be consistent, so we simply convert to sRGB where necessary
+            if (!currentColor.getColorSpace().isCS_sRGB()) {
+                //Convert to sRGB
+                currentColor = ColorUtil.toSRGBColor(currentColor);
+                theColors.set(currentPosition, currentColor);
+            }
+            if (!nextColor.getColorSpace().isCS_sRGB()) {
+                //Convert to sRGB
+                nextColor = ColorUtil.toSRGBColor(nextColor);
+                theColors.set(currentPosition + 1, nextColor);
             }
 
-            theCzero = currentColor.getVector();
-            theCone = nextColor.getVector();
+            theCzero = toColorVector(currentColor);
+            theCone = toColorVector(nextColor);
 
             myfunc = makeFunction(2, null, null, theCzero, theCone,
                                        interpolation);
@@ -841,6 +851,15 @@ public class PDFFactory {
         return (myPattern);
     }
 
+    private List toColorVector(Color nextColor) {
+        List vector = new java.util.ArrayList();
+        float[] comps = nextColor.getColorComponents(null);
+        for (int i = 0, c = comps.length; i < c; i++) {
+            vector.add(new Double(comps[i]));
+        }
+        return vector;
+    }
+
     /* ============= named destinations and the name dictionary ============ */
 
     /**
@@ -895,35 +914,6 @@ public class PDFFactory {
     }
 
     /**
-     * Creates and returns a StructTreeRoot object. Used for accessibility.
-     * @param parentTree the value of the ParenTree entry
-     * @return structure Tree Root element
-     */
-    public PDFStructTreeRoot makeStructTreeRoot(PDFParentTree parentTree) {
-        PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(parentTree);
-        getDocument().assignObjectNumber(structTreeRoot);
-        getDocument().addTrailerObject(structTreeRoot);
-        getDocument().getRoot().setStructTreeRoot(structTreeRoot);
-        return structTreeRoot;
-    }
-
-    /**
-     * Creates and returns a StructElem object.
-     *
-     * @param structureType the structure type of the new element (value for the
-     * S entry)
-     * @param parent the parent of the new structure element in the structure
-     * hierarchy
-     * @return the newly created element
-     */
-    public PDFStructElem makeStructureElement(PDFName structureType, PDFObject parent) {
-        PDFStructElem structElem = new PDFStructElem(parent, structureType);
-        getDocument().assignObjectNumber(structElem);
-        getDocument().addTrailerObject(structElem);
-        return structElem;
-    }
-
-    /**
      * Make a the head object of the name dictionary (the /Dests object).
      *
      * @param destinationList a list of PDFDestination instances
@@ -1362,10 +1352,15 @@ public class PDFFactory {
         } else {
             FontType fonttype = metrics.getFontType();
 
-            PDFFontDescriptor pdfdesc = makeFontDescriptor(descriptor);
+            String fontPrefix = descriptor.isSubsetEmbedded() ? createSubsetFontPrefix() : "";
+
+            String subsetFontName = fontPrefix + basefont;
+
+            PDFFontDescriptor pdfdesc = makeFontDescriptor(descriptor, fontPrefix);
 
             PDFFont font = null;
-            font = PDFFont.createFont(fontname, fonttype, basefont, null);
+
+            font = PDFFont.createFont(fontname, fonttype, subsetFontName, null);
             getDocument().registerObject(font);
 
             if (fonttype == FontType.TYPE0) {
@@ -1380,8 +1375,7 @@ public class PDFFactory {
                     = new PDFCIDSystemInfo(cidMetrics.getRegistry(),
                                          cidMetrics.getOrdering(),
                                          cidMetrics.getSupplement());
-                PDFCIDFont cidFont
-                    = new PDFCIDFont(basefont,
+                PDFCIDFont cidFont = new PDFCIDFont(subsetFontName,
                                    cidMetrics.getCIDType(),
                                    cidMetrics.getDefaultWidth(),
                                    getSubsetWidths(cidMetrics), sysInfo,
@@ -1535,18 +1529,35 @@ public class PDFFactory {
         return warray;
     }
 
+    private String createSubsetFontPrefix() {
+        subsetFontCounter++;
+        DecimalFormat counterFormat = new DecimalFormat("00000");
+        String counterString = counterFormat.format(subsetFontCounter);
+
+        // Subset prefix as described in chapter 5.5.3 of PDF 1.4
+        StringBuffer sb = new StringBuffer("E");
+
+        for (char c : counterString.toCharArray()) {
+            // translate numbers to uppercase characters
+            sb.append((char) (c + ('A' - '0')));
+        }
+        sb.append("+");
+        return sb.toString();
+    }
+
     /**
      * make a /FontDescriptor object
      *
      * @param desc the font descriptor
+     * @param fontPrefix the String with which to prefix the font name
      * @return the new PDF font descriptor
      */
-    public PDFFontDescriptor makeFontDescriptor(FontDescriptor desc) {
+    private PDFFontDescriptor makeFontDescriptor(FontDescriptor desc, String fontPrefix) {
         PDFFontDescriptor descriptor = null;
 
         if (desc.getFontType() == FontType.TYPE0) {
             // CID Font
-            descriptor = new PDFCIDFontDescriptor(desc.getEmbedFontName(),
+            descriptor = new PDFCIDFontDescriptor(fontPrefix + desc.getEmbedFontName(),
                                             desc.getFontBBox(),
                                             desc.getCapHeight(),
                                             desc.getFlags(),
@@ -1780,6 +1791,38 @@ public class PDFFactory {
     }
 
     /**
+     * Create a new Separation color space.
+     * @param res the resource context (may be null)
+     * @param ncs the named color space to map to a separation color space
+     * @return the newly created Separation color space
+     */
+    public PDFSeparationColorSpace makeSeparationColorSpace(PDFResourceContext res,
+            NamedColorSpace ncs) {
+        String colorName = ncs.getColorName();
+        final Double zero = new Double(0d);
+        final Double one = new Double(1d);
+        List theDomain = Arrays.asList(new Double[] {zero, one});
+        List theRange = Arrays.asList(new Double[] {zero, one, zero, one, zero, one});
+        List theCZero = Arrays.asList(new Double[] {one, one, one});
+        List theCOne = new ArrayList();
+        float[] comps = ncs.getRGBColor().getColorComponents(null);
+        for (int i = 0, c = comps.length; i < c; i++) {
+            theCOne.add(new Double(comps[i]));
+        }
+        PDFFunction tintFunction = makeFunction(2, theDomain, theRange,
+                theCZero, theCOne, 1.0d);
+        PDFSeparationColorSpace cs = new PDFSeparationColorSpace(colorName, tintFunction);
+        getDocument().registerObject(cs);
+        if (res != null) {
+            res.getPDFResources().addColorSpace(cs);
+        } else {
+            getDocument().getResources().addColorSpace(cs);
+        }
+
+        return cs;
+    }
+
+    /**
      * Make an Array object (ex. Widths array for a font).
      *
      * @param values the int array values

Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilter.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilter.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilter.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilter.java Thu Apr  5 16:19:19 2012
@@ -19,15 +19,15 @@
 
 package org.apache.fop.pdf;
 
-import java.io.OutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 
 /**
- * PDF Filter class.
- * This represents a PDF filter object.
- * Filter implementations should extend this class.
+ * <p>PDF Filter class.
+ * This class represents a PDF filter object.
+ * Filter implementations should extend this class.</p>
  *
- * @author Eric SCHAEFFER, Kelly A. Campbell
+ * <p>This work was authored by Eric Schaeffer and Kelly A. Campbell.</p>
  */
 public abstract class PDFFilter {
     /*

Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterException.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterException.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterException.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterException.java Thu Apr  5 16:19:19 2012
@@ -20,10 +20,10 @@
 package org.apache.fop.pdf;
 
 /**
- * PDF Filter exception.
- * This is used for exceptions relating to use a PDF filter.
+ * <p>PDF Filter exception.
+ * This is used for exceptions relating to use a PDF filter.</p>
  *
- * @author Eric SCHAEFFER
+ * <p>This work was authored by Eric Schaeffer.</p>
  */
 public class PDFFilterException extends Exception {
     /**

Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterList.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterList.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterList.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFilterList.java Thu Apr  5 16:19:19 2012
@@ -21,6 +21,7 @@ package org.apache.fop.pdf;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -47,7 +48,7 @@ public class PDFFilterList {
     /** Key for the filter used for metadata */
     public static final String METADATA_FILTER = "metadata";
 
-    private List filters = new java.util.ArrayList();
+    private List<PDFFilter> filters = new java.util.ArrayList<PDFFilter>();
 
     private boolean ignoreASCIIFilters = false;
 
@@ -197,6 +198,10 @@ public class PDFFilterList {
         }
     }
 
+    List<PDFFilter> getFilters() {
+        return Collections.unmodifiableList(filters);
+    }
+
     /**
      * Apply the filters to the data
      * in the order given and return the /Filter and /DecodeParms
@@ -206,7 +211,7 @@ public class PDFFilterList {
      * @return a String representing the filter list
      */
     protected String buildFilterDictEntries() {
-        if (filters != null && filters.size() > 0) {
+        if (filters.size() > 0) {
             List names = new java.util.ArrayList();
             List parms = new java.util.ArrayList();
 
@@ -229,7 +234,7 @@ public class PDFFilterList {
      * @param dict the PDFDictionary to set the entries on
      */
     protected void putFilterDictEntries(PDFDictionary dict) {
-        if (filters != null && filters.size() > 0) {
+        if (filters.size() > 0) {
             List names = new java.util.ArrayList();
             List parms = new java.util.ArrayList();
 
@@ -358,7 +363,7 @@ public class PDFFilterList {
      */
     public OutputStream applyFilters(OutputStream stream) throws IOException {
         OutputStream out = stream;
-        if (filters != null && !isDisableAllFilters()) {
+        if (!isDisableAllFilters()) {
             for (int count = filters.size() - 1; count >= 0; count--) {
                 PDFFilter filter = (PDFFilter)filters.get(count);
                 out = filter.applyFilter(out);

Modified: xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFont.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFont.java?rev=1309921&r1=1309920&r2=1309921&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFont.java (original)
+++ xmlgraphics/fop/branches/Temp_TrueTypeInPostScript/src/java/org/apache/fop/pdf/PDFFont.java Thu Apr  5 16:19:19 2012
@@ -174,7 +174,7 @@ public class PDFFont extends PDFDictiona
     }
 
     /** {@inheritDoc} */
-    protected int output(OutputStream stream) throws IOException {
+    public int output(OutputStream stream) throws IOException {
         validate();
         return super.output(stream);
     }



---------------------------------------------------------------------
To unsubscribe, e-mail: fop-commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: fop-commits-help@xmlgraphics.apache.org