From 0cc4cbb9f06fd9c1d8549fb17f501d24bd7b5976 Mon Sep 17 00:00:00 2001 From: Chris Bowditch Date: Fri, 6 Jan 2012 16:03:44 +0000 Subject: [PATCH] Bugzilla #51644: 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 --- .../org/apache/fop/pdf/AbstractPDFStream.java | 19 +- src/java/org/apache/fop/pdf/PDFArray.java | 24 +- .../org/apache/fop/pdf/PDFDestination.java | 15 +- .../org/apache/fop/pdf/PDFDictionary.java | 45 ++- src/java/org/apache/fop/pdf/PDFDocument.java | 24 +- .../org/apache/fop/pdf/PDFEmbeddedFiles.java | 5 +- src/java/org/apache/fop/pdf/PDFName.java | 25 +- src/java/org/apache/fop/pdf/PDFNull.java | 6 +- src/java/org/apache/fop/pdf/PDFNumsArray.java | 33 +- src/java/org/apache/fop/pdf/PDFObject.java | 49 +-- src/java/org/apache/fop/pdf/PDFRectangle.java | 17 +- src/java/org/apache/fop/pdf/PDFReference.java | 17 +- src/java/org/apache/fop/pdf/PDFWritable.java | 14 +- status.xml | 3 + .../org/apache/fop/StandardTestSuite.java | 2 + .../org/apache/fop/UtilityCodeTestSuite.java | 4 - .../fop/pdf/AbstractPDFStreamTestCase.java | 92 ++++++ .../org/apache/fop/pdf/PDFArrayTestCase.java | 237 +++++++++++++++ .../org/apache/fop/pdf/PDFDestsTestCase.java | 64 ++++ .../apache/fop/pdf/PDFDictionaryTestCase.java | 135 +++++++++ .../apache/fop/pdf/PDFDocumentTestCase.java | 62 ++++ .../apache/fop/pdf/PDFLibraryTestSuite.java | 54 ++++ .../org/apache/fop/pdf/PDFNameTestCase.java | 169 +++++++++++ .../org/apache/fop/pdf/PDFNullTestCase.java | 49 +++ .../fop/{util => pdf}/PDFNumberTestCase.java | 283 ++++++++++-------- .../apache/fop/pdf/PDFNumsArrayTestCase.java | 54 ++++ .../org/apache/fop/pdf/PDFObjectTestCase.java | 167 +++++++++-- .../apache/fop/pdf/PDFRectangleTestCase.java | 52 ++++ .../apache/fop/pdf/PDFReferenceTestCase.java | 64 ++++ 29 files changed, 1495 insertions(+), 289 deletions(-) create mode 100644 test/java/org/apache/fop/pdf/AbstractPDFStreamTestCase.java create mode 100644 test/java/org/apache/fop/pdf/PDFArrayTestCase.java create mode 100644 test/java/org/apache/fop/pdf/PDFDestsTestCase.java create mode 100644 test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java create mode 100644 test/java/org/apache/fop/pdf/PDFDocumentTestCase.java create mode 100644 test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java create mode 100644 test/java/org/apache/fop/pdf/PDFNameTestCase.java create mode 100644 test/java/org/apache/fop/pdf/PDFNullTestCase.java rename test/java/org/apache/fop/{util => pdf}/PDFNumberTestCase.java (70%) create mode 100644 test/java/org/apache/fop/pdf/PDFNumsArrayTestCase.java create mode 100644 test/java/org/apache/fop/pdf/PDFRectangleTestCase.java create mode 100644 test/java/org/apache/fop/pdf/PDFReferenceTestCase.java diff --git a/src/java/org/apache/fop/pdf/AbstractPDFStream.java b/src/java/org/apache/fop/pdf/AbstractPDFStream.java index 91605b293..52262252f 100644 --- a/src/java/org/apache/fop/pdf/AbstractPDFStream.java +++ b/src/java/org/apache/fop/pdf/AbstractPDFStream.java @@ -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(); } diff --git a/src/java/org/apache/fop/pdf/PDFArray.java b/src/java/org/apache/fop/pdf/PDFArray.java index a79e2704f..78eba3bb9 100644 --- a/src/java/org/apache/fop/pdf/PDFArray.java +++ b/src/java/org/apache/fop/pdf/PDFArray.java @@ -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 values = new java.util.ArrayList(); /** * 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 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(); } diff --git a/src/java/org/apache/fop/pdf/PDFDestination.java b/src/java/org/apache/fop/pdf/PDFDestination.java index ced3ac497..400c4a4b6 100644 --- a/src/java/org/apache/fop/pdf/PDFDestination.java +++ b/src/java/org/apache/fop/pdf/PDFDestination.java @@ -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(); } diff --git a/src/java/org/apache/fop/pdf/PDFDictionary.java b/src/java/org/apache/fop/pdf/PDFDictionary.java index 932f2d03e..5a6724304 100644 --- a/src/java/org/apache/fop/pdf/PDFDictionary.java +++ b/src/java/org/apache/fop/pdf/PDFDictionary.java @@ -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 entries = new java.util.HashMap(); /** * 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 order = new java.util.ArrayList(); /** * 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"); } } diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java index cbca3ea8f..543d1a45f 100644 --- a/src/java/org/apache/fop/pdf/PDFDocument.java +++ b/src/java/org/apache/fop/pdf/PDFDocument.java @@ -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 flush() 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); } /** diff --git a/src/java/org/apache/fop/pdf/PDFEmbeddedFiles.java b/src/java/org/apache/fop/pdf/PDFEmbeddedFiles.java index 8e89f5ada..c4979312e 100644 --- a/src/java/org/apache/fop/pdf/PDFEmbeddedFiles.java +++ b/src/java/org/apache/fop/pdf/PDFEmbeddedFiles.java @@ -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() { diff --git a/src/java/org/apache/fop/pdf/PDFName.java b/src/java/org/apache/fop/pdf/PDFName.java index e40a12f13..7fa6842fa 100644 --- a/src/java/org/apache/fop/pdf/PDFName.java +++ b/src/java/org/apache/fop/pdf/PDFName.java @@ -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()); } } diff --git a/src/java/org/apache/fop/pdf/PDFNull.java b/src/java/org/apache/fop/pdf/PDFNull.java index 7e11a1763..43ba2dd82 100644 --- a/src/java/org/apache/fop/pdf/PDFNull.java +++ b/src/java/org/apache/fop/pdf/PDFNull.java @@ -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()); } } diff --git a/src/java/org/apache/fop/pdf/PDFNumsArray.java b/src/java/org/apache/fop/pdf/PDFNumsArray.java index 6db7b02ac..ecd301647 100644 --- a/src/java/org/apache/fop/pdf/PDFNumsArray.java +++ b/src/java/org/apache/fop/pdf/PDFNumsArray.java @@ -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 map = new java.util.TreeMap(); /** * 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 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(); } diff --git a/src/java/org/apache/fop/pdf/PDFObject.java b/src/java/org/apache/fop/pdf/PDFObject.java index 6dd0c800f..d74133222 100644 --- a/src/java/org/apache/fop/pdf/PDFObject.java +++ b/src/java/org/apache/fop/pdf/PDFObject.java @@ -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. + *

+ * 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 flush 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 diff --git a/src/java/org/apache/fop/pdf/PDFRectangle.java b/src/java/org/apache/fop/pdf/PDFRectangle.java index ce5b46440..30f218824 100644 --- a/src/java/org/apache/fop/pdf/PDFRectangle.java +++ b/src/java/org/apache/fop/pdf/PDFRectangle.java @@ -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); } } diff --git a/src/java/org/apache/fop/pdf/PDFReference.java b/src/java/org/apache/fop/pdf/PDFReference.java index 1d9ddc349..29f8c7c48 100644 --- a/src/java/org/apache/fop/pdf/PDFReference.java +++ b/src/java/org/apache/fop/pdf/PDFReference.java @@ -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 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(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"); } } diff --git a/src/java/org/apache/fop/pdf/PDFWritable.java b/src/java/org/apache/fop/pdf/PDFWritable.java index 6e71dc7d8..67f10ff75 100644 --- a/src/java/org/apache/fop/pdf/PDFWritable.java +++ b/src/java/org/apache/fop/pdf/PDFWritable.java @@ -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 - * flush() is called before any direct calls to out are made. + * Writes a "direct object" (inline object) representation to the stream. A text buffer is given + * for optimized encoding of text content. + *

+ * 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; } diff --git a/status.xml b/status.xml index 9c47a3dc3..5006a20cc 100644 --- a/status.xml +++ b/status.xml @@ -61,6 +61,9 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + Tagged PDF performance improvement plus tests + Improved AdobeStandardEncoding support in AFM files for type1 fonts diff --git a/test/java/org/apache/fop/StandardTestSuite.java b/test/java/org/apache/fop/StandardTestSuite.java index 548a2e4aa..bb59c8605 100644 --- a/test/java/org/apache/fop/StandardTestSuite.java +++ b/test/java/org/apache/fop/StandardTestSuite.java @@ -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, diff --git a/test/java/org/apache/fop/UtilityCodeTestSuite.java b/test/java/org/apache/fop/UtilityCodeTestSuite.java index 442d63f5b..cf6b8875d 100644 --- a/test/java/org/apache/fop/UtilityCodeTestSuite.java +++ b/test/java/org/apache/fop/UtilityCodeTestSuite.java @@ -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 index 000000000..59b3452ec --- /dev/null +++ b/test/java/org/apache/fop/pdf/AbstractPDFStreamTestCase.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Test; + +/** + * Test case for {@link AbstractPDFStream}. + */ +public class AbstractPDFStreamTestCase extends PDFObjectTestCase { + + private AbstractPDFStream abstractStream; + + private String textData = "This is an arbitrary string for testing."; + + private static byte[] encodedBytes; + static { + int[] encoded = { 0x78, 0x9c, 0x0b, 0xc9, 0xc8, 0x2c, 0x56, 0x00, 0xa2, 0xc4, 0x3c, 0x85, + 0xc4, 0xa2, 0xa4, 0xcc, 0x92, 0xa2, 0xc4, 0xa2, 0x4a, 0x85, 0xe2, 0x92, 0xa2, 0xcc, + 0xbc, 0x74, 0x85, 0xb4, 0xfc, 0x22, 0x85, 0x92, 0xd4, 0xe2, 0x12, 0x20, 0x5b, 0x0f, + 0x00, 0x2d, 0x2b, 0x0e, 0xde, 0x0a }; + encodedBytes = new byte[encoded.length]; + int i = 0; + for (int in : encoded) { + encodedBytes[i++] = (byte) (in & 0xff); + } + } + private String startStream = "1 0 obj\n" + + "<< /Length 5 0 R /Filter /FlateDecode >>\n" + + "stream\n"; + private String endStream = "endstream\nendobj\n"; + + @Before + public void setUp() { + abstractStream = new AbstractPDFStream() { + + @Override + protected void outputRawStreamData(OutputStream out) throws IOException { + out.write(textData.getBytes()); + } + + @Override + protected int getSizeHint() throws IOException { + return textData.length(); + } + }; + abstractStream.setDocument(doc); + abstractStream.setParent(parent); + + pdfObjectUnderTest = abstractStream; + } + + /** + * Tests output() - ensure that this object is correctly formatted to the output stream. + * @throws IOException if an I/O error occurs + */ + @Test + public void testOutput() throws IOException { + // This differs from most other objects, if the object number = 0 an exception is thrown + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + abstractStream.setObjectNumber(1); + ByteArrayOutputStream expectedStream = new ByteArrayOutputStream(); + expectedStream.write(startStream.getBytes()); + expectedStream.write(encodedBytes); + expectedStream.write(endStream.getBytes()); + assertEquals(expectedStream.size(), abstractStream.output(outStream)); + assertEquals(expectedStream.toString(), outStream.toString()); + } +} diff --git a/test/java/org/apache/fop/pdf/PDFArrayTestCase.java b/test/java/org/apache/fop/pdf/PDFArrayTestCase.java new file mode 100644 index 000000000..73fda56d1 --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFArrayTestCase.java @@ -0,0 +1,237 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Test case for {@link PDFArray}. + */ +public class PDFArrayTestCase extends PDFObjectTestCase { + private PDFArray intArray; + private String intArrayOutput; + private PDFArray doubleArray; + private String doubleArrayOutput; + private PDFArray collectionArray; + private String collectionArrayOutput; + private PDFArray objArray; + private String objArrayOutput; + + /** A PDF object used solely for testing */ + private PDFNumber num; + + @Before + public void setUp() { + intArray = new PDFArray(parent, new int[] {1, 2, 3, 4, 5}); + intArrayOutput = "[1 2 3 4 5]"; + + doubleArray = new PDFArray(parent, new double[] {1.1, 2.2, 3.3, 4.4, 5.5}); + doubleArrayOutput = "[1.1 2.2 3.3 4.4 5.5]"; + + List strList = new ArrayList(); + strList.add("one"); + strList.add("two"); + strList.add("three"); + collectionArray = new PDFArray(parent, strList); + collectionArrayOutput = "[(one) (two) (three)]"; + + // Set arbitrary values here + num = new PDFNumber(); + num.setNumber(20); + num.setObjectNumber(4); + objArray = new PDFArray(parent, new Object[] {"one", 2, 3.0f, num}); + objArrayOutput = "[(one) 2 3 4 0 R]"; + + // set the document + intArray.setDocument(doc); + doubleArray.setDocument(doc); + collectionArray.setDocument(doc); + objArray.setDocument(doc); + + // Test the progenitor in the inheritance stack + objArray.setParent(parent); + pdfObjectUnderTest = objArray; + } + + private void intArrayContainsTests() { + for (int i = 1; i <= 5; i++) { + assertTrue(intArray.contains(i)); + } + assertFalse(intArray.contains(6)); + assertFalse(intArray.contains(0)); + } + + private void doubleArrayContainsTests() { + assertTrue(doubleArray.contains(1.1)); + assertTrue(doubleArray.contains(2.2)); + assertTrue(doubleArray.contains(3.3)); + assertTrue(doubleArray.contains(4.4)); + assertTrue(doubleArray.contains(5.5)); + assertFalse(doubleArray.contains(10.0)); + assertFalse(doubleArray.contains(0.0)); + } + + private void collectionArrayContainsTests() { + assertTrue(collectionArray.contains("one")); + assertTrue(collectionArray.contains("two")); + assertTrue(collectionArray.contains("three")); + assertFalse(collectionArray.contains("zero")); + assertFalse(collectionArray.contains("four")); + } + + private void objectArrayContainsTests() { + assertTrue(objArray.contains("one")); + assertTrue(objArray.contains(2)); + assertTrue(objArray.contains(3.0f)); + assertTrue(objArray.contains(num)); + assertFalse(objArray.contains("four")); + assertFalse(objArray.contains(0.0)); + } + + /** + * Test contains() - test whether this PDFArray contains an object. + */ + @Test + public void testContains() { + // Test some arbitrary values + intArrayContainsTests(); + doubleArrayContainsTests(); + collectionArrayContainsTests(); + objectArrayContainsTests(); + } + + /** + * Test length() - tests the length of an array. + */ + @Test + public void testLength() { + assertEquals(5, intArray.length()); + assertEquals(5, doubleArray.length()); + assertEquals(3, collectionArray.length()); + assertEquals(4, objArray.length()); + + // Test the count is incremented when an object is added (this only + // needs to be tested once) + intArray.add(6); + assertEquals(6, intArray.length()); + } + + /** + * Test set() - tests that a particular point has been properly set. + */ + @Test + public void testSet() { + PDFName name = new PDFName("zero test"); + objArray.set(0, name); + assertEquals(name, objArray.get(0)); + + objArray.set(1, "test"); + assertEquals("test", objArray.get(1)); + // This goes through the set(int, double) code path rather than set(int, Object) + objArray.set(2, 5); + assertEquals(5.0, objArray.get(2)); + try { + objArray.set(4, 2); + fail("out of bounds"); + } catch (IndexOutOfBoundsException e) { + // Pass + } + } + + /** + * Test get() - gets the object stored at a given index. + */ + @Test + public void testGet() { + // Test some arbitrary values + for (int i = 1; i <= 5; i++) { + assertEquals(i, intArray.get(i - 1)); + } + + assertEquals(1.1, doubleArray.get(0)); + assertEquals(2.2, doubleArray.get(1)); + assertEquals(3.3, doubleArray.get(2)); + assertEquals(4.4, doubleArray.get(3)); + assertEquals(5.5, doubleArray.get(4)); + + assertEquals("one", collectionArray.get(0)); + assertEquals("two", collectionArray.get(1)); + assertEquals("three", collectionArray.get(2)); + + assertEquals("one", objArray.get(0)); + assertEquals(2, objArray.get(1)); + assertEquals(0, Double.compare(3.0, (Float) objArray.get(2))); + assertEquals(num, objArray.get(3)); + } + + /** + * Tests add() - tests that objects are appended to the end of the array as expected. + */ + @Test + public void testAdd() { + intArray.add(new Integer(6)); + doubleArray.add(6.6); + // Test some arbitrary values + for (int i = 1; i <= 6; i++) { + assertEquals(i, intArray.get(i - 1)); + } + + assertEquals(1.1, doubleArray.get(0)); + assertEquals(2.2, doubleArray.get(1)); + assertEquals(3.3, doubleArray.get(2)); + assertEquals(4.4, doubleArray.get(3)); + assertEquals(5.5, doubleArray.get(4)); + assertEquals(6.6, doubleArray.get(5)); + + collectionArray.add(1); + assertEquals("one", collectionArray.get(0)); + assertEquals("two", collectionArray.get(1)); + assertEquals("three", collectionArray.get(2)); + assertEquals(1.0, collectionArray.get(3)); + + objArray.add("four"); + assertEquals("one", objArray.get(0)); + assertEquals(2, objArray.get(1)); + assertEquals(0, Double.compare(3.0, (Float) objArray.get(2))); + assertEquals("four", objArray.get(4)); + } + + /** + * Tests output() - tests that this object is properly streamed to the PDF document. + * @throws IOException error caused by I/O + */ + @Test + public void testOutput() throws IOException { + testOutputStreams(intArrayOutput, intArray); + testOutputStreams(doubleArrayOutput, doubleArray); + testOutputStreams(collectionArrayOutput, collectionArray); + testOutputStreams(objArrayOutput, objArray); + } +} diff --git a/test/java/org/apache/fop/pdf/PDFDestsTestCase.java b/test/java/org/apache/fop/pdf/PDFDestsTestCase.java new file mode 100644 index 000000000..be2dfe4e5 --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFDestsTestCase.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Test case for {@link PDFDests}. + */ +public class PDFDestsTestCase extends PDFObjectTestCase { + + private PDFDests dests = new PDFDests(); + private String expectedString = "<< /Names [(number) 10 (name) /Test#20name] >>\n"; + + @Before + public void setUp() { + List destinations = new ArrayList(); + PDFNumber number = new PDFNumber(); + number.setNumber(10); + PDFDestination testNumber = new PDFDestination("number", number); + testNumber.setDocument(doc); + destinations.add(testNumber); + PDFDestination testName = new PDFDestination("name", new PDFName("Test name")); + testName.setDocument(doc); + destinations.add(testName); + + dests = new PDFDests(destinations); + dests.setDocument(doc); + dests.setParent(parent); + pdfObjectUnderTest = dests; + } + + /** + * Populate the object with some arbitrary values and ensure they are wrapped properly. + * @throws IOException if an I/O error occurs + */ + @Test + public void testConstructor() throws IOException { + // Seems the only way to test this is by testing the output + testOutputStreams(expectedString, dests); + } +} diff --git a/test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java b/test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java new file mode 100644 index 000000000..0ef5171aa --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFDictionaryTestCase.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.apache.commons.io.output.CountingOutputStream; +import org.junit.Before; +import org.junit.Test; + + +/** + * Test case for {@link PDFDictionary}. + */ +public class PDFDictionaryTestCase extends PDFObjectTestCase { + /** The test subject */ + private PDFDictionary pdfDictUnderTest; + private PDFArray testArray; + private PDFNumber testNumber; + /** The order in which these objects are put into the dictionary MUST be maintained. */ + private String expectedOutput = "<<\n" + + " /String (TestValue)\n" + + " /int 10\n" + + " /double 3.1\n" + + " /array [1 (two) 20]\n" + + " /number 20\n" + + " /null null\n" + + ">>\n"; + + @Before + public void setUp() { + // A PDFNumber for testing, this DOES have a parent + testNumber = new PDFNumber(); + testNumber.setParent(parent); + testNumber.setNumber(20); + // An array for testing, this DOES NOT have a parent + testArray = new PDFArray(); + testArray.add(1); + testArray.add("two"); + testArray.add(testNumber); + // Populating the dictionary with a parent, document and the various objects + pdfDictUnderTest = new PDFDictionary(parent); + pdfDictUnderTest.setDocument(doc); + pdfDictUnderTest.put("String", "TestValue"); + pdfDictUnderTest.put("int", 10); + pdfDictUnderTest.put("double", Double.valueOf(3.1)); + pdfDictUnderTest.put("array", testArray); + pdfDictUnderTest.put("number", testNumber); + // null is a valid PDF object + pdfDictUnderTest.put("null", null); + // test that the interface is maintained + pdfObjectUnderTest = pdfDictUnderTest; + } + + /** + * Tests put() - tests that the object is put into the dictionary and it is handled if it is a + * {@link PDFObject}. + */ + @Test + public void testPut() { + // The "put()" commands have already been done in setUp(), so just test them. + assertEquals("TestValue", pdfDictUnderTest.get("String")); + assertEquals(10, pdfDictUnderTest.get("int")); + assertEquals(3.1, pdfDictUnderTest.get("double")); + // With PDFObjects, if they DO NOT have a parent, the dict becomes their parent. + assertEquals(testArray, pdfDictUnderTest.get("array")); + assertEquals(pdfDictUnderTest, testArray.getParent()); + // With PDFObjects, if they DO have a parent, the dict DOES NOT change the parent object. + assertEquals(testNumber, pdfDictUnderTest.get("number")); + // Test it doesn't explode when we try to get a non-existent entry + assertNull(pdfDictUnderTest.get("Not in dictionary")); + // Tests that we can over-write objects + pdfDictUnderTest.put("array", 10); + assertEquals(10, pdfDictUnderTest.get("array")); + // Test that nulls are handled appropriately + assertNull(pdfDictUnderTest.get("null")); + } + + /** + * Tests get() - tests that objects can be properly retrieved from the dictionary. + */ + @Test + public void testGet() { + // Tested fairly comprehensively in testPut(). + } + + /** + * Tests writeDictionary() - tests that the dictionary is properly written to the output-stream. + */ + @Test + public void testWriteDictionary() { + // Ensure that the objects stored in the dictionary are streamed in the correct format. + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + CountingOutputStream cout = new CountingOutputStream(outStream); + StringBuilder textBuffer = new StringBuilder(); + try { + pdfDictUnderTest.writeDictionary(cout, textBuffer); + PDFDocument.flushTextBuffer(textBuffer, cout); + assertEquals(expectedOutput, outStream.toString()); + } catch (IOException e) { + fail("IOException: " + e.getMessage()); + } + } + + /** + * Tests output() - test that this object can write itself to an output stream. + * @throws IOException error caused by I/O + */ + @Test + public void testOutput() throws IOException { + testOutputStreams(expectedOutput, pdfDictUnderTest); + } +} diff --git a/test/java/org/apache/fop/pdf/PDFDocumentTestCase.java b/test/java/org/apache/fop/pdf/PDFDocumentTestCase.java new file mode 100644 index 000000000..dc3a05e2f --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFDocumentTestCase.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Test case for {@link PDFDocument} + */ +public class PDFDocumentTestCase { + + /** + * Test flushTextBuffer() - ensure that the text given will stream to the PDF document as + * expected. + * @throws IOException when an I/O error occurs + */ + @Test + public void testFlushTextBuffer() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StringBuilder textBuffer = new StringBuilder(); + String testString = "This is a test string, just some arbitrary data."; + textBuffer.append(testString); + + PDFDocument.flushTextBuffer(textBuffer, out); + assertEquals(testString, out.toString()); + + // Should reset the textBuffer + assertEquals(0, textBuffer.length()); + assertEquals("", textBuffer.toString()); + out.reset(); + + String[] strArray = { "Try ", "with ", "multiple ", "strings." }; + for (String str : strArray) { + textBuffer.append(str); + } + String fullString = textBuffer.toString(); + PDFDocument.flushTextBuffer(textBuffer, out); + assertEquals(fullString, out.toString()); + } +} diff --git a/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java b/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java new file mode 100644 index 000000000..0332ec768 --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import junit.framework.Test; +import junit.framework.TestSuite; + + +/** + * Test suite for FOP's utility classes. + */ +public class PDFLibraryTestSuite { + + /** + * Builds the test suite + * @return the test suite + */ + public static Test suite() { + TestSuite suite = new TestSuite( + "Test suite for FOP's utility classes"); + //$JUnit-BEGIN$ + suite.addTest(new TestSuite(PDFArrayTestCase.class)); + suite.addTest(new TestSuite(PDFDictionaryTestCase.class)); + suite.addTest(new TestSuite(PDFNumberTestCase.class)); + suite.addTest(new TestSuite(PDFObjectTestCase.class)); + suite.addTest(new TestSuite(PDFNameTestCase.class)); + suite.addTest(new TestSuite(AbstractPDFStreamTestCase.class)); + suite.addTest(new TestSuite(PDFDestsTestCase.class)); + suite.addTest(new TestSuite(PDFDocumentTestCase.class)); + suite.addTest(new TestSuite(PDFNullTestCase.class)); + suite.addTest(new TestSuite(PDFNumsArrayTestCase.class)); + suite.addTest(new TestSuite(PDFRectangleTestCase.class)); + suite.addTest(new TestSuite(PDFReferenceTestCase.class)); + //$JUnit-END$ + return suite; + } +} diff --git a/test/java/org/apache/fop/pdf/PDFNameTestCase.java b/test/java/org/apache/fop/pdf/PDFNameTestCase.java new file mode 100644 index 000000000..189f2e61f --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFNameTestCase.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.apache.commons.io.output.CountingOutputStream; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.fail; +import static org.junit.Assert.assertEquals; + +/** + * Test class for {@link PDFName}. + */ +public class PDFNameTestCase extends PDFObjectTestCase { + private PDFName pdfName; + + /** + * Sets up the local variables + */ + @Before + public void setUp() { + pdfName = new PDFName("TestName"); + pdfName.setParent(parent); + pdfName.setDocument(doc); + + pdfObjectUnderTest = pdfName; + } + + /** + * Tests escapeName() - tests that this method escapes the necessary characters. + */ + @Test + public void testEscapeName() { + try { + // Test for null, this is a programming error thus the NPE + PDFName.escapeName(null); + fail("NPE not thrown when null object given to escapeName()"); + } catch (NullPointerException e) { + // PASS + } + // All names are prefixed by "/", check the PDF spec for further details. + assertEquals("/Test", PDFName.escapeName("Test")); + // Check that if the name is already prefixed with "/" it doens't do it twice + assertEquals("/Test", PDFName.escapeName("/Test")); + // Test with a space in the middle + assertEquals("/Test#20test", PDFName.escapeName("Test test")); + // Test that all chars apart from ASCII '!' --> '~' are escaped + nonEscapedCharactersTests(); + escapedCharactersTests(); + } + + private void escapedCharactersTests() { + for (char i = 0; i < '!'; i++) { + String str = Integer.toHexString(i >>> 4 & 0x0f).toUpperCase(); + str += Integer.toHexString(i & 0x0f).toUpperCase(); + assertEquals("/#" + str, PDFName.escapeName(String.valueOf(i))); + } + for (char i = '~' + 1; i < 256; i++) { + String str = Integer.toHexString(i >>> 4 & 0x0f).toUpperCase(); + str += Integer.toHexString(i & 0x0f).toUpperCase(); + assertEquals("/#" + str, PDFName.escapeName(String.valueOf(i))); + } + checkCharacterIsEscaped('#'); + checkCharacterIsEscaped('%'); + checkCharacterIsEscaped('('); + checkCharacterIsEscaped(')'); + checkCharacterIsEscaped('<'); + checkCharacterIsEscaped('>'); + checkCharacterIsEscaped('['); + checkCharacterIsEscaped(']'); + checkCharacterIsEscaped('>'); + } + + private void checkCharacterIsEscaped(char c) { + String str = Integer.toHexString(c >>> 4 & 0x0f).toUpperCase(); + str += Integer.toHexString(c & 0x0f).toUpperCase(); + assertEquals("/#" + str, PDFName.escapeName(String.valueOf(c))); + } + + private void nonEscapedCharactersTests() { + charactersNotEscapedBetween('!', '"'); + charactersNotEscapedBetween('*', ';'); + charactersNotEscapedBetween('?', 'Z'); + charactersNotEscapedBetween('^', '~'); + } + + private void charactersNotEscapedBetween(char c1, char c2) { + for (char i = c1; i <= c2; i++) { + String str = String.valueOf(i); + String expected = !str.equals("/") ? "/" + str : str; + assertEquals(expected, PDFName.escapeName(str)); + } + } + + /** + * Tests toString() - this has been overridden to return the String that PDFName wraps. + */ + @Test + public void testToString() { + // The escape characters have already been tested in testEscapeName() so this doesn't need + // to be done twice. + PDFName test1 = new PDFName("test1"); + assertEquals("/test1", test1.toString()); + PDFName test2 = new PDFName("another test"); + assertEquals("/another#20test", test2.toString()); + try { + new PDFName(null); + fail("NPE not thrown when null passed to constructor"); + } catch (NullPointerException e) { + // PASS + } + } + + /** + * Tests output() - check that this object can stream itself in the correct format. + * @throws IOException error caused by I/O + */ + @Test + public void testOutput() throws IOException { + testOutputStreams("/TestName", pdfName); + testOutputStreams("/test#20test", new PDFName("test test")); + } + + /** + * Test outputInline() - this writes the object reference if it is a direct object (has an + * object number), or writes the String representation if there is no object number. + */ + @Test + public void testOutputInline() { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + CountingOutputStream cout = new CountingOutputStream(outStream); + StringBuilder textBuffer = new StringBuilder(); + try { + // test with no object number set. + pdfName.outputInline(outStream, textBuffer); + PDFDocument.flushTextBuffer(textBuffer, cout); + assertEquals("/TestName", outStream.toString()); + + outStream.reset(); + // test with object number set + pdfName.setObjectNumber(1); + pdfName.outputInline(outStream, textBuffer); + PDFDocument.flushTextBuffer(textBuffer, cout); + assertEquals("1 0 R", outStream.toString()); + } catch (IOException e) { + fail("IOException: " + e.getMessage()); + } + } +} diff --git a/test/java/org/apache/fop/pdf/PDFNullTestCase.java b/test/java/org/apache/fop/pdf/PDFNullTestCase.java new file mode 100644 index 000000000..51d3de4ea --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFNullTestCase.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Test case for {@link PDFNull}. + */ +public class PDFNullTestCase extends PDFObjectTestCase { + + /** + * Test outputInline() - test that "null" is printed to the output stream. + */ + @Test + public void testOutputInline() throws IOException { + PDFNull obj = PDFNull.INSTANCE; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StringBuilder text = new StringBuilder(); + obj.outputInline(out, text); + assertEquals("null", text.toString()); + + // Ensure previously written text is not discarded + obj.outputInline(out, text); + assertEquals("nullnull", text.toString()); + } +} diff --git a/test/java/org/apache/fop/util/PDFNumberTestCase.java b/test/java/org/apache/fop/pdf/PDFNumberTestCase.java similarity index 70% rename from test/java/org/apache/fop/util/PDFNumberTestCase.java rename to test/java/org/apache/fop/pdf/PDFNumberTestCase.java index 73eb6fd77..61d7b0297 100644 --- a/test/java/org/apache/fop/util/PDFNumberTestCase.java +++ b/test/java/org/apache/fop/pdf/PDFNumberTestCase.java @@ -1,121 +1,162 @@ -/* - * 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 - } - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +/** + * This test tests PDFNumber's doubleOut() methods. + */ +public class PDFNumberTestCase extends PDFObjectTestCase { + /** + * Sets up the local variables, most of these are inherited from PDFObjectTestCase + */ + @Before + public void setUp() { + pdfObjectUnderTest = new PDFNumber(); + pdfObjectUnderTest.setParent(parent); + pdfObjectUnderTest.setDocument(doc); + } + + /** + * 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 + */ + 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 + */ + 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 + */ + 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 + */ + 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 + } + try { + PDFNumber.doubleOut(null); + fail("NullPointer expected!"); + } catch (NullPointerException e) { + // PASS + } + } + + /** + * Tests both getNumber() and setNumber() - basic getter/setter methods... Why there isn't a + * constructor is beyond me... + */ + public void testGetSetNumber() { + PDFNumber pdfNum = new PDFNumber(); + // Check with a floating point number + pdfNum.setNumber(1.111f); + assertEquals(1.111f, pdfNum.getNumber()); + // try with an int + pdfNum.setNumber(2); + assertEquals(2, pdfNum.getNumber()); + // See what happens with a null... make sure it doesn't explode + pdfNum.setNumber(null); + assertEquals(null, pdfNum.getNumber()); + } + + /** + * Tests toPDFString() - this serializes PDFNumber to PDF format. + * @throws IOException error caused by I/O + */ + public void testToPDFString() throws IOException { + PDFNumber testSubject = new PDFNumber(); + testSubject.setNumber(1.0001); + testOutputStreams("1.0001", testSubject); + testSubject.setNumber(999); + testOutputStreams("999", testSubject); + } +} diff --git a/test/java/org/apache/fop/pdf/PDFNumsArrayTestCase.java b/test/java/org/apache/fop/pdf/PDFNumsArrayTestCase.java new file mode 100644 index 000000000..3268fc977 --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFNumsArrayTestCase.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +/** + * Test case for {@link PDFNumsArray}. + */ +public class PDFNumsArrayTestCase extends PDFObjectTestCase { + private PDFNumsArray numsArray; + private String expectedString = "[0 /Test#20name 1 10]"; + + @Before + public void setUp() { + numsArray = new PDFNumsArray(parent); + numsArray.put(0, new PDFName("Test name")); + PDFNumber num = new PDFNumber(); + num.setNumber(10); + numsArray.put(1, num); + numsArray.setDocument(doc); + + pdfObjectUnderTest = numsArray; + } + + /** + * Test output() - ensure that this object is properly outputted to the PDF document. + * @throws IOException if an I/O error occurs + */ + @Test + public void testOutput() throws IOException { + testOutputStreams(expectedString, numsArray); + } +} diff --git a/test/java/org/apache/fop/pdf/PDFObjectTestCase.java b/test/java/org/apache/fop/pdf/PDFObjectTestCase.java index 2e1f452af..d4ad87d78 100644 --- a/test/java/org/apache/fop/pdf/PDFObjectTestCase.java +++ b/test/java/org/apache/fop/pdf/PDFObjectTestCase.java @@ -19,44 +19,133 @@ 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 "1 0 obj\n" */ + protected final String beginObj = "1 0 obj\n"; + /** The string to end describing the object "\nendobj\n" */ + 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 " 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 " 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: + *
+     * 1 0 obj  ** ommited from expectedString
+     * 10
+     * endobj   ** ommited from expectedString
+     * 
+ * 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 index 000000000..023a91f65 --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFRectangleTestCase.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * Test case for {@link PDFRectangle}. + */ +public class PDFRectangleTestCase { + + /** + * Test outputInline() - ensure properly formatted co-ords are printed to the output stream. + * @throws IOException if an I/O error occurs + */ + @Test + public void testOutputInline() throws IOException { + OutputStream out = new ByteArrayOutputStream(); + // These are arbitrary values thus have no meaning + PDFRectangle rect = new PDFRectangle(1, 2, 3, 4); + + StringBuilder textBuffer = new StringBuilder(); + // Ensure text before the outputInline() is maintained + textBuffer.append("Test "); + + rect.outputInline(out, textBuffer); + assertEquals("Test [1 2 3 4]", textBuffer.toString()); + } +} diff --git a/test/java/org/apache/fop/pdf/PDFReferenceTestCase.java b/test/java/org/apache/fop/pdf/PDFReferenceTestCase.java new file mode 100644 index 000000000..1691698b3 --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFReferenceTestCase.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Test case for {@link PDFReference}. + */ +public class PDFReferenceTestCase { + + /** + * Tests outputInline() - ensure that this object is properly formatted when printed to the + * output stream. + * @throws IOException if an I/O error occurs + */ + @Test + public void testOutputInline() throws IOException { + PDFName name = new PDFName("Test name"); + name.setObjectNumber(2); + PDFReference pdfRef = new PDFReference(name); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StringBuilder textBuffer = new StringBuilder(); + // Ensure that text before outputInline() is kept + textBuffer.append("Text "); + + pdfRef.outputInline(out, textBuffer); + assertEquals("Text 2 0 R", textBuffer.toString()); + } + + /** + * Tests toString() - since this is used quite a lot, we have to ensure the format is correct. + */ + @Test + public void testToString() { + PDFName name = new PDFName("arbitrary"); + name.setObjectNumber(10); + PDFReference ref = new PDFReference(name); + assertEquals("10 0 R", ref.toString()); + } +} -- 2.39.5