]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
FOP-2445: Merge PDF Linearization branch
authorSimon Steiner <ssteiner@apache.org>
Tue, 24 Feb 2015 11:23:44 +0000 (11:23 +0000)
committerSimon Steiner <ssteiner@apache.org>
Tue, 24 Feb 2015 11:23:44 +0000 (11:23 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1661887 13f79535-47bb-0310-9956-ffa450edef68

52 files changed:
src/java/org/apache/fop/pdf/AbstractPDFStream.java
src/java/org/apache/fop/pdf/BitmapImage.java
src/java/org/apache/fop/pdf/CompressedObject.java
src/java/org/apache/fop/pdf/PDFAnnotList.java
src/java/org/apache/fop/pdf/PDFArray.java
src/java/org/apache/fop/pdf/PDFCIDFont.java
src/java/org/apache/fop/pdf/PDFDictionary.java
src/java/org/apache/fop/pdf/PDFDocument.java
src/java/org/apache/fop/pdf/PDFEncryptionJCE.java
src/java/org/apache/fop/pdf/PDFEncryptionManager.java
src/java/org/apache/fop/pdf/PDFFactory.java
src/java/org/apache/fop/pdf/PDFGState.java
src/java/org/apache/fop/pdf/PDFGoTo.java
src/java/org/apache/fop/pdf/PDFICCBasedColorSpace.java
src/java/org/apache/fop/pdf/PDFImageXObject.java
src/java/org/apache/fop/pdf/PDFInfo.java
src/java/org/apache/fop/pdf/PDFLinearization.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFLink.java
src/java/org/apache/fop/pdf/PDFObject.java
src/java/org/apache/fop/pdf/PDFObjectNumber.java [new file with mode: 0644]
src/java/org/apache/fop/pdf/PDFOutline.java
src/java/org/apache/fop/pdf/PDFPages.java
src/java/org/apache/fop/pdf/PDFReference.java
src/java/org/apache/fop/pdf/PDFResources.java
src/java/org/apache/fop/pdf/PDFRoot.java
src/java/org/apache/fop/pdf/PDFStream.java
src/java/org/apache/fop/pdf/xref/CompressedObjectReference.java
src/java/org/apache/fop/pdf/xref/CrossReferenceStream.java
src/java/org/apache/fop/pdf/xref/CrossReferenceTable.java
src/java/org/apache/fop/pdf/xref/TrailerDictionary.java
src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java
src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java
src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java
src/java/org/apache/fop/render/pdf/PDFPainter.java
src/java/org/apache/fop/render/pdf/PDFRendererConfig.java
src/java/org/apache/fop/render/pdf/PDFRendererOption.java
src/java/org/apache/fop/render/pdf/PDFRendererOptionsConfig.java
src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java
src/java/org/apache/fop/svg/PDFGraphics2D.java
test/java/org/apache/fop/pdf/AbstractPDFStreamTestCase.java
test/java/org/apache/fop/pdf/ObjectStreamManagerTestCase.java
test/java/org/apache/fop/pdf/ObjectStreamTestCase.java
test/java/org/apache/fop/pdf/PDFEncryptionJCETestCase.java
test/java/org/apache/fop/pdf/PDFLinearizationTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/pdf/PDFObjectTestCase.java
test/java/org/apache/fop/pdf/xref/CompressedObjectReferenceTestCase.java
test/java/org/apache/fop/pdf/xref/CrossReferenceObjectTest.java
test/java/org/apache/fop/pdf/xref/CrossReferenceStreamTestCase.java
test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java

index 331d4f7a59b710fd4b1fbf2b88d2d28edeb4e758..e550f7e240b67d01db3233f70bceb4918aa500a5 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.pdf;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Set;
 
 import org.apache.commons.io.output.CountingOutputStream;
 
@@ -36,7 +37,9 @@ public abstract class AbstractPDFStream extends PDFObject {
     /** The filters that should be applied */
     private PDFFilterList filters;
 
-    private final boolean encodeOnTheFly;
+    private boolean encodeOnTheFly;
+
+    private PDFNumber refLength = new PDFNumber();
 
     protected AbstractPDFStream() {
         this(true);
@@ -220,11 +223,11 @@ public abstract class AbstractPDFStream extends PDFObject {
         StringBuilder textBuffer = new StringBuilder(64);
 
         StreamCache encodedStream = null;
-        PDFNumber refLength = null;
         final Object lengthEntry;
         if (encodeOnTheFly) {
-            refLength = new PDFNumber();
-            getDocumentSafely().registerObject(refLength);
+            if (!refLength.hasObjectNumber()) {
+                registerChildren();
+            }
             lengthEntry = refLength;
         } else {
             encodedStream = encodeStream();
@@ -281,4 +284,18 @@ public abstract class AbstractPDFStream extends PDFObject {
     protected boolean multipleFiltersAllowed() {
         return true;
     }
+
+    @Override
+    public void getChildren(Set<PDFObject> children) {
+        dictionary.getChildren(children);
+        if (encodeOnTheFly) {
+            children.add(refLength);
+        }
+    }
+
+    public void registerChildren() {
+        if (encodeOnTheFly) {
+            getDocument().registerObject(refLength);
+        }
+    }
 }
index 2f7be57e6d5cb103b47e62bb9be15552326127ca..de6997f95fba97f7785ba19d9203b57a5e8918e9 100644 (file)
@@ -51,7 +51,7 @@ public class BitmapImage implements PDFImage {
      * @param mask the transparency mask reference if any
      */
     public BitmapImage(String k, int width, int height, byte[] data,
-                  String mask) {
+                  PDFReference mask) {
         this.key = k;
         this.height = height;
         this.width = width;
@@ -59,7 +59,7 @@ public class BitmapImage implements PDFImage {
         this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
         this.bitmaps = data;
         if (mask != null) {
-            maskRef = new PDFReference(mask);
+            maskRef = mask;
         }
     }
 
index 55d9c2953b192e833f54db6d00bf1cd2dd504062..270e672c1fd939a8f580a089857f84821b88969a 100644 (file)
@@ -35,7 +35,7 @@ interface CompressedObject {
      *
      * @return the object number.
      */
-    int getObjectNumber();
+    PDFObjectNumber getObjectNumber();
 
     /**
      * Outputs this object's content into the given stream.
index 65b327e3108cfc81982d4a4598f15994514bbefc..4259d0e67ae97cc0e210d0aaad9f3ed9ee4b9ca0 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.pdf;
 
 // Java
 import java.util.List;
+import java.util.Set;
 
 /**
  * class representing an object which is a list of annotations.
@@ -73,4 +74,9 @@ public class PDFAnnotList extends PDFObject {
      * 19 0 R
      * ]
      */
+
+    @Override
+    public void getChildren(Set<PDFObject> children) {
+        PDFDictionary.getChildren(links, children);
+    }
 }
index 78f5d080b0c8b8a8e96a4fa8761f589ef8ce44c5..36ff57799574877047f9f109615056005b918a61 100644 (file)
@@ -21,7 +21,9 @@ package org.apache.fop.pdf;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.commons.io.output.CountingOutputStream;
 
@@ -213,4 +215,14 @@ public class PDFArray extends PDFObject {
         return cout.getCount();
     }
 
+    @Override
+    public void getChildren(Set<PDFObject> children) {
+        List<Object> contents = new ArrayList<Object>();
+        for (Object c : values) {
+            if (!(c instanceof PDFReference)) {
+                contents.add(c);
+            }
+        }
+        PDFDictionary.getChildren(contents, children);
+    }
 }
index 907cab0dfe580407f6f7dd61722ee7f3a13c20f9..28908c6c395a62393435281c82bd1538bfb275f5 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.pdf;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.util.Set;
 
 import org.apache.fop.fonts.CIDFontType;
 
@@ -301,5 +302,20 @@ public class PDFCIDFont extends PDFObject {
         return bout.toByteArray();
     }
 
+    @Override
+    public void getChildren(Set<PDFObject> children) {
+        super.getChildren(children);
+        if (cidMap != null) {
+            children.add(cidMap);
+            cidMap.getChildren(children);
+        }
+        children.add(descriptor);
+        descriptor.getChildren(children);
+        if (cmap != null) {
+            children.add(cmap);
+            cmap.getChildren(children);
+        }
+    }
+
 }
 
index ae0b950fd385ecfbac26a1af85151cf6f3bf0704..d3e2e57d18f51fa2564f18ec3b3c9e9d55f63d18 100644 (file)
@@ -21,8 +21,12 @@ package org.apache.fop.pdf;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.commons.io.output.CountingOutputStream;
 
@@ -30,7 +34,7 @@ import org.apache.commons.io.output.CountingOutputStream;
  * Class representing a PDF dictionary object
  */
 public class PDFDictionary extends PDFObject {
-
+    private boolean visited;
     /**
      * the entry map
      */
@@ -134,4 +138,28 @@ public class PDFDictionary extends PDFObject {
         textBuffer.append(">>");
     }
 
+    @Override
+    public void getChildren(Set<PDFObject> children) {
+        if (!visited) {
+            visited = true;
+            Map<String, Object> childrenMap = new HashMap<String, Object>(entries);
+            childrenMap.remove("Parent");
+            getChildren(childrenMap.values(), children);
+            visited = false;
+        }
+    }
+
+    public static void getChildren(Collection<Object> values, Set<PDFObject> children) {
+        for (Object x : values) {
+            if (x instanceof PDFReference) {
+                x = ((PDFReference) x).getObject();
+            }
+            if (x instanceof PDFObject) {
+                if (((PDFObject) x).hasObjectNumber()) {
+                    children.add((PDFObject) x);
+                }
+                ((PDFObject) x).getChildren(children);
+            }
+        }
+    }
 }
index ccdd69a7a3d644b1aedbe62532f6569a520acd62..59ff9c988c7657eed79de2ac2804d766c7e3d7be 100644 (file)
@@ -78,18 +78,18 @@ public class PDFDocument {
     private Log log = LogFactory.getLog("org.apache.fop.pdf");
 
     /** the current character position */
-    private long position;
+    protected long position;
 
     /** the character position of each object */
-    private List<Long> indirectObjectOffsets = new ArrayList<Long>();
+    protected List<Long> indirectObjectOffsets = new ArrayList<Long>();
 
-    private Collection<PDFStructElem> structureTreeElements;
+    protected List<PDFStructElem> structureTreeElements;
 
     /** List of objects to write in the trailer */
-    private List<PDFObject> trailerObjects = new ArrayList<PDFObject>();
+    protected List<PDFObject> trailerObjects = new ArrayList<PDFObject>();
 
     /** the objects themselves */
-    private List<PDFObject> objects = new LinkedList<PDFObject>();
+    protected List<PDFObject> objects = new LinkedList<PDFObject>();
 
     /** Controls the PDF version of this document */
     private VersionController versionController;
@@ -128,6 +128,8 @@ public class PDFDocument {
     /** the counter for XObject numbering */
     private int xObjectCount;
 
+    protected int gStateObjectCount;
+
     /* TODO: Should be modified (works only for image subtype) */
     private Map<String, PDFXObject> xObjectsMap = new HashMap<String, PDFXObject>();
 
@@ -155,6 +157,8 @@ public class PDFDocument {
 
     private List<PDFLaunch> launches = new ArrayList<PDFLaunch>();
 
+    protected List<PDFPage> pageObjs = new ArrayList<PDFPage>();
+
     private List<PDFLayer> layers;
 
     private List<PDFNavigator> navigators;
@@ -169,6 +173,10 @@ public class PDFDocument {
 
     private boolean mergeFontsEnabled;
 
+    private boolean linearizationEnabled;
+
+    protected boolean outputStarted;
+
     /**
      * Creates an empty PDF document.
      *
@@ -347,6 +355,14 @@ public class PDFDocument {
         return this.root;
     }
 
+    /**
+     * Get the Structural Tree Collection for this document
+     * @return
+     */
+    public List<PDFStructElem> getStructureTreeElements() {
+        return structureTreeElements;
+    }
+
     /**
      * Creates and returns a StructTreeRoot object.
      *
@@ -398,6 +414,9 @@ public class PDFDocument {
     public PDFObject registerObject(PDFObject obj) {
         assignObjectNumber(obj);
         addObject(obj);
+        if (obj instanceof AbstractPDFStream) {
+            ((AbstractPDFStream) obj).registerChildren();
+        }
         return obj;
     }
 
@@ -421,6 +440,9 @@ public class PDFDocument {
      * @param obj {@link PDFObject} to assign a number to
      */
     public void assignObjectNumber(PDFObject obj) {
+        if (outputStarted && isLinearizationEnabled()) {
+            throw new IllegalStateException("Can't assign number after start of output");
+        }
         if (obj == null) {
             throw new NullPointerException("obj must not be null");
         }
@@ -436,7 +458,7 @@ public class PDFDocument {
                     + "PDFObject already has a parent PDFDocument");
         }
 
-        obj.setObjectNumber(++this.objectcount);
+        obj.setObjectNumber(this);
 
         if (currentParent == null) {
             obj.setDocument(this);
@@ -485,6 +507,7 @@ public class PDFDocument {
         }
         if (obj instanceof PDFPage) {
             this.pages.notifyKidRegistered((PDFPage)obj);
+            pageObjs.add((PDFPage) obj);
         }
         if (obj instanceof PDFLaunch) {
             this.launches.add((PDFLaunch) obj);
@@ -551,7 +574,7 @@ public class PDFDocument {
     public void setEncryption(PDFEncryptionParams params) {
         getProfile().verifyEncryptionAllowed();
         fileIDGenerator = FileIDGenerator.getRandomFileIDGenerator();
-        this.encryption = PDFEncryptionManager.newInstance(++this.objectcount, params, this);
+        this.encryption = PDFEncryptionManager.newInstance(params, this);
         if (this.encryption != null) {
             PDFObject pdfObject = (PDFObject)this.encryption;
             addTrailerObject(pdfObject);
@@ -965,6 +988,7 @@ public class PDFDocument {
      * @throws IOException if there is an exception writing to the output stream
      */
     public void output(OutputStream stream) throws IOException {
+        outputStarted = true;
         //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.
@@ -974,9 +998,29 @@ public class PDFDocument {
         }
     }
 
-    private void streamIndirectObject(PDFObject o, OutputStream stream) throws IOException {
+    protected void writeTrailer(OutputStream stream, int first, int last, int size, long mainOffset, long startxref)
+            throws IOException {
+        TrailerOutputHelper trailerOutputHelper = mayCompressStructureTreeElements()
+                ? new CompressedTrailerOutputHelper()
+                : new UncompressedTrailerOutputHelper();
+        if (structureTreeElements != null) {
+            trailerOutputHelper.outputStructureTreeElements(stream);
+        }
+        TrailerDictionary trailerDictionary = createTrailerDictionary(mainOffset != 0);
+        if (mainOffset != 0) {
+            trailerDictionary.getDictionary().put("Prev", mainOffset);
+        }
+        trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary, first, last, size);
+        String trailer = "\nstartxref\n" + startxref + "\n%%EOF\n";
+        stream.write(encode(trailer));
+    }
+
+    protected int streamIndirectObject(PDFObject o, OutputStream stream) throws IOException {
+        outputStarted = true;
         recordObjectOffset(o);
-        this.position += outputIndirectObject(o, stream);
+        int len = outputIndirectObject(o, stream);
+        this.position += len;
+        return len;
     }
 
     private void streamIndirectObjects(Collection<? extends PDFObject> objects, OutputStream stream)
@@ -987,7 +1031,7 @@ public class PDFDocument {
     }
 
     private void recordObjectOffset(PDFObject object) {
-        int index = object.getObjectNumber() - 1;
+        int index = object.getObjectNumber().getNumber() - 1;
         while (indirectObjectOffsets.size() <= index) {
             indirectObjectOffsets.add(null);
         }
@@ -1076,23 +1120,26 @@ public class PDFDocument {
             trailerOutputHelper.outputStructureTreeElements(stream);
         }
         streamIndirectObjects(trailerObjects, stream);
-        TrailerDictionary trailerDictionary = createTrailerDictionary();
-        long startxref = trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary);
+        TrailerDictionary trailerDictionary = createTrailerDictionary(true);
+        long startxref = trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary, 0,
+                indirectObjectOffsets.size(), indirectObjectOffsets.size());
         String trailer = "\nstartxref\n" + startxref + "\n%%EOF\n";
         stream.write(encode(trailer));
     }
 
     private boolean mayCompressStructureTreeElements() {
         return accessibilityEnabled
-                && versionController.getPDFVersion().compareTo(Version.V1_5) >= 0;
+                && versionController.getPDFVersion().compareTo(Version.V1_5) >= 0
+                && !isLinearizationEnabled();
     }
 
-    private TrailerDictionary createTrailerDictionary() {
+    private TrailerDictionary createTrailerDictionary(boolean addRoot) {
         FileIDGenerator gen = getFileIDGenerator();
-        TrailerDictionary trailerDictionary = new TrailerDictionary(this)
-                .setRoot(root)
-                .setInfo(info)
-                .setFileID(gen.getOriginalFileID(), gen.getUpdatedFileID());
+        TrailerDictionary trailerDictionary = new TrailerDictionary(this);
+        if (addRoot) {
+            trailerDictionary.setRoot(root).setInfo(info);
+        }
+        trailerDictionary.setFileID(gen.getOriginalFileID(), gen.getUpdatedFileID());
         if (isEncryptionActive()) {
             trailerDictionary.setEncryption(encryption);
         }
@@ -1117,7 +1164,8 @@ public class PDFDocument {
         /**
          * @return the offset of the cross-reference object (the value of startxref)
          */
-        long outputCrossReferenceObject(OutputStream stream, TrailerDictionary trailerDictionary)
+        long outputCrossReferenceObject(OutputStream stream, TrailerDictionary trailerDictionary,
+                                        int first, int last, int size)
                 throws IOException;
     }
 
@@ -1129,9 +1177,9 @@ public class PDFDocument {
         }
 
         public long outputCrossReferenceObject(OutputStream stream,
-                TrailerDictionary trailerDictionary) throws IOException {
+                TrailerDictionary trailerDictionary, int first, int last, int size) throws IOException {
             new CrossReferenceTable(trailerDictionary, position,
-                    indirectObjectOffsets).output(stream);
+                    indirectObjectOffsets, first, last, size).output(stream);
             return position;
         }
     }
@@ -1150,7 +1198,7 @@ public class PDFDocument {
         }
 
         public long outputCrossReferenceObject(OutputStream stream,
-                TrailerDictionary trailerDictionary) throws IOException {
+                TrailerDictionary trailerDictionary, int first, int last, int size) throws IOException {
             // Outputting the object streams should not have created new indirect objects
             assert objects.isEmpty();
             new CrossReferenceStream(PDFDocument.this, ++objectcount, trailerDictionary, position,
@@ -1175,4 +1223,13 @@ public class PDFDocument {
         }
         return fileIDGenerator;
     }
+
+    public boolean isLinearizationEnabled() {
+        return linearizationEnabled;
+    }
+
+    public void setLinearizationEnabled(boolean b) {
+        linearizationEnabled = b;
+    }
+
 }
index 63b31d9e0ae2899e9f984abef0cff31ea4386796..ff2aac68f44ed9b0363f32a67d4a8beed3ebc754 100644 (file)
@@ -621,11 +621,11 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
 
     private class EncryptionFilter extends PDFFilter {
 
-        private int streamNumber;
+        private PDFObjectNumber streamNumber;
 
         private int streamGeneration;
 
-        EncryptionFilter(int streamNumber, int streamGeneration) {
+        EncryptionFilter(PDFObjectNumber streamNumber, int streamGeneration) {
             this.streamNumber  = streamNumber;
             this.streamGeneration = streamGeneration;
         }
@@ -658,7 +658,7 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
                 out.flush();
                 return new CipherOutputStream(out, cipher);
             } else {
-                byte[] key = createEncryptionKey(streamNumber, streamGeneration);
+                byte[] key = createEncryptionKey(streamNumber.getNumber(), streamGeneration);
                 Cipher cipher = initCipher(key);
                 return new CipherOutputStream(out, cipher);
             }
@@ -666,7 +666,7 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
 
     }
 
-    private PDFEncryptionJCE(int objectNumber, PDFEncryptionParams params, PDFDocument pdf) {
+    private PDFEncryptionJCE(PDFObjectNumber objectNumber, PDFEncryptionParams params, PDFDocument pdf) {
         setObjectNumber(objectNumber);
         try {
             if (params.getEncryptionLengthInBits() == 256) {
@@ -692,7 +692,7 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
      * @return the newly created encryption object
      */
     public static PDFEncryption make(
-            int objectNumber, PDFEncryptionParams params, PDFDocument pdf) {
+            PDFObjectNumber objectNumber, PDFEncryptionParams params, PDFDocument pdf) {
         return new PDFEncryptionJCE(objectNumber, params, pdf);
     }
 
@@ -714,7 +714,7 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
             System.arraycopy(encryptedData, 0, storedData, 16, encryptedData.length);
             return storedData;
         } else {
-            byte[] key = createEncryptionKey(o.getObjectNumber(), o.getGeneration());
+            byte[] key = createEncryptionKey(o.getObjectNumber().getNumber(), o.getGeneration());
             return encryptWithKey(key, data);
         }
     }
index 6e57b1518999765a4fc55f726f110055e83140db..0bf2ca2e2cbbd8335b19d7223b01cb199a745483 100644 (file)
@@ -107,20 +107,21 @@ 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(PDFEncryptionParams params,
             PDFDocument pdf) {
         try {
+            PDFObjectNumber pdfObjectNumber = new PDFObjectNumber();
+            pdfObjectNumber.setDocument(pdf);
             Class<?> clazz = Class.forName("org.apache.fop.pdf.PDFEncryptionJCE");
             Method makeMethod = clazz.getMethod("make",
-                        new Class[] {int.class, PDFEncryptionParams.class, PDFDocument.class});
+                        new Class[] {PDFObjectNumber.class, PDFEncryptionParams.class, PDFDocument.class});
             Object obj = makeMethod.invoke(null,
-                        new Object[] {new Integer(objnum), params, pdf});
+                        new Object[] {pdfObjectNumber, params, pdf});
             return (PDFEncryption)obj;
         } catch (ClassNotFoundException e) {
             if (checkAvailableAlgorithms()) {
index 5ab483f608cb599bf4d27b3c726026454f513543..017761754ddc275bc18731db7418b335b5287037 100644 (file)
@@ -105,7 +105,7 @@ public class PDFFactory {
      */
     public PDFRoot makeRoot(PDFPages pages) {
         //Make a /Pages object. This object is written in the trailer.
-        PDFRoot pdfRoot = new PDFRoot(++this.document.objectcount, pages);
+        PDFRoot pdfRoot = new PDFRoot(document, pages);
         pdfRoot.setDocument(getDocument());
         getDocument().addTrailerObject(pdfRoot);
         return pdfRoot;
@@ -117,7 +117,7 @@ public class PDFFactory {
      * @return a new PDF Pages object for adding pages to
      */
     public PDFPages makePages() {
-        PDFPages pdfPages = new PDFPages(++(this.document.objectcount));
+        PDFPages pdfPages = new PDFPages(getDocument());
         pdfPages.setDocument(getDocument());
         getDocument().addTrailerObject(pdfPages);
         return pdfPages;
@@ -129,7 +129,7 @@ public class PDFFactory {
      * @return a new PDF resources object
      */
     public PDFResources makeResources() {
-        PDFResources pdfResources = new PDFResources(++this.document.objectcount);
+        PDFResources pdfResources = new PDFResources(getDocument());
         pdfResources.setDocument(getDocument());
         getDocument().addTrailerObject(pdfResources);
         return pdfResources;
@@ -197,7 +197,14 @@ public class PDFFactory {
          * create a PDFPage with the next object number, the given
          * resources, contents and dimensions
          */
-        PDFPage page = new PDFPage(resources, pageIndex, mediaBox, cropBox, bleedBox, trimBox);
+        PDFResources res = getDocument().getFactory().makeResources();
+        if (getDocument().isLinearizationEnabled()) {
+            getDocument().trailerObjects.remove(resources);
+        }
+        res.setParentResources(resources);
+
+        PDFPage page = new PDFPage(res, pageIndex, mediaBox, cropBox, bleedBox, trimBox);
+        res.setParent(page);
 
         getDocument().assignObjectNumber(page);
         getDocument().getPages().addPage(page);
@@ -763,7 +770,7 @@ public class PDFFactory {
      * @return the new PDF outline object
      */
     public PDFOutline makeOutline(PDFOutline parent, String label,
-                                  String actionRef, boolean showSubItems) {
+                                  PDFReference actionRef, boolean showSubItems) {
         PDFOutline pdfOutline = new PDFOutline(label, actionRef, showSubItems);
         if (parent != null) {
             parent.addOutline(pdfOutline);
@@ -785,7 +792,7 @@ public class PDFFactory {
                                   PDFAction pdfAction, boolean showSubItems) {
         return pdfAction == null
                  ? null
-                 : makeOutline(parent, label, pdfAction.getAction(), showSubItems);
+                 : makeOutline(parent, label, new PDFReference(pdfAction.getAction()), showSubItems);
     }
 
     // This one is obsolete now, at least it isn't called from anywhere inside FOP
@@ -804,7 +811,7 @@ public class PDFFactory {
                                   boolean showSubItems) {
 
         String goToRef = getGoToReference(destination, yoffset);
-        return makeOutline(parent, label, goToRef, showSubItems);
+        return makeOutline(parent, label, new PDFReference(goToRef), showSubItems);
     }
 
 
index 540917b60dd94a0d6d004f1f9be0c48e609ccdc4..094b7c246584b5f74e7412b4ca40256342a663a4 100644 (file)
@@ -105,13 +105,17 @@ public class PDFGState extends PDFObject {
     }
 
     private Map values = new java.util.HashMap();
+    private int objNum;
 
     /**
      * Returns the name of this object
      * @return the name
      */
     public String getName() {
-        return "GS" + getObjectNumber();
+        if (objNum == 0) {
+            objNum = ++getDocument().gStateObjectCount;
+        }
+        return "GS" + objNum;
     }
 
     /**
index 89beac236fa63002afb93f138634ff931b6591a3..695a5b73cd82bb505add79d299184e992366e180 100644 (file)
@@ -31,7 +31,7 @@ public class PDFGoTo extends PDFAction {
     /**
      * the pageReference
      */
-    private String pageReference;
+    private PDFReference pageReference;
     private String destination;
     private float xPosition;
     private float yPosition;
@@ -43,7 +43,9 @@ public class PDFGoTo extends PDFAction {
      */
     public PDFGoTo(String pageReference) {
         super();
-        setPageReference(pageReference);
+        if (pageReference != null) {
+            setPageReference(new PDFReference(pageReference));
+        }
     }
 
     /**
@@ -63,7 +65,7 @@ public class PDFGoTo extends PDFAction {
      *
      * @param pageReference the new page reference to use
      */
-    public void setPageReference(String pageReference) {
+    public void setPageReference(PDFReference pageReference) {
         this.pageReference = pageReference;
     }
 
index b86ba29f5453af54f87988997c67910fa35e03fe..9635917ce0cb61f759177238af2311a5e48c7f78 100644 (file)
@@ -23,6 +23,7 @@ import java.awt.color.ColorSpace;
 import java.awt.color.ICC_Profile;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Set;
 
 import org.apache.commons.io.IOUtils;
 
@@ -155,4 +156,10 @@ public class PDFICCBasedColorSpace extends PDFObject implements PDFColorSpace {
         return sRGBProfile;
     }
 
+    @Override
+    public void getChildren(Set<PDFObject> children) {
+        super.getChildren(children);
+        children.add(iccStream);
+        iccStream.getChildren(children);
+    }
 }
index 1c28cb2a75ae173fdf6ffee07f756aff169ec43f..e472efbea41501f53509b220990b1f7bb1ad74fe 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.fop.pdf;
 // Java
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Set;
 
 /* modified by JKT to integrate with 0.12.0 */
 /* modified by Eric SCHAEFFER to integrate with 0.13.0 */
@@ -171,4 +172,14 @@ public class PDFImageXObject extends PDFXObject {
         return pdfimage.multipleFiltersAllowed();
     }
 
+    @Override
+    public void getChildren(Set<PDFObject> children) {
+        super.getChildren(children);
+        PDFICCStream pdfICCStream = pdfimage.getICCStream();
+        if (pdfICCStream != null) {
+            children.add(pdfICCStream);
+            pdfICCStream.getChildren(children);
+        }
+    }
+
 }
index dc7c3bdcaefa68ac76d2f4731d1f3a7a3ba25368..97bca903812e44d458c374197b4095fb3b6fd2ed 100644 (file)
@@ -22,7 +22,7 @@ package org.apache.fop.pdf;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.Date;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.TimeZone;
 
@@ -317,7 +317,7 @@ public class PDFInfo extends PDFObject {
             throw new IllegalArgumentException(key + " is a reserved keyword");
         }
         if (customProperties == null) {
-            customProperties = new HashMap<PDFName, String>();
+            customProperties = new LinkedHashMap<PDFName, String>();
         }
         customProperties.put(new PDFName(key), value);
     }
diff --git a/src/java/org/apache/fop/pdf/PDFLinearization.java b/src/java/org/apache/fop/pdf/PDFLinearization.java
new file mode 100644 (file)
index 0000000..fb12d27
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+package org.apache.fop.pdf;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.io.output.CountingOutputStream;
+
+public class PDFLinearization {
+    private PDFDocument doc;
+    private Map<PDFPage, Set<PDFObject>> pageObjsMap = new HashMap<PDFPage, Set<PDFObject>>();
+    private PDFDictionary linearDict;
+    private HintTable hintTable;
+
+    public PDFLinearization(PDFDocument doc) {
+        this.doc = doc;
+    }
+
+    static class HintTable extends PDFStream {
+        private List<PDFPage> pages;
+        int pageStartPos;
+        List<Integer> sharedLengths = new ArrayList<Integer>();
+        List<Integer> pageLengths = new ArrayList<Integer>();
+        List<Integer> contentStreamLengths = new ArrayList<Integer>();
+        List<Integer> objCount = new ArrayList<Integer>();
+        Map<String, int[]> hintGroups = new HashMap<String, int[]>();
+
+        public HintTable(PDFDocument doc) {
+            super(false);
+            doc.assignObjectNumber(this);
+            doc.addObject(this);
+            pages = doc.pageObjs;
+            for (int i = 0; i < pages.size(); i++) {
+                pageLengths.add(0);
+                contentStreamLengths.add(0);
+                objCount.add(0);
+            }
+            hintGroups.put("/C", new int[4]);
+            hintGroups.put("/L", new int[4]);
+            hintGroups.put("/I", new int[4]);
+            hintGroups.put("/E", new int[4]);
+            hintGroups.put("/O", new int[4]);
+            hintGroups.put("/V", new int[4]);
+        }
+
+        @Override
+        public PDFFilterList getFilterList() {
+            return new PDFFilterList(getDocument().isEncryptionActive());
+        }
+
+        @Override
+        protected void outputRawStreamData(OutputStream os) throws IOException {
+            CountingOutputStream bos = new CountingOutputStream(os);
+
+            //start header
+            writeULong(1, bos); //1
+            writeULong(pageStartPos, bos); //2
+            writeCard16(32, bos); //3
+            writeULong(0, bos); //4
+            writeCard16(32, bos); //5
+            writeULong(0, bos); //6
+            writeCard16(0, bos); //7
+            writeULong(0, bos); //8
+            writeCard16(32, bos); //9
+            writeCard16(0, bos); //10
+            writeCard16(0, bos); //11
+            writeCard16(0, bos); //12
+            writeCard16(4, bos); //13
+            //end header
+
+            for (PDFPage page : pages) {
+                writeULong(objCount.get(page.pageIndex) - 1, bos);
+            }
+            for (PDFPage page : pages) {
+                writeULong(pageLengths.get(page.pageIndex), bos);
+            }
+            for (PDFPage page : pages) {
+                writeULong(contentStreamLengths.get(page.pageIndex), bos);
+            }
+
+            writeSharedTable(bos);
+
+            for (Map.Entry<String, int[]> group : hintGroups.entrySet()) {
+                put(group.getKey(), bos.getCount());
+                for (int i : group.getValue()) {
+                    writeULong(i, bos);
+                }
+                if (group.getKey().equals("/C")) {
+                    writeULong(0, bos);
+                    writeCard16(0, bos);
+                }
+            }
+        }
+
+        private void writeSharedTable(CountingOutputStream bos) throws IOException {
+            put("/S", bos.getCount());
+
+            //Shared object hint table, header section
+            writeULong(0, bos); //1
+            writeULong(0, bos); //2
+            writeULong(sharedLengths.size(), bos); //3
+            writeULong(sharedLengths.size(), bos); //4
+            writeCard16(0, bos); //5
+            writeULong(0, bos); //6
+            writeCard16(32, bos); //7
+
+            for (int i : sharedLengths) {
+                writeULong(i, bos);
+            }
+            writeULong(0, bos);
+        }
+
+        private void writeCard16(int s, OutputStream bos) throws IOException {
+            byte b1 = (byte)((s >> 8) & 0xff);
+            byte b2 = (byte)(s & 0xff);
+            bos.write(b1);
+            bos.write(b2);
+        }
+
+        private void writeULong(int s, OutputStream bos) throws IOException {
+            byte b1 = (byte)((s >> 24) & 0xff);
+            byte b2 = (byte)((s >> 16) & 0xff);
+            byte b3 = (byte)((s >> 8) & 0xff);
+            byte b4 = (byte)(s & 0xff);
+            bos.write(b1);
+            bos.write(b2);
+            bos.write(b3);
+            bos.write(b4);
+        }
+    }
+
+    static class LinearPDFDictionary extends PDFDictionary {
+        private int lastsize = -1;
+
+        public LinearPDFDictionary(PDFDocument doc) {
+            put("Linearized", 1);
+            put("/L", 0);
+            PDFArray larray = new PDFArray();
+            larray.add(0);
+            larray.add(0);
+            put("/H", larray);
+            doc.assignObjectNumber(this);
+            getObjectNumber().getNumber();
+            put("/O", getObjectNumber().getNumber() + 3);
+            put("/E", 0);
+            put("/N", doc.pageObjs.size());
+            put("/T", 0);
+        }
+
+        public int output(OutputStream stream) throws IOException {
+            int size = super.output(stream);
+            int padding = lastsize - size + 32;
+            if (lastsize == -1) {
+                padding = 32;
+                lastsize = size;
+            }
+            writePadding(padding, stream);
+            return size + padding;
+        }
+    }
+
+
+    private Set<PDFObject> assignNumbers() throws IOException {
+        Set<PDFObject> page1Children = getPage1Children();
+        if (!doc.pageObjs.isEmpty()) {
+            for (int i = 1; i < doc.pageObjs.size(); i++) {
+                PDFPage page = doc.pageObjs.get(i);
+                Set<PDFObject> children = pageObjsMap.get(page);
+                for (PDFObject c : children) {
+                    if (!page1Children.contains(c) && c.hasObjectNumber()) {
+                        c.getObjectNumber().getNumber();
+                    }
+                }
+            }
+            for (PDFObject o : doc.objects) {
+                if (o instanceof PDFDests || o instanceof PDFOutline) {
+                    for (PDFObject c : getChildren(o)) {
+                        c.getObjectNumber().getNumber();
+                    }
+                }
+                if (o instanceof PDFInfo || o instanceof PDFPageLabels) {
+                    o.getObjectNumber().getNumber();
+                }
+            }
+            for (PDFObject o : doc.objects) {
+                if (!page1Children.contains(o)) {
+                    o.getObjectNumber().getNumber();
+                }
+            }
+        }
+        linearDict = new LinearPDFDictionary(doc);
+        for (PDFObject o : page1Children) {
+            o.getObjectNumber().getNumber();
+        }
+        sort(doc.objects);
+        return page1Children;
+    }
+
+    private void sort(List<PDFObject> objects) {
+        Collections.sort(objects, new Comparator<PDFObject>() {
+            public int compare(PDFObject o1, PDFObject o2) {
+                return ((Integer) o1.getObjectNumber().getNumber()).compareTo(o2.getObjectNumber().getNumber());
+            }
+        });
+    }
+
+    private Set<PDFObject> getChildren(PDFObject o) {
+        Set<PDFObject> children = new LinkedHashSet<PDFObject>();
+        children.add(o);
+        o.getChildren(children);
+        return children;
+    }
+
+    public void outputPages(OutputStream stream) throws IOException {
+        Collections.sort(doc.pageObjs, new Comparator<PDFPage>() {
+            public int compare(PDFPage o1, PDFPage o2) {
+                return ((Integer) o1.pageIndex).compareTo(o2.pageIndex);
+            }
+        });
+        doc.objects.addAll(doc.trailerObjects);
+        doc.trailerObjects = null;
+        if (doc.getStructureTreeElements() != null) {
+            doc.objects.addAll(doc.getStructureTreeElements());
+            doc.structureTreeElements = null;
+        }
+        for (int i = 0; i < doc.objects.size() * 2; i++) {
+            doc.indirectObjectOffsets.add(0L);
+        }
+        Set<PDFObject> page1Children = assignNumbers();
+        doc.streamIndirectObject(linearDict, new ByteArrayOutputStream());
+        for (PDFObject o : page1Children) {
+            doc.objects.remove(o);
+        }
+        int sizeOfRest = doc.objects.size();
+
+        ByteArrayOutputStream fakeHeaderTrailerStream = new ByteArrayOutputStream();
+        long topTrailer = doc.position;
+        doc.writeTrailer(fakeHeaderTrailerStream, sizeOfRest, page1Children.size() + 1,
+                page1Children.size() + sizeOfRest + 1, Long.MAX_VALUE, 0);
+        doc.position += fakeHeaderTrailerStream.size();
+
+        ByteArrayOutputStream pageStream = new ByteArrayOutputStream();
+        writeObjects(page1Children, pageStream, sizeOfRest + 1);
+        long trailerOffset = doc.position;
+        ByteArrayOutputStream footerTrailerStream = new ByteArrayOutputStream();
+        doc.writeTrailer(footerTrailerStream, 0, sizeOfRest, sizeOfRest, 0, topTrailer);
+        doc.position += footerTrailerStream.size();
+
+        linearDict.put("/L", doc.position);
+
+        PDFDocument.outputIndirectObject(linearDict, stream);
+        CountingOutputStream realTrailer = new CountingOutputStream(stream);
+        doc.writeTrailer(realTrailer, sizeOfRest, page1Children.size() + 1,
+                page1Children.size() + sizeOfRest + 1, trailerOffset, 0);
+        writePadding(fakeHeaderTrailerStream.size() - realTrailer.getCount(), stream);
+        for (PDFObject o : page1Children) {
+            PDFDocument.outputIndirectObject(o, stream);
+            if (o instanceof HintTable) {
+                break;
+            }
+        }
+        stream.write(pageStream.toByteArray());
+        stream.write(footerTrailerStream.toByteArray());
+    }
+
+    private Set<PDFObject> getPage1Children() throws IOException {
+        Set<PDFObject> page1Children = new LinkedHashSet<PDFObject>();
+        if (!doc.pageObjs.isEmpty()) {
+            PDFPage page1 = doc.pageObjs.get(0);
+            page1Children.add(doc.getRoot());
+            hintTable = new HintTable(doc);
+            page1Children.add(hintTable);
+            page1Children.add(page1);
+            page1.getChildren(page1Children);
+            doc.objects.remove(doc.getPages());
+            doc.objects.add(0, doc.getPages());
+            pageObjsMap.put(page1, page1Children);
+
+            for (int i = 1; i < doc.pageObjs.size(); i++) {
+                PDFPage page = doc.pageObjs.get(i);
+                pageObjsMap.put(page, getChildren(page));
+            }
+        }
+        return page1Children;
+    }
+
+    private static void writePadding(int padding, OutputStream stream) throws IOException {
+        for (int i = 0; i < padding; i++) {
+            stream.write(" ".getBytes("UTF-8"));
+        }
+    }
+
+    private void writeObjects(Set<PDFObject> children1, OutputStream pageStream, int sizeOfRest) throws IOException {
+        writePage1(children1, pageStream);
+        linearDict.put("/E", doc.position);
+        for (PDFPage page : doc.pageObjs) {
+            if (page.pageIndex != 0) {
+                writePage(page, pageStream);
+            }
+        }
+        while (!doc.objects.isEmpty()) {
+            PDFObject o = doc.objects.remove(0);
+            if (o instanceof PDFOutline) {
+                writeObjectGroup("/O", getChildren(o), pageStream);
+            } else if (o instanceof PDFDests) {
+                writeObjectGroup("/E", getChildren(o), pageStream);
+            } else if (o instanceof PDFInfo) {
+                writeObjectGroup("/I", getChildren(o), pageStream);
+            } else if (o instanceof PDFPageLabels) {
+                writeObjectGroup("/L", getChildren(o), pageStream);
+            } else if (o instanceof PDFStructTreeRoot) {
+                writeObjectGroup("/C", getChildren(o), pageStream);
+            } else {
+                doc.streamIndirectObject(o, pageStream);
+            }
+        }
+        linearDict.put("/T", doc.position + 8 + String.valueOf(sizeOfRest).length());
+    }
+
+    private void writeObjectGroup(String name, Set<PDFObject> objects, OutputStream pageStream)
+            throws IOException {
+        List<PDFObject> children = new ArrayList<PDFObject>(objects);
+        sort(children);
+
+        int[] values = hintTable.hintGroups.get(name);
+        values[0] = children.iterator().next().getObjectNumber().getNumber();
+        values[1] = (int) doc.position;
+        values[2] = children.size();
+        for (PDFObject o : children) {
+            values[3] += doc.streamIndirectObject(o, pageStream);
+            doc.objects.remove(o);
+        }
+    }
+
+    private void writePage1(Set<PDFObject> children1, OutputStream pageStream) throws IOException {
+        hintTable.pageStartPos = (int) doc.position;
+        OutputStream stream = new ByteArrayOutputStream();
+
+        Set<PDFObject> sharedChildren = getSharedObjects();
+
+        int page1Len = 0;
+        int objCount = 0;
+        int sharedCount = 0;
+        for (PDFObject o : children1) {
+            if (o instanceof HintTable) {
+                PDFArray a = (PDFArray) linearDict.get("/H");
+                a.set(0, doc.position);
+                doc.streamIndirectObject(o, stream);
+                a.set(1, doc.position - (Double)a.get(0));
+                stream = pageStream;
+            } else {
+                int len = doc.streamIndirectObject(o, stream);
+                if (o instanceof PDFStream && hintTable.contentStreamLengths.get(0) == 0) {
+                    hintTable.contentStreamLengths.set(0, len);
+                }
+                if (!(o instanceof PDFRoot)) {
+                    page1Len += len;
+                    objCount++;
+                }
+                if (sharedChildren.contains(o)) {
+                    hintTable.sharedLengths.set(sharedCount, len);
+                    sharedCount++;
+                }
+            }
+        }
+        hintTable.pageLengths.set(0, page1Len);
+        hintTable.objCount.set(0, objCount);
+    }
+
+    private Set<PDFObject> getSharedObjects() {
+        Set<PDFObject> pageSharedChildren = getChildren(doc.pageObjs.get(0));
+        for (int i = 0; i < pageSharedChildren.size(); i++) {
+            hintTable.sharedLengths.add(0);
+        }
+        return pageSharedChildren;
+    }
+
+    private void writePage(PDFPage page, OutputStream pageStream) throws IOException {
+        Set<PDFObject> children = pageObjsMap.get(page);
+        int pageLen = 0;
+        int objCount = 0;
+        for (PDFObject c : children) {
+            if (doc.objects.contains(c)) {
+                int len = doc.streamIndirectObject(c, pageStream);
+                if (c instanceof PDFStream) {
+                    hintTable.contentStreamLengths.set(page.pageIndex, len);
+                }
+                pageLen += len;
+                doc.objects.remove(c);
+                objCount++;
+            }
+        }
+        hintTable.pageLengths.set(page.pageIndex, pageLen);
+        hintTable.objCount.set(page.pageIndex, objCount);
+    }
+}
index f35a15e57c49b74d55a803b362e588f65d093f9c..fd74c100ae1f87d77b4e06bef4ed35311bc652f7 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.pdf;
 
 // Java
 import java.awt.geom.Rectangle2D;
+import java.util.Set;
 
 /**
  * class representing an /Annot object of /Subtype /Link
@@ -142,5 +143,14 @@ public class PDFLink extends PDFObject {
 
         return true;
     }
+
+    @Override
+    public void getChildren(Set<PDFObject> children) {
+        super.getChildren(children);
+        if (action.hasObjectNumber()) {
+            children.add(action);
+        }
+        action.getChildren(children);
+    }
 }
 
index 7ec1e2407af9aa73b99c6dd981710cd2f1274c40..12402d72c6eeb53e3a5caf43b9c4f166f4d46bee 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.fop.pdf;
 // Java
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Set;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -41,7 +42,8 @@ public abstract class PDFObject implements PDFWritable {
     /**
      * the object's number
      */
-    private int objnum;
+    private boolean hasObjNum;
+    private PDFObjectNumber objNum = new PDFObjectNumber();
 
     /**
      * the object's generation (0 in new documents)
@@ -60,11 +62,11 @@ public abstract class PDFObject implements PDFWritable {
      * Returns the object's number.
      * @return the PDF Object number
      */
-    public int getObjectNumber() {
-        if (this.objnum == 0) {
+    public PDFObjectNumber getObjectNumber() {
+        if (!hasObjNum) {
             throw new IllegalStateException("Object has no number assigned: " + this.toString());
         }
-        return this.objnum;
+        return objNum;
     }
 
     /**
@@ -88,23 +90,33 @@ public abstract class PDFObject implements PDFWritable {
      * @return True if it has an object number
      */
     public boolean hasObjectNumber() {
-        return this.objnum > 0;
+        return hasObjNum;
     }
 
     /**
      * Sets the object number
-     * @param objnum the object number
      */
-    public void setObjectNumber(int objnum) {
-        this.objnum = objnum;
+    public void setObjectNumber(PDFDocument document) {
+        objNum.setDocument(document);
+        hasObjNum = true;
         PDFDocument doc = getDocument();
         setParent(null);
         setDocument(doc); //Restore reference to PDFDocument after setting parent to null
         if (log.isTraceEnabled()) {
-            log.trace("Assigning " + this + " object number " + objnum);
+            log.trace("Assigning " + this + " object number " + objNum);
         }
     }
 
+    public void setObjectNumber(PDFObjectNumber objectNumber) {
+        objNum = objectNumber;
+        hasObjNum = true;
+    }
+
+    public void setObjectNumber(int objectNumber) {
+        objNum = new PDFObjectNumber(objectNumber);
+        hasObjNum = true;
+    }
+
     /**
      * Returns this object's generation.
      * @return the PDF Object generation
@@ -184,8 +196,7 @@ public abstract class PDFObject implements PDFWritable {
             throw new IllegalArgumentException(
                     "Cannot reference this object. It doesn't have an object number");
         }
-        String ref = getObjectNumber() + " " + getGeneration() + " R";
-        return ref;
+        return makeReference().toString();
     }
 
     /**
@@ -344,4 +355,7 @@ public abstract class PDFObject implements PDFWritable {
     protected boolean contentEquals(PDFObject o) {
         return this.equals(o);
     }
+
+    public void getChildren(Set<PDFObject> children) {
+    }
 }
diff --git a/src/java/org/apache/fop/pdf/PDFObjectNumber.java b/src/java/org/apache/fop/pdf/PDFObjectNumber.java
new file mode 100644 (file)
index 0000000..d26823f
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+package org.apache.fop.pdf;
+
+public class PDFObjectNumber {
+    private int num;
+    private PDFDocument doc;
+
+    public PDFObjectNumber() {
+    }
+    public PDFObjectNumber(int num) {
+        this.num = num;
+    }
+
+    public void setDocument(PDFDocument doc) {
+        this.doc = doc;
+    }
+
+    public int getNumber() {
+        if (num == 0 && doc != null) {
+//            assert doc.outputStarted;
+            num = ++doc.objectcount;
+        }
+        return num;
+    }
+
+    public String toString() {
+        return String.valueOf(getNumber());
+    }
+}
index 8edea457fdb3f9050d9ebebaa98f15d5e804bd2e..c43db61aa520e44a250544ce39a5cb3368c3b941 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.fop.pdf;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.List;
+import java.util.Set;
 
 /**
  * <p>This represents a single Outline object in a PDF, including the root Outlines
@@ -58,7 +59,7 @@ public class PDFOutline extends PDFObject {
      */
     private String title;
 
-    private String actionRef;
+    private PDFReference actionRef;
 
     /**
      * Create a PDF outline with the title and action.
@@ -67,7 +68,7 @@ public class PDFOutline extends PDFObject {
      * @param action the action for this outline
      * @param openItem indicator of whether child items are visible or not
      */
-    public PDFOutline(String title, String action, boolean openItem) {
+    public PDFOutline(String title, PDFReference action, boolean openItem) {
         super();
         subentries = new java.util.ArrayList();
         count = 0;
@@ -170,4 +171,19 @@ public class PDFOutline extends PDFObject {
         return bout.toByteArray();
     }
 
+    @Override
+    public void getChildren(Set<PDFObject> children) {
+        if (parent != null) {
+            children.add(parent);
+        }
+        if (first != null && last != null) {
+            children.add(first);
+            children.add(last);
+            first.getChildren(children);
+            last.getChildren(children);
+        }
+        if (actionRef != null) {
+            children.add(actionRef.getObject());
+        }
+    }
 }
index b565949262f7eb9fb0a988e9d004874d8b88911b..d335bde525d7e267f289223546aa3212ec663015 100644 (file)
@@ -50,11 +50,9 @@ public class PDFPages extends PDFObject {
      * It must also be allocated an object ID (so that the kids
      * can refer to the parent) so that the XRef table needs to
      * be updated before this object is written.
-     *
-     * @param objnum the object's number
      */
-    public PDFPages(int objnum) {
-        setObjectNumber(objnum);
+    public PDFPages(PDFDocument document) {
+        setObjectNumber(document);
     }
 
     /**
@@ -81,9 +79,9 @@ public class PDFPages extends PDFObject {
                 throw new IllegalStateException("A page already exists at index "
                         + idx + " (zero-based).");
             }
-            this.kids.set(idx, page.referencePDF());
+            this.kids.set(idx, page.makeReference());
         } else {
-            this.kids.add(page.referencePDF());
+            this.kids.add(page.makeReference());
         }
     }
 
index 29f8c7c4844c3b86a9a4e5323b1e5b5a96b20e1d..ca704978b155724e0d01a18bbf1c2c6a50f2d2df 100644 (file)
@@ -31,7 +31,7 @@ import java.lang.ref.SoftReference;
  */
 public class PDFReference implements PDFWritable {
 
-    private int objectNumber;
+    private PDFObjectNumber objectNumber;
     private int generation;
 
     private Reference<PDFObject> objReference;
@@ -56,7 +56,7 @@ public class PDFReference implements PDFWritable {
         }
         String[] parts = ref.split(" ");
         assert parts.length == 3;
-        this.objectNumber = Integer.parseInt(parts[0]);
+        this.objectNumber = new PDFObjectNumber(Integer.parseInt(parts[0]));
         this.generation = Integer.parseInt(parts[1]);
         assert "R".equals(parts[2]);
     }
@@ -81,7 +81,7 @@ public class PDFReference implements PDFWritable {
      * Returns the object number.
      * @return the object number
      */
-    public int getObjectNumber() {
+    public PDFObjectNumber getObjectNumber() {
         return this.objectNumber;
     }
 
@@ -103,7 +103,7 @@ public class PDFReference implements PDFWritable {
 
     /** {@inheritDoc} */
     public void outputInline(OutputStream out, StringBuilder textBuffer) {
-        textBuffer.append(getObjectNumber()).append(' ').append(getGeneration()).append(" R");
+        textBuffer.append(getObjectNumber().getNumber()).append(' ').append(getGeneration()).append(" R");
     }
 
 }
index f351b23beefdfdb35dce26117b632ff3f759c333..9ba9c7f8fb09464eab69279c35c78ef98659b00c 100644 (file)
@@ -52,7 +52,7 @@ public class PDFResources extends PDFDictionary {
      */
     protected Set<PDFXObject> xObjects = new LinkedHashSet<PDFXObject>();
     /** Map of color spaces (key: color space name) */
-    protected Map<PDFName, PDFColorSpace> colorSpaces = new LinkedHashMap<PDFName, PDFColorSpace>();
+    protected Map<LazyName, PDFColorSpace> colorSpaces = new LinkedHashMap<LazyName, PDFColorSpace>();
 
     /** Map of ICC color spaces (key: ICC profile description) */
     protected Map<String, PDFICCBasedColorSpace> iccColorSpaces = new LinkedHashMap<String, PDFICCBasedColorSpace>();
@@ -68,13 +68,11 @@ public class PDFResources extends PDFDictionary {
 
     /**
      * create a /Resources object.
-     *
-     * @param objnum the object's number
      */
-    public PDFResources(int objnum) {
+    public PDFResources(PDFDocument doc) {
         /* generic creation of object */
         super();
-        setObjectNumber(objnum);
+        setObjectNumber(doc);
     }
 
     public void addContext(PDFResourceContext c) {
@@ -156,7 +154,7 @@ public class PDFResources extends PDFDictionary {
      * @param colorSpace the color space
      */
     public void addColorSpace(PDFColorSpace colorSpace) {
-        this.colorSpaces.put(new PDFName(colorSpace.getName()), colorSpace);
+        this.colorSpaces.put(new LazyName(colorSpace), colorSpace);
         if (colorSpace instanceof PDFICCBasedColorSpace) {
             PDFICCBasedColorSpace icc = (PDFICCBasedColorSpace)colorSpace;
             String desc = ColorProfileUtil.getICCProfileDescription(
@@ -165,6 +163,16 @@ public class PDFResources extends PDFDictionary {
         }
     }
 
+    static class LazyName {
+        private PDFColorSpace colorSpace;
+        public LazyName(PDFColorSpace colorSpace) {
+            this.colorSpace = colorSpace;
+        }
+        public PDFName getName() {
+            return new PDFName(colorSpace.getName());
+        }
+    }
+
     /**
      * Returns a ICCBased color space by profile name.
      * @param desc the name of the color space
@@ -181,8 +189,12 @@ public class PDFResources extends PDFDictionary {
      * @return the requested color space or null if it wasn't found
      */
     public PDFColorSpace getColorSpace(PDFName name) {
-        PDFColorSpace cs = this.colorSpaces.get(name);
-        return cs;
+        for (Map.Entry<LazyName, PDFColorSpace> x : colorSpaces.entrySet()) {
+            if (x.getKey().getName().equals(name)) {
+                return x.getValue();
+            }
+        }
+        return null;
     }
 
     /**
@@ -303,4 +315,43 @@ public class PDFResources extends PDFDictionary {
         }
     }
 
+    @Override
+    public void getChildren(Set<PDFObject> children) {
+        getChildren(children, false);
+    }
+
+    private void getChildren(Set<PDFObject> children, boolean isParent) {
+        super.getChildren(children);
+        for (PDFDictionary f : fonts.values()) {
+            children.add(f);
+            f.getChildren(children);
+        }
+        for (PDFResourceContext c : contexts) {
+            for (PDFXObject x : c.getXObjects()) {
+                children.add(x);
+                x.getChildren(children);
+            }
+            for (PDFPattern x : c.getPatterns()) {
+                children.add(x);
+                x.getChildren(children);
+            }
+            for (PDFShading x : c.getShadings()) {
+                children.add(x);
+                x.getChildren(children);
+            }
+            for (PDFGState x : c.getGStates()) {
+                children.add(x);
+                x.getChildren(children);
+            }
+        }
+        if (!isParent) {
+            for (PDFColorSpace x : colorSpaces.values()) {
+                children.add((PDFObject)x);
+                ((PDFObject)x).getChildren(children);
+            }
+        }
+        if (parent != null) {
+            parent.getChildren(children, true);
+        }
+    }
 }
index e74c7747802c3549a5ef29dfbd8ec814f902b2b7..0a0f03d6dccccb3dcb76137c194b24a1cbd0629f 100644 (file)
@@ -50,6 +50,9 @@ public class PDFRoot extends PDFDictionary {
      */
     public static final int PAGEMODE_FULLSCREEN = 3;
 
+    private final PDFDocument document;
+
+
     private static final PDFName[] PAGEMODE_NAMES = new PDFName[] {
         new PDFName("UseNone"),
         new PDFName("UseOutlines"),
@@ -67,9 +70,9 @@ public class PDFRoot extends PDFDictionary {
      * @param objnum the object's number
      * @param pages the PDFPages object
      */
-    public PDFRoot(int objnum, PDFPages pages) {
-        super();
-        setObjectNumber(objnum);
+    public PDFRoot(PDFDocument document, PDFPages pages) {
+        this.document = document;
+        setObjectNumber(document);
         put("Type", new PDFName("Catalog"));
         setRootPages(pages);
         setLanguage("x-unknown");
index 2801d4d1f8664d985105aa25eea48a1aa1fe287b..d6dd98cf3e688c8bed1087625c08484cbe67e457 100644 (file)
@@ -187,7 +187,7 @@ public class PDFStream extends AbstractPDFStream {
         final int len = super.output(stream);
 
         //Now that the data has been written, it can be discarded.
-        this.data = null;
+//        this.data = null;
         return len;
     }
 
index eb619fcc679e84e42f53d1af08324c3cc610cc2d..8da267af139ece8b93e21762ebc290fea808073b 100644 (file)
@@ -22,15 +22,17 @@ package org.apache.fop.pdf.xref;
 import java.io.DataOutputStream;
 import java.io.IOException;
 
+import org.apache.fop.pdf.PDFObjectNumber;
+
 /**
  * A reference to an indirect object stored in an object stream. Contains the relevant
  * information to add to a cross-reference stream.
  */
 public class CompressedObjectReference implements ObjectReference {
 
-    private final int objectNumber;
+    private final PDFObjectNumber objectNumber;
 
-    private final int objectStreamNumber;
+    private final PDFObjectNumber objectStreamNumber;
 
     private final int index;
 
@@ -42,7 +44,7 @@ public class CompressedObjectReference implements ObjectReference {
      * object is to be found
      * @param index the index of the compressed object in the object stream
      */
-    public CompressedObjectReference(int objectNumber, int objectStreamNumber, int index) {
+    public CompressedObjectReference(PDFObjectNumber objectNumber, PDFObjectNumber objectStreamNumber, int index) {
         this.objectNumber = objectNumber;
         this.objectStreamNumber = objectStreamNumber;
         this.index = index;
@@ -50,16 +52,16 @@ public class CompressedObjectReference implements ObjectReference {
 
     public void output(DataOutputStream out) throws IOException {
         out.write(2);
-        out.writeLong(objectStreamNumber);
+        out.writeLong(objectStreamNumber.getNumber());
         out.write(0);
         out.write(index);
     }
 
-    public int getObjectNumber() {
+    public PDFObjectNumber getObjectNumber() {
         return objectNumber;
     }
 
-    public int getObjectStreamNumber() {
+    public PDFObjectNumber getObjectStreamNumber() {
         return objectStreamNumber;
     }
 
index 32c31573ff1b91532e68421bae24012c0d8c672e..9dbd317a7fa45e52619344a7aa982879d190004a 100644 (file)
@@ -31,6 +31,7 @@ import org.apache.fop.pdf.PDFDictionary;
 import org.apache.fop.pdf.PDFDocument;
 import org.apache.fop.pdf.PDFFilterList;
 import org.apache.fop.pdf.PDFName;
+import org.apache.fop.pdf.PDFObjectNumber;
 import org.apache.fop.pdf.PDFStream;
 
 /**
@@ -60,7 +61,10 @@ public class CrossReferenceStream extends CrossReferenceObject {
             objectReferences.add(offset == null ? null : new UncompressedObjectReference(offset));
         }
         for (CompressedObjectReference ref : compressedObjectReferences) {
-            this.objectReferences.set(ref.getObjectNumber() - 1, ref);
+            while (ref.getObjectNumber().getNumber() > objectReferences.size()) {
+                objectReferences.add(null);
+            }
+            this.objectReferences.set(ref.getObjectNumber().getNumber() - 1, ref);
         }
     }
 
@@ -77,7 +81,7 @@ public class CrossReferenceStream extends CrossReferenceObject {
             }
 
         };
-        helperStream.setObjectNumber(objectNumber);
+        helperStream.setObjectNumber(new PDFObjectNumber(objectNumber));
         helperStream.setDocument(document);
         ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
         DataOutputStream data = new DataOutputStream(byteArray);
index 41a1146ca942d971dbd98069d0b264c926807be2..09beab5b6c4b14f1f31ce88e8309e1aac0244af3 100644 (file)
@@ -35,10 +35,17 @@ public class CrossReferenceTable extends CrossReferenceObject {
 
     private final StringBuilder pdf = new StringBuilder(256);
 
+    private int last;
+    private int first;
+    private int size;
+
     public CrossReferenceTable(TrailerDictionary trailerDictionary, long startxref,
-            List<Long> location) {
+            List<Long> location, int first, int last, int size) {
         super(trailerDictionary, startxref);
         this.objectReferences = location;
+        this.first = first;
+        this.last = last;
+        this.size = size;
     }
 
     public void output(OutputStream stream) throws IOException {
@@ -47,10 +54,17 @@ public class CrossReferenceTable extends CrossReferenceObject {
     }
 
     private void outputXref() throws IOException {
-        pdf.append("xref\n0 ");
-        pdf.append(objectReferences.size() + 1);
-        pdf.append("\n0000000000 65535 f \n");
-        for (Long objectReference : objectReferences) {
+        if (first == 0) {
+            pdf.append("xref\n0 ");
+            pdf.append(last + 1);
+            pdf.append("\n0000000000 65535 f \n");
+        } else {
+            pdf.append("xref\n" + (first + 1) + " ");
+            pdf.append(last + "\n");
+        }
+        for (int i = first; i < first + last; i++) {
+            Long objectReference = objectReferences.get(i);
+            assert objectReference != null;
             final String padding = "0000000000";
             String s = String.valueOf(objectReference);
             if (s.length() > 10) {
@@ -66,7 +80,7 @@ public class CrossReferenceTable extends CrossReferenceObject {
         pdf.append("trailer\n");
         stream.write(PDFDocument.encode(pdf.toString()));
         PDFDictionary dictionary = trailerDictionary.getDictionary();
-        dictionary.put("/Size", objectReferences.size() + 1);
+        dictionary.put("/Size", size + 1);
         dictionary.output(stream);
     }
 
index d5d62522fe78d603af474d2fbe9571d2656cb908..09545d81c995785c704b1cc07999dcea97b12e3b 100644 (file)
@@ -87,7 +87,7 @@ public class TrailerDictionary {
         return this;
     }
 
-    PDFDictionary getDictionary() {
+    public PDFDictionary getDictionary() {
         return dictionary;
     }
 
index 6558bddc285c88bb276a608a691f0d1feda6b80f..fc1f31cc6fd813bfc067143b23a7da2d85949dbe 100644 (file)
@@ -28,6 +28,7 @@ import java.io.OutputStream;
 import org.apache.fop.pdf.PDFColorHandler;
 import org.apache.fop.pdf.PDFDocument;
 import org.apache.fop.pdf.PDFFilterList;
+import org.apache.fop.pdf.PDFLinearization;
 import org.apache.fop.pdf.PDFNumber;
 import org.apache.fop.pdf.PDFPaintingState;
 import org.apache.fop.pdf.PDFReference;
@@ -148,6 +149,9 @@ public class PDFContentGenerator {
      * @throws IOException if an error occurs while flushing the PDF objects
      */
     public void flushPDFDoc() throws IOException {
+        if (document.isLinearizationEnabled()) {
+            new PDFLinearization(document).outputPages(outputStream);
+        }
         this.document.output(this.outputStream);
     }
 
index fddbdff913d2c5e24190052df9b509eaa918d9e8..05e9cb580f05d2585f9526ca5e01d07abfeca738 100644 (file)
@@ -41,6 +41,7 @@ import org.apache.fop.pdf.PDFAnnotList;
 import org.apache.fop.pdf.PDFArray;
 import org.apache.fop.pdf.PDFDocument;
 import org.apache.fop.pdf.PDFPage;
+import org.apache.fop.pdf.PDFReference;
 import org.apache.fop.pdf.PDFResources;
 import org.apache.fop.render.extensions.prepress.PageBoundaries;
 import org.apache.fop.render.extensions.prepress.PageScale;
@@ -180,9 +181,13 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
 
     /** {@inheritDoc} */
     public void endDocument() throws IFException {
+        pdfDoc.getResources().addFonts(pdfDoc, fontInfo);
         try {
-            pdfDoc.getResources().addFonts(pdfDoc, fontInfo);
-            pdfDoc.outputTrailer(this.outputStream);
+            if (pdfDoc.isLinearizationEnabled()) {
+                generator.flushPDFDoc();
+            } else {
+                pdfDoc.outputTrailer(this.outputStream);
+            }
             this.pdfDoc = null;
 
             pdfResources = null;
@@ -286,8 +291,11 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
                 this.pdfDoc.addObject(annots);
             }
             this.pdfDoc.addObject(currentPage);
-            this.generator.flushPDFDoc();
-            this.generator = null;
+
+            if (!pdfDoc.isLinearizationEnabled()) {
+                this.generator.flushPDFDoc();
+                this.generator = null;
+            }
         } catch (IOException ioe) {
             throw new IFException("I/O error in endPage()", ioe);
         }
@@ -329,17 +337,15 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
 
     static final class PageReference {
 
-        private final String pageRef;
+        private final PDFReference pageRef;
         private final Dimension pageDimension;
 
         private PageReference(PDFPage page, Dimension dim) {
-            // Avoid keeping references to PDFPage as memory usage is
-            // considerably increased when handling thousands of pages.
-            this.pageRef = page.makeReference().toString();
+            this.pageRef = page.makeReference();
             this.pageDimension = new Dimension(dim);
         }
 
-        public String getPageRef() {
+        public PDFReference getPageRef() {
             return this.pageRef;
         }
 
index ce43fc14b4ababf2e5a6a9c70e2f3adf38c4e06b..cb9f43fa946829b14377eeceb036551b1885ff52 100644 (file)
@@ -31,6 +31,7 @@ import org.apache.fop.pdf.PDFFactory;
 import org.apache.fop.pdf.PDFGoTo;
 import org.apache.fop.pdf.PDFLink;
 import org.apache.fop.pdf.PDFOutline;
+import org.apache.fop.pdf.PDFReference;
 import org.apache.fop.pdf.PDFStructElem;
 import org.apache.fop.render.intermediate.IFDocumentNavigationHandler;
 import org.apache.fop.render.intermediate.IFException;
@@ -87,7 +88,7 @@ public class PDFDocumentNavigationHandler implements IFDocumentNavigationHandler
             parent = getPDFDoc().getOutlineRoot();
         }
         PDFAction action = getAction(bookmark.getAction());
-        String actionRef = (action != null ? action.makeReference().toString() : null);
+        PDFReference actionRef = (action != null ? action.makeReference() : null);
         PDFOutline pdfOutline = getPDFDoc().getFactory().makeOutline(parent,
             bookmark.getTitle(), actionRef, bookmark.isShown());
         Iterator iter = bookmark.getChildBookmarks().iterator();
@@ -194,7 +195,7 @@ public class PDFDocumentNavigationHandler implements IFDocumentNavigationHandler
             p2d = new Point2D.Double(
                     action.getTargetLocation().x / 1000.0,
                     (pageRef.getPageDimension().height - action.getTargetLocation().y) / 1000.0);
-            String pdfPageRef = pageRef.getPageRef();
+            PDFReference pdfPageRef = pageRef.getPageRef();
             pdfGoTo.setPageReference(pdfPageRef);
             pdfGoTo.setPosition(p2d);
 
index 5ab0b700f1329c221bab50e769a328ca566e968a..60f2c11bc9de4a50579613ef3f37fb73a9a0ca24 100644 (file)
@@ -85,7 +85,7 @@ public class PDFImageHandlerGraphics2D extends AbstractImageHandlerGraphics2D {
         final boolean textAsShapes = false;
         PDFGraphics2D graphics = new PDFGraphics2D(textAsShapes,
                 pdfContext.getFontInfo(), generator.getDocument(),
-                generator.getResourceContext(), pdfContext.getPage().referencePDF(),
+                generator.getResourceContext(), pdfContext.getPage().makeReference(),
                 "", 0.0f, null);
         graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
 
index 13d61dc3529ae13677e7e4bbcf2c0bb96313e246..fa3813d061b5f88a0d3d8f5c2d25bff4aba08fc5 100644 (file)
@@ -171,7 +171,7 @@ public class PDFImageHandlerSVG implements ImageHandler {
 
         PDFGraphics2D graphics = new PDFGraphics2D(true, pdfContext.getFontInfo(),
                 generator.getDocument(),
-                generator.getResourceContext(), pdfContext.getPage().referencePDF(),
+                generator.getResourceContext(), pdfContext.getPage().makeReference(),
                 "", 0, new TransparencyIgnoredEventListener(pdfContext, imageSVG));
         graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
 
index cd992841e3baff10bc07e1521c0482b7dc0adf53..c43e6347a3be47bb8b4b75ae79f709d073281748 100644 (file)
@@ -171,7 +171,9 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> {
                 prepareImageMCID(structElem);
             }
             drawImageUsingURI(uri, rect);
-            flushPDFDoc();
+            if (!getDocumentHandler().getPDFDocument().isLinearizationEnabled()) {
+                flushPDFDoc();
+            }
         }
     }
 
@@ -228,7 +230,9 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> {
             prepareImageMCID(structElem);
         }
         drawImageUsingDocument(doc, rect);
-        flushPDFDoc();
+        if (!getDocumentHandler().getPDFDocument().isLinearizationEnabled()) {
+            flushPDFDoc();
+        }
     }
 
     private void flushPDFDoc() throws IFException {
index f01cdb71744725d088cd289b31d1b01a36b88944..f25872c4c92c31e7a8c5d307163718069d0703fc 100644 (file)
@@ -56,6 +56,7 @@ import static org.apache.fop.render.pdf.PDFEncryptionOption.OWNER_PASSWORD;
 import static org.apache.fop.render.pdf.PDFEncryptionOption.USER_PASSWORD;
 import static org.apache.fop.render.pdf.PDFRendererOption.DISABLE_SRGB_COLORSPACE;
 import static org.apache.fop.render.pdf.PDFRendererOption.FILTER_LIST;
+import static org.apache.fop.render.pdf.PDFRendererOption.LINEARIZATION;
 import static org.apache.fop.render.pdf.PDFRendererOption.MERGE_FONTS;
 import static org.apache.fop.render.pdf.PDFRendererOption.OUTPUT_PROFILE;
 import static org.apache.fop.render.pdf.PDFRendererOption.PDF_A_MODE;
@@ -137,6 +138,7 @@ public final class PDFRendererConfig implements RendererConfig {
                 parseAndPut(OUTPUT_PROFILE, cfg);
                 parseAndPut(DISABLE_SRGB_COLORSPACE, cfg);
                 parseAndPut(MERGE_FONTS, cfg);
+                parseAndPut(LINEARIZATION, cfg);
 
                 parseAndPut(VERSION, cfg);
             } catch (ConfigurationException e) {
index 9c45f981d16c3be76efe421f57be0ef9043292b3..cfad76b8142cf3f75e36c5eec7249528ca49ec49 100644 (file)
@@ -72,6 +72,12 @@ public enum PDFRendererOption implements RendererConfigOption {
             return Boolean.valueOf(value);
         }
     },
+    LINEARIZATION("linearization", false) {
+        @Override
+        Boolean deserialize(String value) {
+            return Boolean.valueOf(value);
+        }
+    },
     /** Rendering Options key for the ICC profile for the output intent. */
     OUTPUT_PROFILE("output-profile") {
         @Override
index 3a1a2433926d9261fe2a60898886857b0ae6e2e4..8124db9f51b4702c8069c349e08ef30552d7401c 100644 (file)
@@ -31,6 +31,7 @@ import org.apache.fop.pdf.Version;
 
 import static org.apache.fop.render.pdf.PDFRendererOption.DISABLE_SRGB_COLORSPACE;
 import static org.apache.fop.render.pdf.PDFRendererOption.FILTER_LIST;
+import static org.apache.fop.render.pdf.PDFRendererOption.LINEARIZATION;
 import static org.apache.fop.render.pdf.PDFRendererOption.MERGE_FONTS;
 import static org.apache.fop.render.pdf.PDFRendererOption.OUTPUT_PROFILE;
 import static org.apache.fop.render.pdf.PDFRendererOption.PDF_A_MODE;
@@ -125,4 +126,8 @@ public final class PDFRendererOptionsConfig {
     public Boolean getMergeFontsEnabled() {
         return (Boolean)properties.get(MERGE_FONTS);
     }
+
+    public Boolean getLinearizationEnabled() {
+        return (Boolean)properties.get(LINEARIZATION);
+    }
 }
index 111cca6b284e2e8710ca51f9d53c9f25a8de4ad2..f9362833111600b7b2ca087c14af469d28dadfe8 100644 (file)
@@ -588,6 +588,7 @@ class PDFRenderingUtil {
 
         this.pdfDoc.enableAccessibility(userAgent.isAccessibilityEnabled());
         pdfDoc.setMergeFontsEnabled(rendererConfig.getMergeFontsEnabled());
+        pdfDoc.setLinearizationEnabled(rendererConfig.getLinearizationEnabled());
 
         return this.pdfDoc;
     }
index cb2b58c40e95b08784bfc604b38d8cc44c487950..19862480b43793da5465f3a770b90861b8e3bc3a 100644 (file)
@@ -337,7 +337,7 @@ public class PDFDocumentGraphics2D extends PDFGraphics2D {
                 width, height);
         resourceContext = page;
         pdfContext.setCurrentPage(page);
-        pageRef = page.referencePDF();
+        pageRef = page.makeReference();
 
         currentStream.write("q\n");
         AffineTransform at = new AffineTransform(1.0, 0.0, 0.0, -1.0,
index dc897f40ece6a6948a81d845081d40b8d6d940ac..5a029928141f363aed2b7dcb6ab25ec24354ccfe 100644 (file)
@@ -88,6 +88,7 @@ import org.apache.fop.pdf.PDFLink;
 import org.apache.fop.pdf.PDFNumber;
 import org.apache.fop.pdf.PDFPaintingState;
 import org.apache.fop.pdf.PDFPattern;
+import org.apache.fop.pdf.PDFReference;
 import org.apache.fop.pdf.PDFResourceContext;
 import org.apache.fop.pdf.PDFResources;
 import org.apache.fop.pdf.PDFShading;
@@ -132,7 +133,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand
     /**
      * The PDF reference of the current page.
      */
-    protected String pageRef;
+    protected PDFReference pageRef;
 
     /**
      * The PDF painting state
@@ -212,7 +213,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand
      * @param size the current font size
      */
     public PDFGraphics2D(boolean textAsShapes, FontInfo fi, PDFDocument doc,
-                         PDFResourceContext page, String pref, String font, float size,
+                         PDFResourceContext page, PDFReference pref, String font, float size,
                          TransparencyIgnoredEventListener listener) {
         this(textAsShapes);
         pdfDoc = doc;
@@ -331,7 +332,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand
      * Gets the PDF reference of the current page.
      * @return the PDF reference of the current page
      */
-    public String getPageReference() {
+    public PDFReference getPageReference() {
         return this.pageRef;
     }
 
@@ -422,7 +423,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand
             if (linkType != PDFLink.EXTERNAL) {
                 String pdfdest = "/FitR " + dest;
                 resourceContext.addAnnotation(
-                    pdfDoc.getFactory().makeLink(rect, getPageReference(), pdfdest));
+                    pdfDoc.getFactory().makeLink(rect, getPageReference().toString(), pdfdest));
             } else {
                 resourceContext.addAnnotation(
                     pdfDoc.getFactory().makeLink(rect, dest, linkType, 0));
@@ -474,7 +475,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand
     }
 
     private void flushPDFDocument() {
-        if (outputStream != null) {
+        if (outputStream != null && !pdfDoc.isLinearizationEnabled()) {
             try {
                 this.pdfDoc.output(outputStream);
             } catch (IOException ioe) {
@@ -1068,13 +1069,13 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand
                 }
             }
 
-            String maskRef = null;
+            PDFReference maskRef = null;
             if (mask != null) {
                 BitmapImage fopimg = new BitmapImage(
                     "TempImageMask:" + pctx.toString(), devW, devH, mask, null);
                 fopimg.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));
                 PDFImageXObject xobj = pdfDoc.addImage(resourceContext, fopimg);
-                maskRef = xobj.referencePDF();
+                maskRef = xobj.makeReference();
 
                 flushPDFDocument();
             }
index dec62c0a43204f9dbdb96a70d6bfe8d4575e1861..ae68cbeb7a5973bd223ebfda27efed5e389af324 100644 (file)
@@ -49,7 +49,7 @@ public class AbstractPDFStreamTestCase extends PDFObjectTestCase {
             encodedBytes[i++] = (byte) (in & 0xff);
         }
     }
-    private String startStream = "<< /Length 5 0 R /Filter /FlateDecode >>\n"
+    private String startStream = "<< /Length 1 0 R /Filter /FlateDecode >>\n"
                 + "stream\n";
 
     private String endStream = "endstream";
index 21eb6088f7a8b9df160a582e5bc5dcd80a343f57..fda1a006ed82bdd14217f588bcd5308a563e6e14 100644 (file)
@@ -46,7 +46,7 @@ public class ObjectStreamManagerTestCase {
         int objectStreamNumber2 = assertSameObjectStream(expectedCapacity, expectedCapacity * 2);
         int objectStreamNumber3 = assertSameObjectStream(expectedCapacity * 2, numCompressedObjects);
         assertDifferent(objectStreamNumber1, objectStreamNumber2, objectStreamNumber3);
-        assertEquals(objectStreamNumber3, pdfDocument.previous.getObjectNumber());
+        assertEquals(objectStreamNumber3, pdfDocument.previous.getObjectNumber().getNumber());
     }
 
     private void createCompressObjectReferences(int numObjects) {
@@ -82,8 +82,8 @@ public class ObjectStreamManagerTestCase {
     private CompressedObject createCompressedObject(final int objectNumber) {
         return new CompressedObject() {
 
-            public int getObjectNumber() {
-                return objectNumber;
+            public PDFObjectNumber getObjectNumber() {
+                return new PDFObjectNumber(objectNumber);
             }
 
             public int output(OutputStream outputStream) throws IOException {
@@ -101,7 +101,7 @@ public class ObjectStreamManagerTestCase {
     }
 
     private int getObjectStreamNumber(int index) {
-        return compressedObjectReferences.get(index).getObjectStreamNumber();
+        return compressedObjectReferences.get(index).getObjectStreamNumber().getNumber();
     }
 
     private void assertDifferent(int objectStreamNumber1, int objectStreamNumber2,
index 3512e11475d017b4fe20efa0d77514abe8b2262a..bf8fe26c8be7c80bae9ee0ecd8f21d34d59d4bc5 100644 (file)
@@ -86,13 +86,14 @@ public class ObjectStreamTestCase {
 
     private String getExpectedOutput() {
         int numObs = compressedObjects.size();
-        int objectStreamNumber = objectStream.getObjectNumber();
+        int objectStreamNumber = objectStream.getObjectNumber().getNumber();
         int offsetsLength = 9;
         StringBuilder expected = new StringBuilder();
         expected.append("<<\n");
         ObjectStream previous = (ObjectStream) objectStream.get("Extends");
         if (previous != null) {
             expected.append("  /Extends ").append(previous.getObjectNumber()).append(" 0 R\n");
+            objectStreamNumber++;
         }
         expected.append("  /Type /ObjStm\n")
                 .append("  /N ").append(numObs).append("\n")
index e3f8344e034909bdea26fc77a920d6e8e26ff595..18d15c384dadbd9b4165ee68704d002844ef510f 100644 (file)
@@ -286,7 +286,7 @@ public class PDFEncryptionJCETestCase {
     public final void testMake() {
         PDFEncryption testEncryptionObj = createEncryptionObject(new PDFEncryptionParams());
         assertTrue(testEncryptionObj instanceof PDFEncryptionJCE);
-        assertEquals(1, ((PDFEncryptionJCE) testEncryptionObj).getObjectNumber());
+        assertEquals(1, ((PDFEncryptionJCE) testEncryptionObj).getObjectNumber().getNumber());
     }
 
     @Test
@@ -608,7 +608,7 @@ public class PDFEncryptionJCETestCase {
                 };
             }
         };
-        return (PDFEncryptionJCE) PDFEncryptionJCE.make(1, params, doc);
+        return (PDFEncryptionJCE) PDFEncryptionJCE.make(new PDFObjectNumber(1), params, doc);
     }
 
     private void runEncryptionTests() throws IOException {
diff --git a/test/java/org/apache/fop/pdf/PDFLinearizationTestCase.java b/test/java/org/apache/fop/pdf/PDFLinearizationTestCase.java
new file mode 100644 (file)
index 0000000..7b78c1f
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+package org.apache.fop.pdf;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.geom.Rectangle2D;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.stream.StreamResult;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.render.intermediate.IFContext;
+import org.apache.fop.render.pdf.PDFContentGenerator;
+import org.apache.fop.render.pdf.PDFDocumentHandler;
+import org.apache.fop.render.pdf.PDFPainter;
+
+public class PDFLinearizationTestCase {
+    private int objectLeast;
+    private int[] objects;
+
+    @Test
+    public void testPDF() throws IOException {
+        PDFDocument doc = new PDFDocument("");
+        doc.setLinearizationEnabled(true);
+        PDFResources resources = new PDFResources(doc);
+        PDFResourceContext context = new PDFResourceContext(resources);
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        PDFContentGenerator gen = null;
+        for (int i = 0; i < 2; i++) {
+            gen = new PDFContentGenerator(doc, out, context);
+            Rectangle2D.Float f = new Rectangle2D.Float();
+            PDFPage page = new PDFPage(resources, i, f, f, f, f);
+            doc.registerObject(page);
+            doc.registerObject(gen.getStream());
+            page.setContents(gen.getStream());
+        }
+        gen.flushPDFDoc();
+        byte[] data = out.toByteArray();
+        checkPDF(data);
+    }
+
+    @Test
+    public void testImage() throws Exception {
+        String fopxconf = "<fop version=\"1.0\"><renderers>"
+                + "<renderer mime=\"application/pdf\">"
+                + "<linearization>true</linearization>"
+                + "</renderer></renderers></fop>";
+        FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI(),
+                new ByteArrayInputStream(fopxconf.getBytes()));
+        FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
+        IFContext ifContext = new IFContext(foUserAgent);
+        PDFDocumentHandler documentHandler = new PDFDocumentHandler(ifContext);
+        documentHandler.getConfigurator().configure(documentHandler);
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        documentHandler.setFontInfo(new FontInfo());
+        documentHandler.setResult(new StreamResult(out));
+        documentHandler.startDocument();
+        documentHandler.startPage(0, "", "", new Dimension());
+        PDFPainter pdfPainter = new PDFPainter(documentHandler, null);
+        pdfPainter.drawImage("test/resources/fop/svg/logo.jpg", new Rectangle());
+        documentHandler.endPage();
+        Assert.assertFalse(out.toString().contains("/Subtype /Image"));
+        documentHandler.endDocument();
+        Assert.assertTrue(out.toString().contains("/Subtype /Image"));
+    }
+
+    private void checkPDF(byte[] data) throws IOException {
+        checkHintTable(data);
+        InputStream is = new ByteArrayInputStream(data);
+        Map<String, StringBuilder> objs = readObjs(is);
+
+        List<String> keys = new ArrayList<String>(objs.keySet());
+        int start = keys.indexOf("1 0 obj");
+        Assert.assertTrue(start > 1);
+        int j = 1;
+        for (int i = start; i < keys.size(); i++) {
+            Assert.assertEquals(keys.get(i), j + " 0 obj");
+            j++;
+        }
+        for (int i = 0; i < start; i++) {
+            Assert.assertEquals(keys.get(i), j + " 0 obj");
+            j++;
+        }
+
+        checkFirstObj(data);
+        checkTrailer(data);
+
+        String firstObj = objs.values().iterator().next().toString().replace("\n", "");
+        Assert.assertTrue(firstObj.startsWith("<<  /Linearized 1  /L " + data.length));
+        Assert.assertTrue(firstObj.endsWith("startxref0%%EOF"));
+        int pageObjNumber = getValue("/O", firstObj);
+        Assert.assertTrue(objs.get(pageObjNumber + " 0 obj").toString().contains("/Type /Page"));
+        Assert.assertTrue(objs.get("5 0 obj").toString().contains("/Type /Pages"));
+
+        int total = 0;
+        for (int i : objects) {
+            total += i;
+        }
+        Assert.assertEquals(total, objs.size() - 6);
+    }
+
+    private void checkFirstObj(byte[] data) throws IOException {
+        int firstObjPos = getValue("/E", getFirstObj(data));
+        InputStream is = new ByteArrayInputStream(data);
+        Assert.assertEquals(is.skip(firstObjPos), firstObjPos);
+        byte[] obj = new byte[10];
+        Assert.assertEquals(is.read(obj), obj.length);
+        Assert.assertTrue(new String(obj).startsWith("1 0 obj"));
+    }
+
+    private void checkTrailer(byte[] data) throws IOException {
+        int trailerPos = getValue("/T", getFirstObj(data));
+        InputStream is = new ByteArrayInputStream(data);
+        Assert.assertEquals(is.skip(trailerPos), trailerPos);
+        byte[] obj = new byte[20];
+        Assert.assertEquals(is.read(obj), obj.length);
+        Assert.assertTrue(new String(obj).startsWith("0000000000 65535 f"));
+    }
+
+    private int getValue(String name, String firstObj) throws IOException {
+        String[] split = firstObj.split(" ");
+        for (int i = 0; i < split.length; i++) {
+            if (split[i].equals(name)) {
+                return Integer.valueOf(split[i + 1].replace(">>", ""));
+            }
+        }
+        throw new IOException(name + " not found " + firstObj);
+    }
+
+    private int[] getArrayValue(String name, String firstObj) throws IOException {
+        String[] split = firstObj.split(" ");
+        for (int i = 0; i < split.length; i++) {
+            if (split[i].equals(name)) {
+                int[] v = new int[2];
+                v[0] = Integer.valueOf(split[i + 1].replace("[", ""));
+                v[1] = Integer.valueOf(split[i + 2].replace("]", ""));
+                return v;
+            }
+        }
+        throw new IOException(name + " not found " + firstObj);
+    }
+
+    private String getFirstObj(byte[] out) throws IOException {
+        InputStream data = new ByteArrayInputStream(out);
+        Map<String, StringBuilder> objs = readObjs(data);
+        return objs.values().iterator().next().toString().replace("\n", "");
+    }
+
+    private void checkHintTable(byte[] out) throws IOException {
+        String firstObj = getFirstObj(out);
+        int hintPos = getArrayValue("/H", firstObj)[0];
+        int hintLength = getArrayValue("/H", firstObj)[1];
+
+        InputStream data = new ByteArrayInputStream(out);
+        Assert.assertEquals(data.skip(hintPos), hintPos);
+
+        byte[] hintTable = new byte[hintLength];
+        Assert.assertEquals(data.read(hintTable), hintLength);
+        String hintTableStr = new String(hintTable);
+
+        Assert.assertTrue(hintTableStr.contains("/S "));
+        Assert.assertTrue(hintTableStr.contains("/C "));
+        Assert.assertTrue(hintTableStr.contains("/E "));
+        Assert.assertTrue(hintTableStr.contains("/L "));
+        Assert.assertTrue(hintTableStr.contains("/V "));
+        Assert.assertTrue(hintTableStr.contains("/O "));
+        Assert.assertTrue(hintTableStr.contains("/I "));
+        Assert.assertTrue(hintTableStr.contains("/Length "));
+        Assert.assertTrue(hintTableStr.contains("stream"));
+        Assert.assertTrue(hintTableStr.contains("endstream"));
+        Assert.assertTrue(hintTableStr.endsWith("endobj\n"));
+
+        data = new ByteArrayInputStream(hintTable);
+        readStart(data);
+        int pages = getValue("/N", firstObj);
+        readObjectsTable(data, pages);
+        readSharedObjectsTable(data);
+        Assert.assertEquals(objectLeast, 1);
+    }
+
+    private void readObjectsTable(InputStream data, int pages)
+            throws IOException {
+        objectLeast = read32(data);
+        read32(data);
+        int bitsDiffObjects = read16(data);
+        read32(data);
+        int bitsDiffPageLength = read16(data);
+        read32(data);
+        read16(data);
+        read32(data);
+        read16(data);
+        read16(data);
+        read16(data);
+        read16(data);
+        read16(data);
+
+        objects = new int[pages];
+        for (int i = 0; i < pages; i++) {
+            objects[i] = objectLeast + readBits(bitsDiffObjects, data);
+        }
+        for (int i = 0; i < pages; i++) {
+            readBits(bitsDiffPageLength, data);
+        }
+        for (int i = 0; i < pages; i++) {
+            readBits(32, data);
+        }
+    }
+
+    private void readSharedObjectsTable(InputStream str) throws IOException {
+        readBits(32, str);
+        readBits(32, str);
+        readBits(32, str);
+        int sharedGroups = readBits(32, str);
+        readBits(16, str);
+        readBits(32, str);
+        int bitsDiffGroupLength = readBits(16, str);
+        for (int i = 0; i < sharedGroups; i++) {
+            readBits(bitsDiffGroupLength, str);
+        }
+    }
+
+    private int readBits(int bits, InputStream data) throws IOException {
+        if (bits == 32) {
+            return read32(data);
+        }
+        if (bits == 16) {
+            return read16(data);
+        }
+        throw new IOException("Wrong bits");
+    }
+
+    private int read32(InputStream data) throws IOException {
+        int ch1 = data.read();
+        int ch2 = data.read();
+        int ch3 = data.read();
+        int ch4 = data.read();
+        return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4));
+    }
+
+    private int read16(InputStream data) throws IOException {
+        int ch1 = data.read();
+        int ch2 = data.read();
+        return (ch1 << 8) + (ch2);
+    }
+
+    private void readStart(InputStream inputStream) throws IOException {
+        StringBuilder sb = new StringBuilder();
+        while (inputStream.available() > 0) {
+            int data = inputStream.read();
+            if (data == '\n') {
+                if (sb.toString().equals("stream")) {
+                    return;
+                }
+                sb.setLength(0);
+            } else {
+                sb.append((char)data);
+            }
+        }
+    }
+
+    private Map<String, StringBuilder> readObjs(InputStream inputStream) throws IOException {
+        Map<String, StringBuilder> objs = new LinkedHashMap<String, StringBuilder>();
+        StringBuilder sb = new StringBuilder();
+        String key = null;
+        while (inputStream.available() > 0) {
+            int data = inputStream.read();
+            if (data == '\n') {
+                if (sb.toString().endsWith(" 0 obj")) {
+                    key = sb.toString().trim();
+                    objs.put(key, new StringBuilder());
+                } else if (key != null) {
+                    objs.get(key).append(sb).append("\n");
+                }
+                sb.setLength(0);
+            } else {
+                sb.append((char)data);
+            }
+        }
+        return objs;
+    }
+}
index f35d2a15c44c369753488389a1380b4edefbf216..5443524381e2a0b07e0fc63538b61838ba7f6dfd 100644 (file)
@@ -58,10 +58,10 @@ public class PDFObjectTestCase {
     @Test
     public void testSetObjectNumber() {
         pdfObjectUnderTest.setObjectNumber(1);
-        assertEquals(1, pdfObjectUnderTest.getObjectNumber());
+        assertEquals(1, pdfObjectUnderTest.getObjectNumber().getNumber());
 
         pdfObjectUnderTest.setObjectNumber(5);
-        assertEquals(5, pdfObjectUnderTest.getObjectNumber());
+        assertEquals(5, pdfObjectUnderTest.getObjectNumber().getNumber());
     }
 
     /**
@@ -157,12 +157,12 @@ public class PDFObjectTestCase {
         PDFDictionary dict = new PDFDictionary();
         dict.setObjectNumber(7);
         PDFReference ref = dict.makeReference();
-        assertEquals(ref.getObjectNumber(), 7);
+        assertEquals(ref.getObjectNumber().getNumber(), 7);
         assertEquals(ref.getGeneration(), 0);
         assertEquals(ref.toString(), "7 0 R");
 
         ref = new PDFReference("8 0 R");
-        assertEquals(ref.getObjectNumber(), 8);
+        assertEquals(ref.getObjectNumber().getNumber(), 8);
         assertEquals(ref.getGeneration(), 0);
         assertEquals(ref.toString(), "8 0 R");
     }
index b7a326c64e452886613806e1bab1a1154fcebae6..b58d2f93ebda2bfe77a2bda3943d110f307e5235 100644 (file)
@@ -27,6 +27,8 @@ import org.junit.Test;
 
 import static org.junit.Assert.assertArrayEquals;
 
+import org.apache.fop.pdf.PDFObjectNumber;
+
 public class CompressedObjectReferenceTestCase extends ObjectReferenceTest {
 
     @Test
@@ -41,7 +43,7 @@ public class CompressedObjectReferenceTestCase extends ObjectReferenceTest {
 
     private void runTest(List<Integer> expectedObjectStreamBytes, int index) throws IOException {
         int objectStreamNumber = (int) computeNumberFromBytes(expectedObjectStreamBytes);
-        sut = new CompressedObjectReference(0, objectStreamNumber, index);
+        sut = new CompressedObjectReference(new PDFObjectNumber(0), new PDFObjectNumber(objectStreamNumber), index);
         byte[] expected = createExpectedOutput((byte) 2, expectedObjectStreamBytes, index);
         byte[] actual = getActualOutput();
         assertArrayEquals(expected, actual);
index cd55577cd23fa6a28526ea131d2a222bc9717681..513f0d754f60d5e80c367d4afab81cfda4874130 100644 (file)
@@ -52,7 +52,7 @@ public abstract class CrossReferenceObjectTest {
         pdfDocument = new PDFDocument("Apache FOP");
         Map<String, List<String>> filterMap = pdfDocument.getFilterMap();
         filterMap.put("default", Arrays.asList("null"));
-        PDFRoot root = new PDFRoot(1, new PDFPages(10));
+        PDFRoot root = new PDFRoot(pdfDocument, new PDFPages(pdfDocument));
         PDFInfo info = new PDFInfo();
         info.setObjectNumber(2);
         byte[] fileID =
index 3e609635df1b1e19b9eade85d3942b88debe1db8..e0e8aef7849d983d3307c87b5f87b8dd24f89f8e 100644 (file)
@@ -29,6 +29,8 @@ import java.util.List;
 
 import org.junit.Test;
 
+import org.apache.fop.pdf.PDFObjectNumber;
+
 public class CrossReferenceStreamTestCase extends CrossReferenceObjectTest {
 
     private List<Long> uncompressedObjectOffsets;
@@ -54,7 +56,7 @@ public class CrossReferenceStreamTestCase extends CrossReferenceObjectTest {
     @Test
     public void testWithObjectStreams1() throws IOException {
         List<CompressedObjectReference> compressedObjectReferences =
-                Arrays.asList(new CompressedObjectReference(2, 1, 0));
+                Arrays.asList(new CompressedObjectReference(new PDFObjectNumber(2), new PDFObjectNumber(1), 0));
         test(Arrays.asList(0L, null), compressedObjectReferences);
     }
 
@@ -72,8 +74,8 @@ public class CrossReferenceStreamTestCase extends CrossReferenceObjectTest {
         for (int index = 0; index < numCompressedObjects; index++) {
             indirectObjectOffsets.add(null);
             int obNum = numIndirectObjects + index + 1;
-            compressedObjectReferences.add(new CompressedObjectReference(obNum,
-                    numIndirectObjects, index));
+            compressedObjectReferences.add(new CompressedObjectReference(new PDFObjectNumber(obNum),
+                    new PDFObjectNumber(numIndirectObjects), index));
         }
         test(indirectObjectOffsets, compressedObjectReferences);
     }
@@ -108,7 +110,7 @@ public class CrossReferenceStreamTestCase extends CrossReferenceObjectTest {
             objectReferences.add(offset == null ? null : new UncompressedObjectReference(offset));
         }
         for (CompressedObjectReference ref : compressedObjectReferences) {
-            objectReferences.set(ref.getObjectNumber() - 1, ref);
+            objectReferences.set(ref.getObjectNumber().getNumber() - 1, ref);
         }
         int maxObjectNumber = objectReferences.size() + 1;
         ByteArrayOutputStream stream = new ByteArrayOutputStream();
index 12f6e3c1b310480bb5091eb7364f13e3cb80edd1..1c609e30f0ce92908a0e063871ba722865c62959 100644 (file)
@@ -53,7 +53,7 @@ public class CrossReferenceTableTestCase extends CrossReferenceObjectTest {
 
     @Override
     protected CrossReferenceObject createCrossReferenceObject() {
-        return new CrossReferenceTable(trailerDictionary, STARTXREF, offsets);
+        return new CrossReferenceTable(trailerDictionary, STARTXREF, offsets, 0, offsets.size(), offsets.size());
     }
 
     @Override