]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Bugzilla #51644:
authorChris Bowditch <cbowditch@apache.org>
Fri, 6 Jan 2012 16:03:44 +0000 (16:03 +0000)
committerChris Bowditch <cbowditch@apache.org>
Fri, 6 Jan 2012 16:03:44 +0000 (16:03 +0000)
Tagged PDF performance improvement + tests
Submitted by: Mehdi Houshmand

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1228243 13f79535-47bb-0310-9956-ffa450edef68

30 files changed:
src/java/org/apache/fop/pdf/AbstractPDFStream.java
src/java/org/apache/fop/pdf/PDFArray.java
src/java/org/apache/fop/pdf/PDFDestination.java
src/java/org/apache/fop/pdf/PDFDictionary.java
src/java/org/apache/fop/pdf/PDFDocument.java
src/java/org/apache/fop/pdf/PDFEmbeddedFiles.java
src/java/org/apache/fop/pdf/PDFName.java
src/java/org/apache/fop/pdf/PDFNull.java
src/java/org/apache/fop/pdf/PDFNumsArray.java
src/java/org/apache/fop/pdf/PDFObject.java
src/java/org/apache/fop/pdf/PDFRectangle.java
src/java/org/apache/fop/pdf/PDFReference.java
src/java/org/apache/fop/pdf/PDFWritable.java
status.xml
test/java/org/apache/fop/StandardTestSuite.java
test/java/org/apache/fop/UtilityCodeTestSuite.java
test/java/org/apache/fop/pdf/AbstractPDFStreamTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/pdf/PDFArrayTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/pdf/PDFDestsTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/pdf/PDFDocumentTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java [new file with mode: 0644]
test/java/org/apache/fop/pdf/PDFNameTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/pdf/PDFNullTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/pdf/PDFNumberTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/pdf/PDFNumsArrayTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/pdf/PDFObjectTestCase.java
test/java/org/apache/fop/pdf/PDFRectangleTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/pdf/PDFReferenceTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/util/PDFNumberTestCase.java [deleted file]

index 91605b29350d8a233f29e804d4fbdae4bf30fa37..52262252f4ed20149ddb5ac8bbb6ade1ce3ce9d5 100644 (file)
@@ -21,7 +21,6 @@ package org.apache.fop.pdf;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.Writer;
 
 import org.apache.commons.io.output.CountingOutputStream;
 
@@ -164,7 +163,7 @@ public abstract class AbstractPDFStream extends PDFDictionary {
         OutputStream filteredOutput = getFilterList().applyFilters(cout);
         outputRawStreamData(filteredOutput);
         filteredOutput.close();
-        refLength.setNumber(new Integer(cout.getCount()));
+        refLength.setNumber(Integer.valueOf(cout.getCount()));
         bytesWritten += cout.getCount();
 
         //Stream trailer
@@ -180,13 +179,13 @@ public abstract class AbstractPDFStream extends PDFDictionary {
      * byte arrays around so much
      * {@inheritDoc}
      */
+    @Override
     protected int output(OutputStream stream) throws IOException {
         setupFilterList();
 
         CountingOutputStream cout = new CountingOutputStream(stream);
-        Writer writer = PDFDocument.getWriterFor(cout);
-        writer.write(getObjectID());
-        //int length = 0;
+        StringBuilder textBuffer = new StringBuilder(64);
+        textBuffer.append(getObjectID());
 
         StreamCache encodedStream = null;
         PDFNumber refLength = null;
@@ -197,14 +196,14 @@ public abstract class AbstractPDFStream extends PDFDictionary {
             lengthEntry = refLength;
         } else {
             encodedStream = encodeStream();
-            lengthEntry = new Integer(encodedStream.getSize() + 1);
+            lengthEntry = Integer.valueOf(encodedStream.getSize() + 1);
         }
 
         populateStreamDict(lengthEntry);
-        writeDictionary(cout, writer);
+        writeDictionary(cout, textBuffer);
 
         //Send encoded stream to target OutputStream
-        writer.flush();
+        PDFDocument.flushTextBuffer(textBuffer, cout);
         if (encodedStream == null) {
             encodeAndWriteStream(cout, refLength);
         } else {
@@ -212,8 +211,8 @@ public abstract class AbstractPDFStream extends PDFDictionary {
             encodedStream.clear(); //Encoded stream can now be discarded
         }
 
-        writer.write("\nendobj\n");
-        writer.flush();
+        textBuffer.append("\nendobj\n");
+        PDFDocument.flushTextBuffer(textBuffer, cout);
         return cout.getCount();
     }
 
index a79e2704fb21c9ebd3f606f99167cbdbc8541607..78eba3bb9bb7267b1070537283504692b2aaf94d 100644 (file)
@@ -21,7 +21,6 @@ package org.apache.fop.pdf;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.Writer;
 import java.util.Collection;
 import java.util.List;
 
@@ -34,7 +33,7 @@ public class PDFArray extends PDFObject {
     /**
      * List holding the values of this array
      */
-    protected List values = new java.util.ArrayList();
+    protected List<Object> values = new java.util.ArrayList<Object>();
 
     /**
      * Create a new, empty array object
@@ -62,7 +61,7 @@ public class PDFArray extends PDFObject {
         super(parent);
 
         for (int i = 0, c = values.length; i < c; i++) {
-            this.values.add(new Integer(values[i]));
+            this.values.add(Integer.valueOf(values[i]));
         }
     }
 
@@ -85,7 +84,7 @@ public class PDFArray extends PDFObject {
      * @param parent the array's parent if any
      * @param values the actual values wrapped by this object
      */
-    public PDFArray(PDFObject parent, Collection values) {
+    public PDFArray(PDFObject parent, Collection<Object> values) {
         /* generic creation of PDF object */
         super(parent);
 
@@ -180,28 +179,29 @@ public class PDFArray extends PDFObject {
     }
 
     /** {@inheritDoc} */
+    @Override
     protected int output(OutputStream stream) throws IOException {
         CountingOutputStream cout = new CountingOutputStream(stream);
-        Writer writer = PDFDocument.getWriterFor(cout);
+        StringBuilder textBuffer = new StringBuilder(64);
         if (hasObjectNumber()) {
-            writer.write(getObjectID());
+            textBuffer.append(getObjectID());
         }
 
-        writer.write('[');
+        textBuffer.append('[');
         for (int i = 0; i < values.size(); i++) {
             if (i > 0) {
-                writer.write(' ');
+                textBuffer.append(' ');
             }
             Object obj = this.values.get(i);
-            formatObject(obj, cout, writer);
+            formatObject(obj, cout, textBuffer);
         }
-        writer.write(']');
+        textBuffer.append(']');
 
         if (hasObjectNumber()) {
-            writer.write("\nendobj\n");
+            textBuffer.append("\nendobj\n");
         }
 
-        writer.flush();
+        PDFDocument.flushTextBuffer(textBuffer, cout);
         return cout.getCount();
     }
 
index ced3ac4970ef3262901743be392a1a23b178acc9..400c4a4b6614497789ccfc153e401b6d0de27b2b 100644 (file)
@@ -21,7 +21,6 @@ package org.apache.fop.pdf;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.Writer;
 
 import org.apache.commons.io.output.CountingOutputStream;
 
@@ -52,15 +51,16 @@ public class PDFDestination extends PDFObject {
     }
 
     /** {@inheritDoc} */
+    @Override
     protected int output(OutputStream stream) throws IOException {
         CountingOutputStream cout = new CountingOutputStream(stream);
-        Writer writer = PDFDocument.getWriterFor(cout);
+        StringBuilder textBuffer = new StringBuilder(64);
 
-        formatObject(getIDRef(), cout, writer);
-        writer.write(' ');
-        formatObject(goToReference, cout, writer);
+        formatObject(getIDRef(), cout, textBuffer);
+        textBuffer.append(' ');
+        formatObject(goToReference, cout, textBuffer);
 
-        writer.flush();
+        PDFDocument.flushTextBuffer(textBuffer, cout);
         return cout.getCount();
     }
 
@@ -70,6 +70,7 @@ public class PDFDestination extends PDFObject {
      * @param goToReference the reference to set in the associated DestinationData object.
      * @deprecated use setGoToReference(Object) instead
      */
+    @Deprecated
     public void setGoToReference(String goToReference) {
         this.goToReference = goToReference;
     }
@@ -107,6 +108,7 @@ public class PDFDestination extends PDFObject {
      * @param obj the object to compare
      * @return true if this equals other object
      */
+    @Override
     public boolean equals(Object obj) {
         if (this == obj) {
             return true;
@@ -125,6 +127,7 @@ public class PDFDestination extends PDFObject {
     }
 
     /** {@inheritDoc} */
+    @Override
     public int hashCode() {
         return getIDRef().hashCode();
     }
index 932f2d03e5ac93b6a91435b47e19369e0ffb6ebe..5a67243047af4e2b70756943bce893e63d510fda 100644 (file)
@@ -21,8 +21,6 @@ package org.apache.fop.pdf;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.Writer;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -36,13 +34,13 @@ public class PDFDictionary extends PDFObject {
     /**
      * the entry map
      */
-    protected Map entries = new java.util.HashMap();
+    protected Map<String, Object> entries = new java.util.HashMap<String, Object>();
 
     /**
      * maintains the order of the entries added to the entry map. Whenever you modify
      * "entries", always make sure you adjust this list accordingly.
      */
-    protected List order = new java.util.ArrayList();
+    protected List<String> order = new java.util.ArrayList<String>();
 
     /**
      * Create a new dictionary object.
@@ -86,7 +84,7 @@ public class PDFDictionary extends PDFObject {
         if (!entries.containsKey(name)) {
             this.order.add(name);
         }
-        this.entries.put(name, new Integer(value));
+        this.entries.put(name, Integer.valueOf(value));
     }
 
     /**
@@ -99,51 +97,50 @@ public class PDFDictionary extends PDFObject {
     }
 
     /** {@inheritDoc} */
+    @Override
     protected int output(OutputStream stream) throws IOException {
         CountingOutputStream cout = new CountingOutputStream(stream);
-        Writer writer = PDFDocument.getWriterFor(cout);
+        StringBuilder textBuffer = new StringBuilder(64);
         if (hasObjectNumber()) {
-            writer.write(getObjectID());
+            textBuffer.append(getObjectID());
         }
 
-        writeDictionary(cout, writer);
+        writeDictionary(cout, textBuffer);
 
         if (hasObjectNumber()) {
-            writer.write("\nendobj\n");
+            textBuffer.append("\nendobj\n");
         }
 
-        writer.flush();
+        PDFDocument.flushTextBuffer(textBuffer, cout);
         return cout.getCount();
     }
 
     /**
      * Writes the contents of the dictionary to a StringBuffer.
      * @param out the OutputStream (for binary content)
-     * @param writer the Writer (for text content, wraps the above OutputStream)
+     * @param textBuffer the text buffer for text output
      * @throws IOException if an I/O error occurs
      */
-    protected void writeDictionary(OutputStream out, Writer writer) throws IOException {
-        writer.write("<<");
+    protected void writeDictionary(OutputStream out, StringBuilder textBuffer) throws IOException {
+        textBuffer.append("<<");
         boolean compact = (this.order.size() <= 2);
-        Iterator iter = this.order.iterator();
-        while (iter.hasNext()) {
-            String key = (String)iter.next();
+        for (String key : this.order) {
             if (compact) {
-                writer.write(' ');
+                textBuffer.append(' ');
             } else {
-                writer.write("\n  ");
+                textBuffer.append("\n  ");
             }
-            writer.write(PDFName.escapeName(key));
-            writer.write(' ');
+            textBuffer.append(PDFName.escapeName(key));
+            textBuffer.append(' ');
             Object obj = this.entries.get(key);
-            formatObject(obj, out, writer);
+            formatObject(obj, out, textBuffer);
         }
         if (compact) {
-            writer.write(' ');
+            textBuffer.append(' ');
         } else {
-            writer.write('\n');
+            textBuffer.append('\n');
         }
-        writer.write(">>\n");
+        textBuffer.append(">>\n");
     }
 
 }
index cbca3ea8f9c0f5f315e8b0ae7db95bf67352806d..543d1a45f8e97e0f846e6dff5639b0b9c5963ab8 100644 (file)
@@ -24,7 +24,6 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
-import java.io.Writer;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -274,20 +273,17 @@ public class PDFDocument {
     }
 
     /**
-     * Creates and returns a Writer object wrapping the given OutputStream. The Writer is
-     * buffered to reduce the number of calls to the encoding converter so don't forget
-     * to <code>flush()</code> the Writer after use or before writing directly to the
-     * underlying OutputStream.
-     *
-     * @param out the OutputStream to write to
-     * @return the requested Writer
+     * Flushes the given text buffer to an output stream with the right encoding and resets
+     * the text buffer. This is used to efficiently switch between outputting text and binary
+     * content.
+     * @param textBuffer the text buffer
+     * @param out the output stream to flush the text content to
+     * @throws IOException if an I/O error occurs while writing to the output stream
      */
-    public static Writer getWriterFor(OutputStream out) {
-        try {
-            return new java.io.BufferedWriter(new java.io.OutputStreamWriter(out, ENCODING));
-        } catch (UnsupportedEncodingException uee) {
-            throw new Error("JVM doesn't support " + ENCODING + " encoding!");
-        }
+    public static void flushTextBuffer(StringBuilder textBuffer, OutputStream out)
+            throws IOException {
+        out.write(encode(textBuffer.toString()));
+        textBuffer.setLength(0);
     }
 
     /**
index 8e89f5ada75bb16215731ebf8b44877af12c3a5a..c4979312e94fdc42ebefd7467a51d2ffbae56baf 100644 (file)
@@ -21,7 +21,6 @@ package org.apache.fop.pdf;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.Writer;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.SortedMap;
@@ -39,9 +38,9 @@ public class PDFEmbeddedFiles extends PDFNameTreeNode {
     }
 
     /** {@inheritDoc} */
-    protected void writeDictionary(OutputStream out, Writer writer) throws IOException {
+    protected void writeDictionary(OutputStream out, StringBuilder textBuffer) throws IOException {
         sortNames(); //Sort the names before writing them out
-        super.writeDictionary(out, writer);
+        super.writeDictionary(out, textBuffer);
     }
 
     private void sortNames() {
index e40a12f13e87f57450cf7a38944d401aff710361..7fa6842fa58e5ea68fd69710851f84175d0f6bf1 100644 (file)
@@ -21,7 +21,6 @@ package org.apache.fop.pdf;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.Writer;
 
 import org.apache.commons.io.output.CountingOutputStream;
 
@@ -49,7 +48,7 @@ public class PDFName extends PDFObject {
      * @return the escaped name
      */
     static String escapeName(String name) {
-        StringBuffer sb = new StringBuffer(Math.min(16, name.length() + 4));
+        StringBuilder sb = new StringBuilder(Math.min(16, name.length() + 4));
         boolean skipFirst = false;
         sb.append('/');
         if (name.startsWith("/")) {
@@ -72,7 +71,7 @@ public class PDFName extends PDFObject {
         = {'0', '1', '2', '3', '4', '5', '6', '7',
            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
 
-    private static void toHex(char ch, StringBuffer sb) {
+    private static void toHex(char ch, StringBuilder sb) {
         if (ch >= 256) {
             throw new IllegalArgumentException(
                     "Only 8-bit characters allowed by this implementation");
@@ -82,6 +81,7 @@ public class PDFName extends PDFObject {
     }
 
     /** {@inheritDoc} */
+    @Override
     public String toString() {
         return this.name;
     }
@@ -110,29 +110,30 @@ public class PDFName extends PDFObject {
 
 
     /** {@inheritDoc} */
+    @Override
     protected int output(OutputStream stream) throws IOException {
         CountingOutputStream cout = new CountingOutputStream(stream);
-        Writer writer = PDFDocument.getWriterFor(cout);
+        StringBuilder textBuffer = new StringBuilder(64);
         if (hasObjectNumber()) {
-            writer.write(getObjectID());
+            textBuffer.append(getObjectID());
         }
 
-        writer.write(toString());
+        textBuffer.append(toString());
 
         if (hasObjectNumber()) {
-            writer.write("\nendobj\n");
+            textBuffer.append("\nendobj\n");
         }
 
-        writer.flush();
+        PDFDocument.flushTextBuffer(textBuffer, cout);
         return cout.getCount();
     }
 
-    /** {@inheritDoc} */
-    public void outputInline(OutputStream out, Writer writer) throws IOException {
+    @Override
+    public void outputInline(OutputStream out, StringBuilder textBuffer) throws IOException {
         if (hasObjectNumber()) {
-            writer.write(referencePDF());
+            textBuffer.append(referencePDF());
         } else {
-            writer.write(toString());
+            textBuffer.append(toString());
         }
     }
 
index 7e11a1763d3275a17b62283ad0f2bdf6fb1a5507..43ba2dd82a034aaffb5193baececef8797219771 100644 (file)
@@ -21,7 +21,6 @@ package org.apache.fop.pdf;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.Writer;
 
 /**
  * Class representing a PDF name object.
@@ -38,13 +37,14 @@ public final class PDFNull implements PDFWritable {
     }
 
     /** {@inheritDoc} */
+    @Override
     public String toString() {
         return "null";
     }
 
     /** {@inheritDoc} */
-    public void outputInline(OutputStream out, Writer writer) throws IOException {
-        writer.write(toString());
+    public void outputInline(OutputStream out, StringBuilder textBuffer) throws IOException {
+        textBuffer.append(toString());
     }
 
 }
index 6db7b02ac942800eabb8590aac91c659aa205ad7..ecd3016477705adf235069336bda672017e04a80 100644 (file)
@@ -21,8 +21,6 @@ package org.apache.fop.pdf;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.Writer;
-import java.util.Iterator;
 import java.util.Map;
 import java.util.SortedMap;
 
@@ -34,7 +32,7 @@ import org.apache.commons.io.output.CountingOutputStream;
 public class PDFNumsArray extends PDFObject {
 
     /** Sorted Map holding the values of this array. */
-    protected SortedMap map = new java.util.TreeMap();
+    protected SortedMap<Integer, Object> map = new java.util.TreeMap<Integer, Object>();
 
     /**
      * Create a new, empty array object.
@@ -67,7 +65,7 @@ public class PDFNumsArray extends PDFObject {
      * @param obj the new value
      */
     public void put(int key, Object obj) {
-        put(new Integer(key), obj);
+        put(Integer.valueOf(key), obj);
     }
 
     /**
@@ -85,37 +83,36 @@ public class PDFNumsArray extends PDFObject {
      * @return the requested value
      */
     public Object get(int key) {
-        return get(new Integer(key));
+        return get(Integer.valueOf(key));
     }
 
     /** {@inheritDoc} */
+    @Override
     protected int output(OutputStream stream) throws IOException {
         CountingOutputStream cout = new CountingOutputStream(stream);
-        Writer writer = PDFDocument.getWriterFor(cout);
+        StringBuilder textBuffer = new StringBuilder(64);
         if (hasObjectNumber()) {
-            writer.write(getObjectID());
+            textBuffer.append(getObjectID());
         }
 
-        writer.write('[');
+        textBuffer.append('[');
         boolean first = true;
-        Iterator iter = this.map.entrySet().iterator();
-        while (iter.hasNext()) {
-            Map.Entry entry = (Map.Entry)iter.next();
+        for (Map.Entry<Integer, Object> entry : this.map.entrySet()) {
             if (!first) {
-                writer.write(" ");
+                textBuffer.append(" ");
             }
             first = false;
-            formatObject(entry.getKey(), cout, writer);
-            writer.write(" ");
-            formatObject(entry.getValue(), cout, writer);
+            formatObject(entry.getKey(), cout, textBuffer);
+            textBuffer.append(" ");
+            formatObject(entry.getValue(), cout, textBuffer);
         }
-        writer.write(']');
+        textBuffer.append(']');
 
         if (hasObjectNumber()) {
-            writer.write("\nendobj\n");
+            textBuffer.append("\nendobj\n");
         }
 
-        writer.flush();
+        PDFDocument.flushTextBuffer(textBuffer, cout);
         return cout.getCount();
     }
 
index 6dd0c800fbff346cf88fb517975110639a95361c..d7413322291b07fefceaa1ca3756223b94c32b33 100644 (file)
@@ -217,10 +217,15 @@ public abstract class PDFObject implements PDFWritable {
 
     /** {@inheritDoc} */
     public void outputInline(OutputStream out, Writer writer) throws IOException {
+        throw new UnsupportedOperationException("Don't use anymore: " + getClass().getName());
+    }
+
+    /** {@inheritDoc} */
+    public void outputInline(OutputStream out, StringBuilder textBuffer) throws IOException {
         if (hasObjectNumber()) {
-            writer.write(referencePDF());
+            textBuffer.append(referencePDF());
         } else {
-            writer.flush();
+            PDFDocument.flushTextBuffer(textBuffer, out);
             output(out);
         }
     }
@@ -302,40 +307,46 @@ public abstract class PDFObject implements PDFWritable {
 
     /**
      * Formats an object for serialization to PDF.
+     * <p>
+     * IMPORTANT: If you need to write out binary output, call
+     * {@link PDFDocument#flushTextBuffer(StringBuilder, OutputStream)} before writing any content
+     * to the {@link OutputStream}!
      * @param obj the object
      * @param out the OutputStream to write to
-     * @param writer a Writer for text content (will always be a wrapper around the above
-     *                  OutputStream. Make sure <code>flush</code> is called when mixing calls)
+     * @param textBuffer a text buffer for text output
      * @throws IOException If an I/O error occurs
      */
-    protected void formatObject(Object obj, OutputStream out, Writer writer) throws IOException {
+    protected void formatObject(Object obj, OutputStream out, StringBuilder textBuffer)
+                throws IOException {
         if (obj == null) {
-            writer.write("null");
+            textBuffer.append("null");
         } else if (obj instanceof PDFWritable) {
-            ((PDFWritable)obj).outputInline(out, writer);
+            ((PDFWritable)obj).outputInline(out, textBuffer);
         } else if (obj instanceof Number) {
             if (obj instanceof Double || obj instanceof Float) {
-                writer.write(PDFNumber.doubleOut(((Number)obj).doubleValue()));
+                textBuffer.append(PDFNumber.doubleOut(((Number)obj).doubleValue()));
             } else {
-                writer.write(obj.toString());
+                textBuffer.append(obj.toString());
             }
         } else if (obj instanceof Boolean) {
-            writer.write(obj.toString());
+            textBuffer.append(obj.toString());
         } else if (obj instanceof byte[]) {
-            writer.flush();
+            PDFDocument.flushTextBuffer(textBuffer, out);
             encodeBinaryToHexString((byte[])obj, out);
         } else {
-            writer.flush();
+            PDFDocument.flushTextBuffer(textBuffer, out);
             out.write(encodeText(obj.toString()));
         }
     }
 
-    /** Formatting pattern for PDF date */
-    protected static final SimpleDateFormat DATE_FORMAT;
-
-    static {
-        DATE_FORMAT = new SimpleDateFormat("'D:'yyyyMMddHHmmss", Locale.ENGLISH);
-        DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
+    /**
+     * Returns a SimpleDateFormat instance for formatting PDF date-times.
+     * @return a new SimpleDateFormat instance
+     */
+    protected SimpleDateFormat getPDFDateFormat() {
+        SimpleDateFormat df = new SimpleDateFormat("'D:'yyyyMMddHHmmss", Locale.ENGLISH);
+        df.setTimeZone(TimeZone.getTimeZone("GMT"));
+        return df;
     }
 
     /**
@@ -355,7 +366,7 @@ public abstract class PDFObject implements PDFWritable {
         //DateFormat is operating on GMT so adjust for time zone offset
         Date dt1 = new Date(time.getTime() + offset);
         StringBuffer sb = new StringBuffer();
-        sb.append(DATE_FORMAT.format(dt1));
+        sb.append(getPDFDateFormat().format(dt1));
 
         offset /= (1000 * 60); //Convert to minutes
 
index ce5b46440b9be2540a13f0991dade7ee2afce402..30f21882483e1f545d61dda809efd488f7838f12 100644 (file)
@@ -21,7 +21,6 @@ package org.apache.fop.pdf;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.Writer;
 
 /**
  * class representing a rectangle
@@ -78,16 +77,26 @@ public class PDFRectangle implements PDFWritable {
     }
 
     private String format() {
-        return "[" + llx + " " + lly + " " + urx + " " + ury + "]";
+        StringBuilder textBuffer = new StringBuilder(32);
+        format(textBuffer);
+        return textBuffer.toString();
+    }
+
+    private void format(StringBuilder textBuffer) {
+        textBuffer.append('[').append(llx)
+                  .append(' ').append(lly)
+                  .append(' ').append(urx)
+                  .append(' ').append(ury).append(']');
     }
 
     /** {@inheritDoc} */
+    @Override
     public String toString() {
         return "PDFRectangle" + format();
     }
 
     /** {@inheritDoc} */
-    public void outputInline(OutputStream out, Writer writer) throws IOException {
-        writer.write(format());
+    public void outputInline(OutputStream out, StringBuilder textBuffer) throws IOException {
+        format(textBuffer);
     }
 }
index 1d9ddc349480d1011d17d2d47643fd7d843f90e5..29f8c7c4844c3b86a9a4e5323b1e5b5a96b20e1d 100644 (file)
@@ -19,9 +19,7 @@
 
 package org.apache.fop.pdf;
 
-import java.io.IOException;
 import java.io.OutputStream;
-import java.io.Writer;
 import java.lang.ref.Reference;
 import java.lang.ref.SoftReference;
 
@@ -36,7 +34,7 @@ public class PDFReference implements PDFWritable {
     private int objectNumber;
     private int generation;
 
-    private Reference objReference;
+    private Reference<PDFObject> objReference;
 
     /**
      * Creates a new PDF reference.
@@ -45,7 +43,7 @@ public class PDFReference implements PDFWritable {
     public PDFReference(PDFObject obj) {
         this.objectNumber = obj.getObjectNumber();
         this.generation = obj.getGeneration();
-        this.objReference = new SoftReference(obj);
+        this.objReference = new SoftReference<PDFObject>(obj);
     }
 
     /**
@@ -69,7 +67,7 @@ public class PDFReference implements PDFWritable {
      */
     public PDFObject getObject() {
         if (this.objReference != null) {
-            PDFObject obj = (PDFObject)this.objReference.get();
+            PDFObject obj = this.objReference.get();
             if (obj == null) {
                 this.objReference = null;
             }
@@ -96,13 +94,16 @@ public class PDFReference implements PDFWritable {
     }
 
     /** {@inheritDoc} */
+    @Override
     public String toString() {
-        return getObjectNumber() + " " + getGeneration() + " R";
+        StringBuilder textBuffer = new StringBuilder();
+        outputInline(null, textBuffer);
+        return textBuffer.toString();
     }
 
     /** {@inheritDoc} */
-    public void outputInline(OutputStream out, Writer writer) throws IOException {
-        writer.write(toString());
+    public void outputInline(OutputStream out, StringBuilder textBuffer) {
+        textBuffer.append(getObjectNumber()).append(' ').append(getGeneration()).append(" R");
     }
 
 }
index 6e71dc7d8a393c739e157c30a5d43c51e88a81d4..67f10ff758350758898ee2f4114c24eff39259d4 100644 (file)
@@ -21,7 +21,6 @@ package org.apache.fop.pdf;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.Writer;
 
 /**
  * This interface is implemented by classes that can be serialized to a PDF file either by
@@ -30,13 +29,16 @@ import java.io.Writer;
 public interface PDFWritable {
 
     /**
-     * Writes a "direct object" (inline object) representation to the stream. A Writer is given
-     * for optimized encoding of text content. Since the Writer is buffered, make sure
-     * <code>flush()</code> is called before any direct calls to <code>out</code> are made.
+     * Writes a "direct object" (inline object) representation to the stream. A text buffer is given
+     * for optimized encoding of text content.
+     * <p>
+     * IMPORTANT: If you need to write out binary output, call
+     * {@link PDFDocument#flushTextBuffer(StringBuilder, OutputStream)} before writing any content
+     * to the {@link OutputStream}!
      * @param out the OutputStream (for binary content)
-     * @param writer the Writer (for text content, wraps the above OutputStream)
+     * @param textBuffer the text buffer for text content
      * @throws IOException if an I/O error occurs
      */
-    void outputInline(OutputStream out, Writer writer) throws IOException;
+    void outputInline(OutputStream out, StringBuilder textBuffer) throws IOException;
 
 }
index 9c47a3dc344f053a75b0943fa8db0ce6903fbf63..5006a20cc75474a30b5d47985b44abbe2b50bb10 100644 (file)
@@ -61,6 +61,9 @@
       documents. Example: the fix of marks layering will be such a case when it's done.
     -->
     <release version="FOP Trunk" date="TBD">
+      <action context="Code" dev="CB" type="add" fixes-bug="51664" due-to="Mehdi Houshmand">
+        Tagged PDF performance improvement plus tests
+      </action>        
       <action context="Code" dev="MH" type="add" fixes-bug="52197">
         Improved AdobeStandardEncoding support in AFM files for type1 fonts
       </action>
index 548a2e4aac68f9b1149b3e821af9cb36b8a25ccb..bb59c8605a21cf0d7e9d563cfd045b91f3f32e2f 100644 (file)
@@ -44,6 +44,7 @@ import org.apache.fop.render.pdf.RenderPDFTestSuite;
 import org.apache.fop.render.ps.PSTestSuite;
 import org.apache.fop.render.rtf.RichTextFormatTestSuite;
 import org.apache.fop.traits.MinOptMaxTestCase;
+import org.apache.fop.pdf.PDFLibraryTestSuite;
 
 /**
  * Test suite for basic functionality of FOP.
@@ -70,6 +71,7 @@ import org.apache.fop.traits.MinOptMaxTestCase;
         MODCAParserTestCase.class,
         CharactersetEncoderTestCase.class,
         org.apache.fop.render.afp.AFPTestSuite.class,
+        PDFLibraryTestSuite.class,
         PSTestSuite.class,
         MinOptMaxTestCase.class,
         AdobeStandardEncodingTestCase.class,
index 442d63f5b298408452a6292a7f21bc3eb2d820d2..cf6b8875d236f2c273f32ad42e26eb1c93385a84 100644 (file)
@@ -28,12 +28,10 @@ import org.apache.fop.pdf.FileIDGeneratorTestCase;
 import org.apache.fop.pdf.PDFDocumentGraphics2DTestCase;
 import org.apache.fop.pdf.PDFEncryptionJCETestCase;
 import org.apache.fop.pdf.PDFFactoryTestCase;
-import org.apache.fop.pdf.PDFObjectTestCase;
 import org.apache.fop.traits.BorderPropsTestCase;
 import org.apache.fop.util.BitmapImageUtilTestCase;
 import org.apache.fop.util.ColorUtilTestCase;
 import org.apache.fop.util.ElementListUtilsTestCase;
-import org.apache.fop.util.PDFNumberTestCase;
 import org.apache.fop.util.XMLResourceBundleTestCase;
 
 /**
@@ -41,8 +39,6 @@ import org.apache.fop.util.XMLResourceBundleTestCase;
  */
 @RunWith(Suite.class)
 @SuiteClasses({
-    PDFNumberTestCase.class,
-    PDFObjectTestCase.class,
     ColorUtilTestCase.class,
     BorderPropsTestCase.class,
     ElementListUtilsTestCase.class,
diff --git a/test/java/org/apache/fop/pdf/AbstractPDFStreamTestCase.java b/test/java/org/apache/fop/pdf/AbstractPDFStreamTestCase.java
new file mode 100644 (file)
index 0000000..59b3452
--- /dev/null
@@ -0,0 +1,92 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
+\r
+package org.apache.fop.pdf;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+import static org.junit.Assert.assertEquals;\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+\r
+/**\r
+ * Test case for {@link AbstractPDFStream}.\r
+ */\r
+public class AbstractPDFStreamTestCase extends PDFObjectTestCase {\r
+\r
+    private AbstractPDFStream abstractStream;\r
+\r
+    private String textData = "This is an arbitrary string for testing.";\r
+\r
+    private static byte[] encodedBytes;\r
+    static {\r
+        int[] encoded = { 0x78, 0x9c, 0x0b, 0xc9, 0xc8, 0x2c, 0x56, 0x00, 0xa2, 0xc4, 0x3c, 0x85,\r
+                0xc4, 0xa2, 0xa4, 0xcc, 0x92, 0xa2, 0xc4, 0xa2, 0x4a, 0x85, 0xe2, 0x92, 0xa2, 0xcc,\r
+                0xbc, 0x74, 0x85, 0xb4, 0xfc, 0x22, 0x85, 0x92, 0xd4, 0xe2, 0x12, 0x20, 0x5b, 0x0f,\r
+                0x00, 0x2d, 0x2b, 0x0e, 0xde, 0x0a };\r
+        encodedBytes = new byte[encoded.length];\r
+        int i = 0;\r
+        for (int in : encoded) {\r
+            encodedBytes[i++] = (byte) (in & 0xff);\r
+        }\r
+    }\r
+    private String startStream = "1 0 obj\n" +\r
+                                "<< /Length 5 0 R /Filter /FlateDecode >>\n" +\r
+                                "stream\n";\r
+    private String endStream = "endstream\nendobj\n";\r
+\r
+    @Before\r
+    public void setUp() {\r
+        abstractStream = new AbstractPDFStream() {\r
+\r
+            @Override\r
+            protected void outputRawStreamData(OutputStream out) throws IOException {\r
+                out.write(textData.getBytes());\r
+            }\r
+\r
+            @Override\r
+            protected int getSizeHint() throws IOException {\r
+                return textData.length();\r
+            }\r
+        };\r
+        abstractStream.setDocument(doc);\r
+        abstractStream.setParent(parent);\r
+\r
+        pdfObjectUnderTest = abstractStream;\r
+    }\r
+\r
+    /**\r
+     * Tests output() - ensure that this object is correctly formatted to the output stream.\r
+     * @throws IOException if an I/O error occurs\r
+     */\r
+    @Test\r
+    public void testOutput() throws IOException {\r
+        // This differs from most other objects, if the object number = 0 an exception is thrown\r
+        ByteArrayOutputStream outStream = new ByteArrayOutputStream();\r
+        abstractStream.setObjectNumber(1);\r
+        ByteArrayOutputStream expectedStream = new ByteArrayOutputStream();\r
+        expectedStream.write(startStream.getBytes());\r
+        expectedStream.write(encodedBytes);\r
+        expectedStream.write(endStream.getBytes());\r
+        assertEquals(expectedStream.size(), abstractStream.output(outStream));\r
+        assertEquals(expectedStream.toString(), outStream.toString());\r
+    }\r
+}\r
diff --git a/test/java/org/apache/fop/pdf/PDFArrayTestCase.java b/test/java/org/apache/fop/pdf/PDFArrayTestCase.java
new file mode 100644 (file)
index 0000000..73fda56
--- /dev/null
@@ -0,0 +1,237 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
+\r
+package org.apache.fop.pdf;\r
+\r
+import static org.junit.Assert.assertFalse;\r
+import static org.junit.Assert.assertTrue;\r
+import static org.junit.Assert.assertEquals;\r
+import static org.junit.Assert.fail;\r
+\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+\r
+import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+/**\r
+ * Test case for {@link PDFArray}.\r
+ */\r
+public class PDFArrayTestCase extends PDFObjectTestCase {\r
+    private PDFArray intArray;\r
+    private String intArrayOutput;\r
+    private PDFArray doubleArray;\r
+    private String doubleArrayOutput;\r
+    private PDFArray collectionArray;\r
+    private String collectionArrayOutput;\r
+    private PDFArray objArray;\r
+    private String objArrayOutput;\r
+\r
+    /** A PDF object used solely for testing */\r
+    private PDFNumber num;\r
+\r
+    @Before\r
+    public void setUp() {\r
+        intArray = new PDFArray(parent, new int[] {1, 2, 3, 4, 5});\r
+        intArrayOutput = "[1 2 3 4 5]";\r
+\r
+        doubleArray = new PDFArray(parent, new double[] {1.1, 2.2, 3.3, 4.4, 5.5});\r
+        doubleArrayOutput = "[1.1 2.2 3.3 4.4 5.5]";\r
+\r
+        List<Object> strList = new ArrayList<Object>();\r
+        strList.add("one");\r
+        strList.add("two");\r
+        strList.add("three");\r
+        collectionArray = new PDFArray(parent, strList);\r
+        collectionArrayOutput = "[(one) (two) (three)]";\r
+\r
+        // Set arbitrary values here\r
+        num = new PDFNumber();\r
+        num.setNumber(20);\r
+        num.setObjectNumber(4);\r
+        objArray = new PDFArray(parent, new Object[] {"one", 2, 3.0f, num});\r
+        objArrayOutput = "[(one) 2 3 4 0 R]";\r
+\r
+        // set the document\r
+        intArray.setDocument(doc);\r
+        doubleArray.setDocument(doc);\r
+        collectionArray.setDocument(doc);\r
+        objArray.setDocument(doc);\r
+\r
+        // Test the progenitor in the inheritance stack\r
+        objArray.setParent(parent);\r
+        pdfObjectUnderTest = objArray;\r
+    }\r
+\r
+    private void intArrayContainsTests() {\r
+        for (int i = 1; i <= 5; i++) {\r
+            assertTrue(intArray.contains(i));\r
+        }\r
+        assertFalse(intArray.contains(6));\r
+        assertFalse(intArray.contains(0));\r
+    }\r
+\r
+    private void doubleArrayContainsTests() {\r
+        assertTrue(doubleArray.contains(1.1));\r
+        assertTrue(doubleArray.contains(2.2));\r
+        assertTrue(doubleArray.contains(3.3));\r
+        assertTrue(doubleArray.contains(4.4));\r
+        assertTrue(doubleArray.contains(5.5));\r
+        assertFalse(doubleArray.contains(10.0));\r
+        assertFalse(doubleArray.contains(0.0));\r
+    }\r
+\r
+    private void collectionArrayContainsTests() {\r
+        assertTrue(collectionArray.contains("one"));\r
+        assertTrue(collectionArray.contains("two"));\r
+        assertTrue(collectionArray.contains("three"));\r
+        assertFalse(collectionArray.contains("zero"));\r
+        assertFalse(collectionArray.contains("four"));\r
+    }\r
+\r
+    private void objectArrayContainsTests() {\r
+        assertTrue(objArray.contains("one"));\r
+        assertTrue(objArray.contains(2));\r
+        assertTrue(objArray.contains(3.0f));\r
+        assertTrue(objArray.contains(num));\r
+        assertFalse(objArray.contains("four"));\r
+        assertFalse(objArray.contains(0.0));\r
+    }\r
+\r
+    /**\r
+     * Test contains() - test whether this PDFArray contains an object.\r
+     */\r
+    @Test\r
+    public void testContains() {\r
+        // Test some arbitrary values\r
+        intArrayContainsTests();\r
+        doubleArrayContainsTests();\r
+        collectionArrayContainsTests();\r
+        objectArrayContainsTests();\r
+    }\r
+\r
+    /**\r
+     * Test length() - tests the length of an array.\r
+     */\r
+    @Test\r
+    public void testLength() {\r
+        assertEquals(5, intArray.length());\r
+        assertEquals(5, doubleArray.length());\r
+        assertEquals(3, collectionArray.length());\r
+        assertEquals(4, objArray.length());\r
+\r
+        // Test the count is incremented when an object is added (this only\r
+        // needs to be tested once)\r
+        intArray.add(6);\r
+        assertEquals(6, intArray.length());\r
+    }\r
+\r
+    /**\r
+     * Test set() - tests that a particular point has been properly set.\r
+     */\r
+    @Test\r
+    public void testSet() {\r
+        PDFName name = new PDFName("zero test");\r
+        objArray.set(0, name);\r
+        assertEquals(name, objArray.get(0));\r
+\r
+        objArray.set(1, "test");\r
+        assertEquals("test", objArray.get(1));\r
+        // This goes through the set(int, double) code path rather than set(int, Object)\r
+        objArray.set(2, 5);\r
+        assertEquals(5.0, objArray.get(2));\r
+        try {\r
+            objArray.set(4, 2);\r
+            fail("out of bounds");\r
+        } catch (IndexOutOfBoundsException e) {\r
+            // Pass\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Test get() - gets the object stored at a given index.\r
+     */\r
+    @Test\r
+    public void testGet() {\r
+        // Test some arbitrary values\r
+        for (int i = 1; i <= 5; i++) {\r
+            assertEquals(i, intArray.get(i - 1));\r
+        }\r
+\r
+        assertEquals(1.1, doubleArray.get(0));\r
+        assertEquals(2.2, doubleArray.get(1));\r
+        assertEquals(3.3, doubleArray.get(2));\r
+        assertEquals(4.4, doubleArray.get(3));\r
+        assertEquals(5.5, doubleArray.get(4));\r
+\r
+        assertEquals("one", collectionArray.get(0));\r
+        assertEquals("two", collectionArray.get(1));\r
+        assertEquals("three", collectionArray.get(2));\r
+\r
+        assertEquals("one", objArray.get(0));\r
+        assertEquals(2, objArray.get(1));\r
+        assertEquals(0, Double.compare(3.0, (Float) objArray.get(2)));\r
+        assertEquals(num, objArray.get(3));\r
+    }\r
+\r
+    /**\r
+     * Tests add() - tests that objects are appended to the end of the array as expected.\r
+     */\r
+    @Test\r
+    public void testAdd() {\r
+        intArray.add(new Integer(6));\r
+        doubleArray.add(6.6);\r
+        // Test some arbitrary values\r
+        for (int i = 1; i <= 6; i++) {\r
+            assertEquals(i, intArray.get(i - 1));\r
+        }\r
+\r
+        assertEquals(1.1, doubleArray.get(0));\r
+        assertEquals(2.2, doubleArray.get(1));\r
+        assertEquals(3.3, doubleArray.get(2));\r
+        assertEquals(4.4, doubleArray.get(3));\r
+        assertEquals(5.5, doubleArray.get(4));\r
+        assertEquals(6.6, doubleArray.get(5));\r
+\r
+        collectionArray.add(1);\r
+        assertEquals("one", collectionArray.get(0));\r
+        assertEquals("two", collectionArray.get(1));\r
+        assertEquals("three", collectionArray.get(2));\r
+        assertEquals(1.0, collectionArray.get(3));\r
+\r
+        objArray.add("four");\r
+        assertEquals("one", objArray.get(0));\r
+        assertEquals(2, objArray.get(1));\r
+        assertEquals(0, Double.compare(3.0, (Float) objArray.get(2)));\r
+        assertEquals("four", objArray.get(4));\r
+    }\r
+\r
+    /**\r
+     * Tests output() - tests that this object is properly streamed to the PDF document.\r
+     * @throws IOException error caused by I/O\r
+     */\r
+    @Test\r
+    public void testOutput() throws IOException {\r
+        testOutputStreams(intArrayOutput, intArray);\r
+        testOutputStreams(doubleArrayOutput, doubleArray);\r
+        testOutputStreams(collectionArrayOutput, collectionArray);\r
+        testOutputStreams(objArrayOutput, objArray);\r
+    }\r
+}\r
diff --git a/test/java/org/apache/fop/pdf/PDFDestsTestCase.java b/test/java/org/apache/fop/pdf/PDFDestsTestCase.java
new file mode 100644 (file)
index 0000000..be2dfe4
--- /dev/null
@@ -0,0 +1,64 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
+\r
+package org.apache.fop.pdf;\r
+\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+\r
+import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+/**\r
+ * Test case for {@link PDFDests}.\r
+ */\r
+public class PDFDestsTestCase extends PDFObjectTestCase {\r
+\r
+    private PDFDests dests = new PDFDests();\r
+    private String expectedString = "<< /Names [(number) 10 (name) /Test#20name] >>\n";\r
+\r
+    @Before\r
+    public void setUp() {\r
+        List<PDFDestination> destinations = new ArrayList<PDFDestination>();\r
+        PDFNumber number = new PDFNumber();\r
+        number.setNumber(10);\r
+        PDFDestination testNumber = new PDFDestination("number", number);\r
+        testNumber.setDocument(doc);\r
+        destinations.add(testNumber);\r
+        PDFDestination testName = new PDFDestination("name", new PDFName("Test name"));\r
+        testName.setDocument(doc);\r
+        destinations.add(testName);\r
+\r
+        dests = new PDFDests(destinations);\r
+        dests.setDocument(doc);\r
+        dests.setParent(parent);\r
+        pdfObjectUnderTest = dests;\r
+    }\r
+\r
+    /**\r
+     * Populate the object with some arbitrary values and ensure they are wrapped properly.\r
+     * @throws IOException if an I/O error occurs\r
+     */\r
+    @Test\r
+    public void testConstructor() throws IOException {\r
+        // Seems the only way to test this is by testing the output\r
+        testOutputStreams(expectedString, dests);\r
+    }\r
+}\r
diff --git a/test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java b/test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java
new file mode 100644 (file)
index 0000000..0ef5171
--- /dev/null
@@ -0,0 +1,135 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
+\r
+package org.apache.fop.pdf;\r
+\r
+import static org.junit.Assert.assertEquals;\r
+import static org.junit.Assert.assertNull;\r
+import static org.junit.Assert.fail;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+\r
+import org.apache.commons.io.output.CountingOutputStream;\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+\r
+\r
+/**\r
+ * Test case for {@link PDFDictionary}.\r
+ */\r
+public class PDFDictionaryTestCase extends PDFObjectTestCase {\r
+    /** The test subject */\r
+    private PDFDictionary pdfDictUnderTest;\r
+    private PDFArray testArray;\r
+    private PDFNumber testNumber;\r
+    /** The order in which these objects are put into the dictionary MUST be maintained. */\r
+    private String expectedOutput = "<<\n"\r
+                                  + "  /String (TestValue)\n"\r
+                                  + "  /int 10\n"\r
+                                  + "  /double 3.1\n"\r
+                                  + "  /array [1 (two) 20]\n"\r
+                                  + "  /number 20\n"\r
+                                  + "  /null null\n"\r
+                                  + ">>\n";\r
+\r
+    @Before\r
+    public void setUp() {\r
+        // A PDFNumber for testing, this DOES have a parent\r
+        testNumber = new PDFNumber();\r
+        testNumber.setParent(parent);\r
+        testNumber.setNumber(20);\r
+        // An array for testing, this DOES NOT have a parent\r
+        testArray = new PDFArray();\r
+        testArray.add(1);\r
+        testArray.add("two");\r
+        testArray.add(testNumber);\r
+        // Populating the dictionary with a parent, document and the various objects\r
+        pdfDictUnderTest = new PDFDictionary(parent);\r
+        pdfDictUnderTest.setDocument(doc);\r
+        pdfDictUnderTest.put("String", "TestValue");\r
+        pdfDictUnderTest.put("int", 10);\r
+        pdfDictUnderTest.put("double", Double.valueOf(3.1));\r
+        pdfDictUnderTest.put("array", testArray);\r
+        pdfDictUnderTest.put("number", testNumber);\r
+        // null is a valid PDF object\r
+        pdfDictUnderTest.put("null", null);\r
+        // test that the interface is maintained\r
+        pdfObjectUnderTest = pdfDictUnderTest;\r
+    }\r
+\r
+    /**\r
+     * Tests put() - tests that the object is put into the dictionary and it is handled if it is a\r
+     * {@link PDFObject}.\r
+     */\r
+    @Test\r
+    public void testPut() {\r
+        // The "put()" commands have already been done in setUp(), so just test them.\r
+        assertEquals("TestValue", pdfDictUnderTest.get("String"));\r
+        assertEquals(10, pdfDictUnderTest.get("int"));\r
+        assertEquals(3.1, pdfDictUnderTest.get("double"));\r
+        // With PDFObjects, if they DO NOT have a parent, the dict becomes their parent.\r
+        assertEquals(testArray, pdfDictUnderTest.get("array"));\r
+        assertEquals(pdfDictUnderTest, testArray.getParent());\r
+        // With PDFObjects, if they DO have a parent, the dict DOES NOT change the parent object.\r
+        assertEquals(testNumber, pdfDictUnderTest.get("number"));\r
+        // Test it doesn't explode when we try to get a non-existent entry\r
+        assertNull(pdfDictUnderTest.get("Not in dictionary"));\r
+        // Tests that we can over-write objects\r
+        pdfDictUnderTest.put("array", 10);\r
+        assertEquals(10, pdfDictUnderTest.get("array"));\r
+        // Test that nulls are handled appropriately\r
+        assertNull(pdfDictUnderTest.get("null"));\r
+    }\r
+\r
+    /**\r
+     * Tests get() - tests that objects can be properly retrieved from the dictionary.\r
+     */\r
+    @Test\r
+    public void testGet() {\r
+        // Tested fairly comprehensively in testPut().\r
+    }\r
+\r
+    /**\r
+     * Tests writeDictionary() - tests that the dictionary is properly written to the output-stream.\r
+     */\r
+    @Test\r
+    public void testWriteDictionary() {\r
+        // Ensure that the objects stored in the dictionary are streamed in the correct format.\r
+        ByteArrayOutputStream outStream = new ByteArrayOutputStream();\r
+        CountingOutputStream cout = new CountingOutputStream(outStream);\r
+        StringBuilder textBuffer = new StringBuilder();\r
+        try {\r
+            pdfDictUnderTest.writeDictionary(cout, textBuffer);\r
+            PDFDocument.flushTextBuffer(textBuffer, cout);\r
+            assertEquals(expectedOutput, outStream.toString());\r
+        } catch (IOException e) {\r
+            fail("IOException: " + e.getMessage());\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Tests output() - test that this object can write itself to an output stream.\r
+     * @throws IOException error caused by I/O\r
+     */\r
+    @Test\r
+    public void testOutput() throws IOException {\r
+        testOutputStreams(expectedOutput, pdfDictUnderTest);\r
+    }\r
+}\r
diff --git a/test/java/org/apache/fop/pdf/PDFDocumentTestCase.java b/test/java/org/apache/fop/pdf/PDFDocumentTestCase.java
new file mode 100644 (file)
index 0000000..dc3a05e
--- /dev/null
@@ -0,0 +1,62 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
+\r
+package org.apache.fop.pdf;\r
+\r
+import static org.junit.Assert.assertEquals;\r
+\r
+import org.junit.Test;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+\r
+/**\r
+ * Test case for {@link PDFDocument}\r
+ */\r
+public class PDFDocumentTestCase {\r
+\r
+    /**\r
+     * Test flushTextBuffer() - ensure that the text given will stream to the PDF document as\r
+     * expected.\r
+     * @throws IOException when an I/O error occurs\r
+     */\r
+    @Test\r
+    public void testFlushTextBuffer() throws IOException {\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        StringBuilder textBuffer = new StringBuilder();\r
+        String testString = "This is a test string, just some arbitrary data.";\r
+        textBuffer.append(testString);\r
+        \r
+        PDFDocument.flushTextBuffer(textBuffer, out);\r
+        assertEquals(testString, out.toString());\r
+        \r
+        // Should reset the textBuffer\r
+        assertEquals(0, textBuffer.length());\r
+        assertEquals("", textBuffer.toString());\r
+        out.reset();\r
+\r
+        String[] strArray = { "Try ", "with ", "multiple ", "strings." };\r
+        for (String str : strArray) {\r
+            textBuffer.append(str);\r
+        }\r
+        String fullString = textBuffer.toString();\r
+        PDFDocument.flushTextBuffer(textBuffer, out);\r
+        assertEquals(fullString, out.toString());\r
+    }\r
+}\r
diff --git a/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java b/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java
new file mode 100644 (file)
index 0000000..0332ec7
--- /dev/null
@@ -0,0 +1,54 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
+\r
+package org.apache.fop.pdf;\r
+\r
+import junit.framework.Test;\r
+import junit.framework.TestSuite;\r
+\r
+\r
+/**\r
+ * Test suite for FOP's utility classes.\r
+ */\r
+public class PDFLibraryTestSuite {\r
+\r
+    /**\r
+     * Builds the test suite\r
+     * @return the test suite\r
+     */\r
+    public static Test suite() {\r
+        TestSuite suite = new TestSuite(\r
+            "Test suite for FOP's utility classes");\r
+        //$JUnit-BEGIN$\r
+        suite.addTest(new TestSuite(PDFArrayTestCase.class));\r
+        suite.addTest(new TestSuite(PDFDictionaryTestCase.class));\r
+        suite.addTest(new TestSuite(PDFNumberTestCase.class));\r
+        suite.addTest(new TestSuite(PDFObjectTestCase.class));\r
+        suite.addTest(new TestSuite(PDFNameTestCase.class));\r
+        suite.addTest(new TestSuite(AbstractPDFStreamTestCase.class));\r
+        suite.addTest(new TestSuite(PDFDestsTestCase.class));\r
+        suite.addTest(new TestSuite(PDFDocumentTestCase.class));\r
+        suite.addTest(new TestSuite(PDFNullTestCase.class));\r
+        suite.addTest(new TestSuite(PDFNumsArrayTestCase.class));\r
+        suite.addTest(new TestSuite(PDFRectangleTestCase.class));\r
+        suite.addTest(new TestSuite(PDFReferenceTestCase.class));\r
+        //$JUnit-END$\r
+        return suite;\r
+    }\r
+}\r
diff --git a/test/java/org/apache/fop/pdf/PDFNameTestCase.java b/test/java/org/apache/fop/pdf/PDFNameTestCase.java
new file mode 100644 (file)
index 0000000..189f2e6
--- /dev/null
@@ -0,0 +1,169 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
+\r
+package org.apache.fop.pdf;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+\r
+import org.apache.commons.io.output.CountingOutputStream;\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+import static org.junit.Assert.fail;\r
+import static org.junit.Assert.assertEquals;\r
+\r
+/**\r
+ * Test class for {@link PDFName}.\r
+ */\r
+public class PDFNameTestCase extends PDFObjectTestCase {\r
+    private PDFName pdfName;\r
+\r
+    /**\r
+     * Sets up the local variables\r
+     */\r
+    @Before\r
+    public void setUp() {\r
+        pdfName = new PDFName("TestName");\r
+        pdfName.setParent(parent);\r
+        pdfName.setDocument(doc);\r
+\r
+        pdfObjectUnderTest = pdfName;\r
+    }\r
+\r
+    /**\r
+     * Tests escapeName() - tests that this method escapes the necessary characters.\r
+     */\r
+    @Test\r
+    public void testEscapeName() {\r
+        try {\r
+            // Test for null, this is a programming error thus the NPE\r
+            PDFName.escapeName(null);\r
+            fail("NPE not thrown when null object given to escapeName()");\r
+        } catch (NullPointerException e) {\r
+            // PASS\r
+        }\r
+        // All names are prefixed by "/", check the PDF spec for further details.\r
+        assertEquals("/Test", PDFName.escapeName("Test"));\r
+        // Check that if the name is already prefixed with "/" it doens't do it twice\r
+        assertEquals("/Test", PDFName.escapeName("/Test"));\r
+        // Test with a space in the middle\r
+        assertEquals("/Test#20test", PDFName.escapeName("Test test"));\r
+        // Test that all chars apart from ASCII '!' --> '~' are escaped\r
+        nonEscapedCharactersTests();\r
+        escapedCharactersTests();\r
+    }\r
+\r
+    private void escapedCharactersTests() {\r
+        for (char i = 0; i < '!'; i++) {\r
+            String str = Integer.toHexString(i >>> 4 & 0x0f).toUpperCase();\r
+            str += Integer.toHexString(i & 0x0f).toUpperCase();\r
+            assertEquals("/#" + str, PDFName.escapeName(String.valueOf(i)));\r
+        }\r
+        for (char i = '~' + 1; i < 256; i++) {\r
+            String str = Integer.toHexString(i >>> 4 & 0x0f).toUpperCase();\r
+            str += Integer.toHexString(i & 0x0f).toUpperCase();\r
+            assertEquals("/#" + str, PDFName.escapeName(String.valueOf(i)));\r
+        }\r
+        checkCharacterIsEscaped('#');\r
+        checkCharacterIsEscaped('%');\r
+        checkCharacterIsEscaped('(');\r
+        checkCharacterIsEscaped(')');\r
+        checkCharacterIsEscaped('<');\r
+        checkCharacterIsEscaped('>');\r
+        checkCharacterIsEscaped('[');\r
+        checkCharacterIsEscaped(']');\r
+        checkCharacterIsEscaped('>');\r
+    }\r
+\r
+    private void checkCharacterIsEscaped(char c) {\r
+        String str = Integer.toHexString(c >>> 4 & 0x0f).toUpperCase();\r
+        str += Integer.toHexString(c & 0x0f).toUpperCase();\r
+        assertEquals("/#" + str, PDFName.escapeName(String.valueOf(c)));\r
+    }\r
+\r
+    private void nonEscapedCharactersTests() {\r
+        charactersNotEscapedBetween('!', '"');\r
+        charactersNotEscapedBetween('*', ';');\r
+        charactersNotEscapedBetween('?', 'Z');\r
+        charactersNotEscapedBetween('^', '~');\r
+    }\r
+\r
+    private void charactersNotEscapedBetween(char c1, char c2) {\r
+        for (char i = c1; i <= c2; i++) {\r
+            String str = String.valueOf(i);\r
+            String expected = !str.equals("/") ? "/" + str : str;\r
+            assertEquals(expected, PDFName.escapeName(str));\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Tests toString() - this has been overridden to return the String that PDFName wraps.\r
+     */\r
+    @Test\r
+    public void testToString() {\r
+        // The escape characters have already been tested in testEscapeName() so this doesn't need\r
+        // to be done twice.\r
+        PDFName test1 = new PDFName("test1");\r
+        assertEquals("/test1", test1.toString());\r
+        PDFName test2 = new PDFName("another test");\r
+        assertEquals("/another#20test", test2.toString());\r
+        try {\r
+            new PDFName(null);\r
+            fail("NPE not thrown when null passed to constructor");\r
+        } catch (NullPointerException e) {\r
+            // PASS\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Tests output() - check that this object can stream itself in the correct format.\r
+     * @throws IOException error caused by I/O\r
+     */\r
+    @Test\r
+    public void testOutput() throws IOException {\r
+        testOutputStreams("/TestName", pdfName);\r
+        testOutputStreams("/test#20test", new PDFName("test test"));\r
+    }\r
+\r
+    /**\r
+     * Test outputInline() - this writes the object reference if it is a direct object (has an\r
+     * object number), or writes the String representation if there is no object number.\r
+     */\r
+    @Test\r
+    public void testOutputInline() {\r
+        ByteArrayOutputStream outStream = new ByteArrayOutputStream();\r
+        CountingOutputStream cout = new CountingOutputStream(outStream);\r
+        StringBuilder textBuffer = new StringBuilder();\r
+        try {\r
+            // test with no object number set.\r
+            pdfName.outputInline(outStream, textBuffer);\r
+            PDFDocument.flushTextBuffer(textBuffer, cout);\r
+            assertEquals("/TestName", outStream.toString());\r
+\r
+            outStream.reset();\r
+            // test with object number set\r
+            pdfName.setObjectNumber(1);\r
+            pdfName.outputInline(outStream, textBuffer);\r
+            PDFDocument.flushTextBuffer(textBuffer, cout);\r
+            assertEquals("1 0 R", outStream.toString());\r
+        } catch (IOException e) {\r
+            fail("IOException: " + e.getMessage());\r
+        }\r
+    }\r
+}\r
diff --git a/test/java/org/apache/fop/pdf/PDFNullTestCase.java b/test/java/org/apache/fop/pdf/PDFNullTestCase.java
new file mode 100644 (file)
index 0000000..51d3de4
--- /dev/null
@@ -0,0 +1,49 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
+\r
+package org.apache.fop.pdf;\r
+\r
+import static org.junit.Assert.assertEquals;\r
+\r
+import org.junit.Test;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+\r
+/**\r
+ * Test case for {@link PDFNull}.\r
+ */\r
+public class PDFNullTestCase extends PDFObjectTestCase {\r
+\r
+    /**\r
+     * Test outputInline() - test that "null" is printed to the output stream.\r
+     */\r
+    @Test\r
+    public void testOutputInline() throws IOException {\r
+        PDFNull obj = PDFNull.INSTANCE;\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        StringBuilder text = new StringBuilder();\r
+        obj.outputInline(out, text);\r
+        assertEquals("null", text.toString());\r
+\r
+        // Ensure previously written text is not discarded\r
+        obj.outputInline(out, text);\r
+        assertEquals("nullnull", text.toString());\r
+    }\r
+}\r
diff --git a/test/java/org/apache/fop/pdf/PDFNumberTestCase.java b/test/java/org/apache/fop/pdf/PDFNumberTestCase.java
new file mode 100644 (file)
index 0000000..61d7b02
--- /dev/null
@@ -0,0 +1,162 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
+\r
+package org.apache.fop.pdf;\r
+\r
+import static org.junit.Assert.assertEquals;\r
+import static org.junit.Assert.fail;\r
+\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+\r
+import java.io.IOException;\r
+\r
+/**\r
+ * This test tests PDFNumber's doubleOut() methods.\r
+ */\r
+public class PDFNumberTestCase extends PDFObjectTestCase {\r
+    /**\r
+     * Sets up the local variables, most of these are inherited from PDFObjectTestCase\r
+     */\r
+    @Before\r
+    public void setUp() {\r
+        pdfObjectUnderTest = new PDFNumber();\r
+        pdfObjectUnderTest.setParent(parent);\r
+        pdfObjectUnderTest.setDocument(doc);\r
+    }\r
+\r
+    /**\r
+     * Tests PDFNumber.doubleOut().\r
+     * @throws Exception if the test fails\r
+     */\r
+    @Test\r
+    public void testDoubleOut1() throws Exception {\r
+        //Default is 6 decimal digits\r
+        assertEquals("0", PDFNumber.doubleOut(0.0f));\r
+        assertEquals("0", PDFNumber.doubleOut(0.0000000000000000000123f));\r
+        assertEquals("0.1", PDFNumber.doubleOut(0.1f));\r
+        assertEquals("100", PDFNumber.doubleOut(100.0f));\r
+        assertEquals("100", PDFNumber.doubleOut(99.99999999999999999999999f));\r
+\r
+        //You'd expect 100.123456 here but DecimalFormat uses the BigDecimal.ROUND_HALF_EVEN\r
+        //strategy. I don't know if that's a problem. The strange thing testDoubleOut2\r
+        //seems to return the normally expected value. Weird.\r
+        assertEquals("100.123459", PDFNumber.doubleOut(100.12345611111111f));\r
+        assertEquals("-100.123459", PDFNumber.doubleOut(-100.12345611111111f));\r
+    }\r
+\r
+    /**\r
+     * Tests PDFNumber.doubleOut().\r
+     * @throws Exception if the test fails\r
+     */\r
+    public void testDoubleOut2() throws Exception {\r
+        //4 decimal digits in this case\r
+        assertEquals("0", PDFNumber.doubleOut(0.0f, 4));\r
+        assertEquals("0", PDFNumber.doubleOut(0.0000000000000000000123f, 4));\r
+        assertEquals("0.1", PDFNumber.doubleOut(0.1f, 4));\r
+        assertEquals("100", PDFNumber.doubleOut(100.0f, 4));\r
+        assertEquals("100", PDFNumber.doubleOut(99.99999999999999999999999f, 4));\r
+        assertEquals("100.1234", PDFNumber.doubleOut(100.12341111111111f, 4));\r
+        assertEquals("-100.1234", PDFNumber.doubleOut(-100.12341111111111f, 4));\r
+    }\r
+\r
+    /**\r
+     * Tests PDFNumber.doubleOut().\r
+     * @throws Exception if the test fails\r
+     */\r
+    public void testDoubleOut3() throws Exception {\r
+        //0 decimal digits in this case\r
+        assertEquals("0", PDFNumber.doubleOut(0.0f, 0));\r
+        assertEquals("0", PDFNumber.doubleOut(0.1f, 0));\r
+        assertEquals("1", PDFNumber.doubleOut(0.6f, 0));\r
+        assertEquals("100", PDFNumber.doubleOut(100.1234f, 0));\r
+        assertEquals("-100", PDFNumber.doubleOut(-100.1234f, 0));\r
+    }\r
+\r
+    /**\r
+     * Tests PDFNumber.doubleOut(). Special cases (former bugs).\r
+     * @throws Exception if the test fails\r
+     */\r
+    public void testDoubleOut4() throws Exception {\r
+        double d = Double.parseDouble("5.7220458984375E-6");\r
+        assertEquals("0.000006", PDFNumber.doubleOut(d));\r
+        assertEquals("0", PDFNumber.doubleOut(d, 4));\r
+        assertEquals("0.00000572", PDFNumber.doubleOut(d, 8));\r
+    }\r
+\r
+    /**\r
+     * Tests PDFNumber.doubleOut(). Tests for wrong parameters.\r
+     * @throws Exception if the test fails\r
+     */\r
+    public void testDoubleOutWrongParameters() throws Exception {\r
+        try {\r
+            PDFNumber.doubleOut(0.1f, -1);\r
+            fail("IllegalArgument expected!");\r
+        } catch (IllegalArgumentException iae) {\r
+            //we want that\r
+        }\r
+        try {\r
+            PDFNumber.doubleOut(0.1f, 17); //We support max 16 decimal digits\r
+            fail("IllegalArgument expected!");\r
+        } catch (IllegalArgumentException iae) {\r
+            //we want that\r
+        }\r
+        try {\r
+            PDFNumber.doubleOut(0.1f, 98274659);\r
+            fail("IllegalArgument expected!");\r
+        } catch (IllegalArgumentException iae) {\r
+            //we want that\r
+        }\r
+        try {\r
+            PDFNumber.doubleOut(null);\r
+            fail("NullPointer expected!");\r
+        } catch (NullPointerException e) {\r
+            // PASS\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Tests both getNumber() and setNumber() - basic getter/setter methods... Why there isn't a\r
+     * constructor is beyond me...\r
+     */\r
+    public void testGetSetNumber() {\r
+        PDFNumber pdfNum = new PDFNumber();\r
+        // Check with a floating point number\r
+        pdfNum.setNumber(1.111f);\r
+        assertEquals(1.111f, pdfNum.getNumber());\r
+        // try with an int\r
+        pdfNum.setNumber(2);\r
+        assertEquals(2, pdfNum.getNumber());\r
+        // See what happens with a null... make sure it doesn't explode\r
+        pdfNum.setNumber(null);\r
+        assertEquals(null, pdfNum.getNumber());\r
+    }\r
+\r
+    /**\r
+     * Tests toPDFString() - this serializes PDFNumber to PDF format.\r
+     * @throws IOException error caused by I/O\r
+     */\r
+    public void testToPDFString() throws IOException {\r
+        PDFNumber testSubject = new PDFNumber();\r
+        testSubject.setNumber(1.0001);\r
+        testOutputStreams("1.0001", testSubject);\r
+        testSubject.setNumber(999);\r
+        testOutputStreams("999", testSubject);\r
+    }\r
+}\r
diff --git a/test/java/org/apache/fop/pdf/PDFNumsArrayTestCase.java b/test/java/org/apache/fop/pdf/PDFNumsArrayTestCase.java
new file mode 100644 (file)
index 0000000..3268fc9
--- /dev/null
@@ -0,0 +1,54 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
+\r
+package org.apache.fop.pdf;\r
+\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+\r
+import java.io.IOException;\r
+\r
+/**\r
+ * Test case for {@link PDFNumsArray}.\r
+ */\r
+public class PDFNumsArrayTestCase extends PDFObjectTestCase {\r
+    private PDFNumsArray numsArray;\r
+    private String expectedString = "[0 /Test#20name 1 10]";\r
+\r
+    @Before\r
+    public void setUp() {\r
+        numsArray = new PDFNumsArray(parent);\r
+        numsArray.put(0, new PDFName("Test name"));\r
+        PDFNumber num = new PDFNumber();\r
+        num.setNumber(10);\r
+        numsArray.put(1, num);\r
+        numsArray.setDocument(doc);\r
+\r
+        pdfObjectUnderTest = numsArray;\r
+    }\r
+\r
+    /**\r
+     * Test output() - ensure that this object is properly outputted to the PDF document.\r
+     * @throws IOException if an I/O error occurs\r
+     */\r
+    @Test\r
+    public void testOutput() throws IOException {\r
+        testOutputStreams(expectedString, numsArray);\r
+    }\r
+}\r
index 2e1f452afefe4bbb4b3b3e3a4f75ff1701b6b9e0..d4ad87d78dc4c93bdcc840b51aad2887066bc241 100644 (file)
 
 package org.apache.fop.pdf;
 
-import static org.junit.Assert.assertEquals;
-
-import java.util.Calendar;
-import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
-
 import org.junit.Test;
+import org.junit.Before;
+import static org.junit.Assert.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 
 /**
  * Tests the PDFObject class.
  */
 public class PDFObjectTestCase {
+    /** The document behind this object */
+    protected final PDFDocument doc = new PDFDocument("test");
+    /** The parent of this object */
+    protected final PDFObject parent = new DummyPDFObject();
+    /** The test subject */
+    protected PDFObject pdfObjectUnderTest;
+    /** The string to begin describing the object <code>"1 0 obj\n"</code> */
+    protected final String beginObj = "1 0 obj\n";
+    /** The string to end describing the object <code>"\nendobj\n"</code> */
+    protected final String endObj = "\nendobj\n";
+
+    private static class DummyPDFObject extends PDFObject {
+
+    };
+
+    @Before
+    public void setUp() {
+        pdfObjectUnderTest = new DummyPDFObject();
+        pdfObjectUnderTest.setDocument(doc);
+        pdfObjectUnderTest.setParent(parent);
+    }
 
     /**
-     * Tests date/time formatting in PDFObject.
-     * @throws Exception if an error occurs
+     * Tests setObjectNumber()
      */
     @Test
-    public void testDateFormatting() throws Exception {
-        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"), Locale.ENGLISH);
-        cal.set(2008, Calendar.FEBRUARY, 07, 15, 11, 07);
-        cal.set(Calendar.MILLISECOND, 0);
-        Date dt = cal.getTime();
-
-        MyPDFObject obj = new MyPDFObject();
-        String s = obj.formatDateTime(dt, TimeZone.getTimeZone("GMT"));
-        assertEquals("D:20080207151107Z", s);
-        s = obj.formatDateTime(dt, TimeZone.getTimeZone("GMT+02:00"));
-        assertEquals("D:20080207171107+02'00'", s);
-        s = obj.formatDateTime(dt, TimeZone.getTimeZone("GMT+02:30"));
-        assertEquals("D:20080207174107+02'30'", s);
-        s = obj.formatDateTime(dt, TimeZone.getTimeZone("GMT-08:00"));
-        assertEquals("D:20080207071107-08'00'", s);
+    public void testSetObjectNumber() {
+        pdfObjectUnderTest.setObjectNumber(1);
+        assertEquals(1, pdfObjectUnderTest.getObjectNumber());
+
+        pdfObjectUnderTest.setObjectNumber(5);
+        assertEquals(5, pdfObjectUnderTest.getObjectNumber());
     }
 
-    private class MyPDFObject extends PDFObject {
+    /**
+     * Tests hasObjectNumber() - returns the object number of the underlying PDF object.
+     */
+    @Test
+    public void testHasObjectNumber() {
+        assertFalse(pdfObjectUnderTest.hasObjectNumber());
 
+        pdfObjectUnderTest.setObjectNumber(1);
+        assertTrue(pdfObjectUnderTest.hasObjectNumber());
+    }
+
+    /**
+     * Tests getGeneration() - returns the generation number of the underlying PDF object.
+     */
+    @Test
+    public void testGetGeneration() {
+        // Default should be 0
+        assertEquals(0, pdfObjectUnderTest.getGeneration());
+        // apparently there is no way to set this to anything other than 0
+    }
+
+    /**
+     * Tests setDocument() - returns the document to which this object is bound.
+     */
+    @Test
+    public void testSetDocument() {
+        assertEquals(doc, pdfObjectUnderTest.getDocument());
+        // assign a different document to the object and test (this should be immutable but isn't)
+        PDFDocument anotherDoc = new PDFDocument("another test");
+        pdfObjectUnderTest.setDocument(anotherDoc);
+        assertEquals(anotherDoc, pdfObjectUnderTest.getDocument());
+    }
+
+    /**
+     * Tests setParent() - assigns the object a parent.
+     */
+    @Test
+    public void testSetParent() {
+        assertEquals(parent, pdfObjectUnderTest.getParent());
+        // assign another parent (this probably shouldn't me mutable)
+        DummyPDFObject anotherParent = new DummyPDFObject();
+        pdfObjectUnderTest.setParent(anotherParent);
+        assertEquals(anotherParent, pdfObjectUnderTest.getParent());
+    }
+
+    /**
+     * Test getObjectID() - returns the PDF object ID.
+     */
+    @Test
+    public void testGetObjectID() {
+        pdfObjectUnderTest.setObjectNumber(10);
+        // String is of the format "<object#> <generation#> obj\n"
+        assertEquals("10 0 obj\n", pdfObjectUnderTest.getObjectID());
+    }
+
+    /**
+     * Test referencePDF() - returns a {@link String} in PDF format to reference this object.
+     */
+    @Test
+    public void testReferencePDF() {
+        try {
+            pdfObjectUnderTest.referencePDF();
+            fail("The object number is not set, an exception should be thrown");
+        } catch (IllegalArgumentException e) {
+            // PASS
+        }
+        pdfObjectUnderTest.setObjectNumber(10);
+        // Referencing this object is in the format "<obj#> <gen#> R"
+        assertEquals("10 0 R", pdfObjectUnderTest.referencePDF());
+    }
+
+    /**
+     * Test makeReference() - returns this object represented as a {@link PDFReference}.
+     */
+    @Test
+    public void testMakeReference() {
+        // Not very intelligent but, there's not much to test here
+        pdfObjectUnderTest.setObjectNumber(10);
+        PDFReference ref = pdfObjectUnderTest.makeReference();
+        assertEquals(pdfObjectUnderTest.getObjectNumber(), ref.getObjectNumber());
+        assertEquals(pdfObjectUnderTest, ref.getObject());
+        assertEquals(pdfObjectUnderTest.referencePDF(), ref.toString());
     }
 
     /**
@@ -78,4 +167,32 @@ public class PDFObjectTestCase {
         assertEquals(ref.toString(), "8 0 R");
     }
 
+    /**
+     * A generic method to test output() for sub-classes of (@link PDFObject}. The expected String
+     * should be formatted such that the object number and object descriptor aren't printed i.e.
+     * for a simple integer object in PDF:
+     * <pre>
+     * 1 0 obj  ** ommited from expectedString
+     * 10
+     * endobj   ** ommited from expectedString
+     * </pre>
+     * Thus the expected string would be "10".
+     * @param expectedString the string that is expected.
+     * @param object the object being tested
+     * @throws IOException error with I/O
+     */
+    protected void testOutputStreams(String expectedString, PDFObject object) throws IOException {
+        // Test both with and without object numbers
+        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+        // Ensure that
+        object.setObjectNumber(0);
+        assertEquals(expectedString.length(), object.output(outStream));
+        assertEquals(expectedString, outStream.toString());
+        outStream.reset();
+        object.setObjectNumber(1);
+        // Test the length of the output string is returned correctly.
+        String string = beginObj + expectedString + endObj;
+        assertEquals(string.length(), object.output(outStream));
+        assertEquals(string, outStream.toString());
+    }
 }
diff --git a/test/java/org/apache/fop/pdf/PDFRectangleTestCase.java b/test/java/org/apache/fop/pdf/PDFRectangleTestCase.java
new file mode 100644 (file)
index 0000000..023a91f
--- /dev/null
@@ -0,0 +1,52 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
+\r
+package org.apache.fop.pdf;\r
+\r
+import static org.junit.Assert.assertEquals;\r
+\r
+import org.junit.Test;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+/**\r
+ * Test case for {@link PDFRectangle}.\r
+ */\r
+public class PDFRectangleTestCase {\r
+\r
+    /**\r
+     * Test outputInline() - ensure properly formatted co-ords are printed to the output stream.\r
+     * @throws IOException if an I/O error occurs\r
+     */\r
+    @Test\r
+    public void testOutputInline() throws IOException {\r
+        OutputStream out = new ByteArrayOutputStream();\r
+        // These are arbitrary values thus have no meaning\r
+        PDFRectangle rect = new PDFRectangle(1, 2, 3, 4);\r
+\r
+        StringBuilder textBuffer = new StringBuilder();\r
+        // Ensure text before the outputInline() is maintained\r
+        textBuffer.append("Test ");\r
+\r
+        rect.outputInline(out, textBuffer);\r
+        assertEquals("Test [1 2 3 4]", textBuffer.toString());\r
+    }\r
+}\r
diff --git a/test/java/org/apache/fop/pdf/PDFReferenceTestCase.java b/test/java/org/apache/fop/pdf/PDFReferenceTestCase.java
new file mode 100644 (file)
index 0000000..1691698
--- /dev/null
@@ -0,0 +1,64 @@
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* $Id$ */\r
+\r
+package org.apache.fop.pdf;\r
+\r
+import static org.junit.Assert.assertEquals;\r
+\r
+import org.junit.Test;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+\r
+/**\r
+ * Test case for {@link PDFReference}.\r
+ */\r
+public class PDFReferenceTestCase {\r
+\r
+    /**\r
+     * Tests outputInline() - ensure that this object is properly formatted when printed to the\r
+     * output stream.\r
+     * @throws IOException if an I/O error occurs\r
+     */\r
+    @Test\r
+    public void testOutputInline() throws IOException {\r
+        PDFName name = new PDFName("Test name");\r
+        name.setObjectNumber(2);\r
+        PDFReference pdfRef = new PDFReference(name);\r
+\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        StringBuilder textBuffer = new StringBuilder();\r
+        // Ensure that text before outputInline() is kept\r
+        textBuffer.append("Text ");\r
+\r
+        pdfRef.outputInline(out, textBuffer);\r
+        assertEquals("Text 2 0 R", textBuffer.toString());\r
+    }\r
+\r
+    /**\r
+     * Tests toString() - since this is used quite a lot, we have to ensure the format is correct.\r
+     */\r
+    @Test\r
+    public void testToString() {\r
+        PDFName name = new PDFName("arbitrary");\r
+        name.setObjectNumber(10);\r
+        PDFReference ref = new PDFReference(name);\r
+        assertEquals("10 0 R", ref.toString());\r
+    }\r
+}\r
diff --git a/test/java/org/apache/fop/util/PDFNumberTestCase.java b/test/java/org/apache/fop/util/PDFNumberTestCase.java
deleted file mode 100644 (file)
index 73eb6fd..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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.util;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-import org.apache.fop.pdf.PDFNumber;
-import org.junit.Test;
-
-/**
- * This test tests PDFNumber's doubleOut() methods.
- */
-public class PDFNumberTestCase {
-
-    /**
-     * Tests PDFNumber.doubleOut().
-     * @throws Exception if the test fails
-     */
-    @Test
-    public void testDoubleOut1() throws Exception {
-        //Default is 6 decimal digits
-        assertEquals("0", PDFNumber.doubleOut(0.0f));
-        assertEquals("0", PDFNumber.doubleOut(0.0000000000000000000123f));
-        assertEquals("0.1", PDFNumber.doubleOut(0.1f));
-        assertEquals("100", PDFNumber.doubleOut(100.0f));
-        assertEquals("100", PDFNumber.doubleOut(99.99999999999999999999999f));
-
-        //You'd expect 100.123456 here but DecimalFormat uses the BigDecimal.ROUND_HALF_EVEN
-        //strategy. I don't know if that's a problem. The strange thing testDoubleOut2
-        //seems to return the normally expected value. Weird.
-        assertEquals("100.123459", PDFNumber.doubleOut(100.12345611111111f));
-        assertEquals("-100.123459", PDFNumber.doubleOut(-100.12345611111111f));
-    }
-
-    /**
-     * Tests PDFNumber.doubleOut().
-     * @throws Exception if the test fails
-     */
-    @Test
-    public void testDoubleOut2() throws Exception {
-        //4 decimal digits in this case
-        assertEquals("0", PDFNumber.doubleOut(0.0f, 4));
-        assertEquals("0", PDFNumber.doubleOut(0.0000000000000000000123f, 4));
-        assertEquals("0.1", PDFNumber.doubleOut(0.1f, 4));
-        assertEquals("100", PDFNumber.doubleOut(100.0f, 4));
-        assertEquals("100", PDFNumber.doubleOut(99.99999999999999999999999f, 4));
-        assertEquals("100.1234", PDFNumber.doubleOut(100.12341111111111f, 4));
-        assertEquals("-100.1234", PDFNumber.doubleOut(-100.12341111111111f, 4));
-    }
-
-    /**
-     * Tests PDFNumber.doubleOut().
-     * @throws Exception if the test fails
-     */
-    @Test
-    public void testDoubleOut3() throws Exception {
-        //0 decimal digits in this case
-        assertEquals("0", PDFNumber.doubleOut(0.0f, 0));
-        assertEquals("0", PDFNumber.doubleOut(0.1f, 0));
-        assertEquals("1", PDFNumber.doubleOut(0.6f, 0));
-        assertEquals("100", PDFNumber.doubleOut(100.1234f, 0));
-        assertEquals("-100", PDFNumber.doubleOut(-100.1234f, 0));
-    }
-
-    /**
-     * Tests PDFNumber.doubleOut(). Special cases (former bugs).
-     * @throws Exception if the test fails
-     */
-    @Test
-    public void testDoubleOut4() throws Exception {
-        double d = Double.parseDouble("5.7220458984375E-6");
-        assertEquals("0.000006", PDFNumber.doubleOut(d));
-        assertEquals("0", PDFNumber.doubleOut(d, 4));
-        assertEquals("0.00000572", PDFNumber.doubleOut(d, 8));
-    }
-
-    /**
-     * Tests PDFNumber.doubleOut(). Tests for wrong parameters.
-     * @throws Exception if the test fails
-     */
-    @Test
-    public void testDoubleOutWrongParameters() throws Exception {
-        try {
-            PDFNumber.doubleOut(0.1f, -1);
-            fail("IllegalArgument expected!");
-        } catch (IllegalArgumentException iae) {
-            //we want that
-        }
-        try {
-            PDFNumber.doubleOut(0.1f, 17); //We support max 16 decimal digits
-            fail("IllegalArgument expected!");
-        } catch (IllegalArgumentException iae) {
-            //we want that
-        }
-        try {
-            PDFNumber.doubleOut(0.1f, 98274659);
-            fail("IllegalArgument expected!");
-        } catch (IllegalArgumentException iae) {
-            //we want that
-        }
-    }
-
-}