From: Vincent Hennebert Date: Wed, 21 Mar 2012 15:12:43 +0000 (+0000) Subject: Added support for PDF object streams. X-Git-Tag: fop-1_1rc1old~87^2 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=refs%2Fheads%2FTemp_PDF_ObjectStreams;p=xmlgraphics-fop.git Added support for PDF object streams. When accessibility is enabled and PDF version 1.5 selected, the structure tree will be stored in object streams in order to reduce the size of the final PDF. This can lead to file reductions by up to 75% git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_PDF_ObjectStreams@1303431 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/fop/pdf/AbstractPDFStream.java b/src/java/org/apache/fop/pdf/AbstractPDFStream.java index 1b25c113f..0181728b8 100644 --- a/src/java/org/apache/fop/pdf/AbstractPDFStream.java +++ b/src/java/org/apache/fop/pdf/AbstractPDFStream.java @@ -29,16 +29,48 @@ import org.apache.fop.util.CloseBlockerOutputStream; /** * This is an abstract base class for PDF streams. */ -public abstract class AbstractPDFStream extends PDFDictionary { +public abstract class AbstractPDFStream extends PDFObject { + + private final PDFDictionary dictionary; /** The filters that should be applied */ private PDFFilterList filters; + private final boolean encodeOnTheFly; + + protected AbstractPDFStream() { + this(true); + } + + protected AbstractPDFStream(PDFDictionary dictionary) { + this(dictionary, true); + } + + protected AbstractPDFStream(boolean encodeOnTheFly) { + this(new PDFDictionary(), encodeOnTheFly); + } + + protected AbstractPDFStream(PDFDictionary dictionary, boolean encodeOnTheFly) { + this.dictionary = dictionary; + this.encodeOnTheFly = encodeOnTheFly; + } + + protected final PDFDictionary getDictionary() { + return dictionary; + } + + protected Object get(String key) { + return dictionary.get(key); + } + /** - * Constructor for AbstractPDFStream. + * Puts the given object in the dictionary associated to this stream. + * + * @param key the key in the dictionary + * @param value the value to store */ - public AbstractPDFStream() { - super(); + public void put(String key, Object value) { + dictionary.put(key, value); } /** @@ -180,17 +212,16 @@ public abstract class AbstractPDFStream extends PDFDictionary { * {@inheritDoc} */ @Override - protected int output(OutputStream stream) throws IOException { + public int output(OutputStream stream) throws IOException { setupFilterList(); CountingOutputStream cout = new CountingOutputStream(stream); StringBuilder textBuffer = new StringBuilder(64); - textBuffer.append(getObjectID()); StreamCache encodedStream = null; PDFNumber refLength = null; final Object lengthEntry; - if (isEncodingOnTheFly()) { + if (encodeOnTheFly) { refLength = new PDFNumber(); getDocumentSafely().registerObject(refLength); lengthEntry = refLength; @@ -200,7 +231,7 @@ public abstract class AbstractPDFStream extends PDFDictionary { } populateStreamDict(lengthEntry); - writeDictionary(cout, textBuffer); + dictionary.writeDictionary(cout, textBuffer); //Send encoded stream to target OutputStream PDFDocument.flushTextBuffer(textBuffer, cout); @@ -211,18 +242,14 @@ public abstract class AbstractPDFStream extends PDFDictionary { encodedStream.clear(); //Encoded stream can now be discarded } - textBuffer.append("\nendobj\n"); PDFDocument.flushTextBuffer(textBuffer, cout); return cout.getCount(); } - /** - * Indicates whether encoding may happen without buffering the encoded data. If this method - * returns true, the /Length entry will be an indirect object, a direct object otherwise. - * @return true if encoding should happen "on the fly" - */ - protected boolean isEncodingOnTheFly() { - return getDocument().isEncodingOnTheFly(); + @Override + public void setDocument(PDFDocument doc) { + dictionary.setDocument(doc); + super.setDocument(doc); } /** @@ -233,7 +260,7 @@ public abstract class AbstractPDFStream extends PDFDictionary { protected void populateStreamDict(Object lengthEntry) { put("Length", lengthEntry); if (!getFilterList().isDisableAllFilters()) { - getFilterList().putFilterDictEntries(this); + getFilterList().putFilterDictEntries(dictionary); } } diff --git a/src/java/org/apache/fop/pdf/CompressedObject.java b/src/java/org/apache/fop/pdf/CompressedObject.java new file mode 100644 index 000000000..55d9c2953 --- /dev/null +++ b/src/java/org/apache/fop/pdf/CompressedObject.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 java.io.IOException; +import java.io.OutputStream; + +/** + * Represents a PDF object that may appear in an object stream. An object stream is a PDF + * stream whose content is a sequence of PDF objects. See Section 3.4.6 of the PDF 1.5 + * Reference. + */ +interface CompressedObject { + + /** + * Returns the object number of this indirect object. Note that a compressed object + * must have a generation number of 0. + * + * @return the object number. + */ + int getObjectNumber(); + + /** + * Outputs this object's content into the given stream. + * + * @param outputStream a stream, likely to be provided by the containing object stream + * @return the number of bytes written to the stream + * @throws IOException + */ + int output(OutputStream outputStream) throws IOException; + +} diff --git a/src/java/org/apache/fop/pdf/ObjectStream.java b/src/java/org/apache/fop/pdf/ObjectStream.java new file mode 100644 index 000000000..e9335bc6f --- /dev/null +++ b/src/java/org/apache/fop/pdf/ObjectStream.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.fop.pdf.xref.CompressedObjectReference; + +/** + * An object stream, as described in section 3.4.6 of the PDF 1.5 Reference. + */ +public class ObjectStream extends PDFStream { + + private static final PDFName OBJ_STM = new PDFName("ObjStm"); + + private List objects = new ArrayList(); + + private int firstObjectOffset; + + ObjectStream() { + super(false); + } + + ObjectStream(ObjectStream previous) { + this(); + put("Extends", previous); + } + + CompressedObjectReference addObject(CompressedObject obj) { + if (obj == null) { + throw new NullPointerException("obj must not be null"); + } + CompressedObjectReference reference = new CompressedObjectReference(obj.getObjectNumber(), + getObjectNumber(), objects.size()); + objects.add(obj); + return reference; + } + + @Override + protected void outputRawStreamData(OutputStream out) throws IOException { + int currentOffset = 0; + StringBuilder offsetsPart = new StringBuilder(); + ByteArrayOutputStream streamContent = new ByteArrayOutputStream(); + for (CompressedObject object : objects) { + offsetsPart.append(object.getObjectNumber()) + .append(' ') + .append(currentOffset) + .append('\n'); + currentOffset += object.output(streamContent); + } + byte[] offsets = PDFDocument.encode(offsetsPart.toString()); + firstObjectOffset = offsets.length; + out.write(offsets); + streamContent.writeTo(out); + } + + @Override + protected void populateStreamDict(Object lengthEntry) { + put("Type", OBJ_STM); + put("N", objects.size()); + put("First", firstObjectOffset); + super.populateStreamDict(lengthEntry); + } +} diff --git a/src/java/org/apache/fop/pdf/ObjectStreamManager.java b/src/java/org/apache/fop/pdf/ObjectStreamManager.java new file mode 100644 index 000000000..723facd96 --- /dev/null +++ b/src/java/org/apache/fop/pdf/ObjectStreamManager.java @@ -0,0 +1,69 @@ +/* + * 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.util.ArrayList; +import java.util.List; + +import org.apache.fop.pdf.xref.CompressedObjectReference; + +/** + * Manages a collection of object streams, creating new streams as necessary to keep the + * number of objects in each stream at the recommended value. Streams are related to each + * other through the use of the Extends entry in the stream dictionary. + */ +class ObjectStreamManager { + + private static final int OBJECT_STREAM_CAPACITY = 100; + + private final PDFDocument pdfDocument; + + private final List compressedObjectReferences; + + private int numObjectsInStream; + + private ObjectStream currentObjectStream; + + ObjectStreamManager(PDFDocument pdfDocument) { + this.pdfDocument = pdfDocument; + createObjectStream(); + compressedObjectReferences = new ArrayList(); + } + + void add(CompressedObject compressedObject) { + if (numObjectsInStream++ == OBJECT_STREAM_CAPACITY) { + createObjectStream(); + numObjectsInStream = 1; + } + compressedObjectReferences.add(currentObjectStream.addObject(compressedObject)); + } + + private void createObjectStream() { + currentObjectStream = currentObjectStream == null + ? new ObjectStream() + : new ObjectStream(currentObjectStream); + pdfDocument.assignObjectNumber(currentObjectStream); + pdfDocument.addTrailerObject(currentObjectStream); + } + + List getCompressedObjectReferences() { + return compressedObjectReferences; + } +} diff --git a/src/java/org/apache/fop/pdf/PDFAnnotList.java b/src/java/org/apache/fop/pdf/PDFAnnotList.java index 0a8710627..65b327e31 100644 --- a/src/java/org/apache/fop/pdf/PDFAnnotList.java +++ b/src/java/org/apache/fop/pdf/PDFAnnotList.java @@ -58,22 +58,19 @@ public class PDFAnnotList extends PDFObject { */ public String toPDFString() { StringBuffer p = new StringBuffer(128); - p.append(getObjectID()); p.append("[\n"); for (int i = 0; i < getCount(); i++) { p.append(((PDFObject)links.get(i)).referencePDF()); p.append("\n"); } - p.append("]\nendobj\n"); + p.append("]"); return p.toString(); } /* * example - * 20 0 obj * [ * 19 0 R * ] - * endobj */ } diff --git a/src/java/org/apache/fop/pdf/PDFArray.java b/src/java/org/apache/fop/pdf/PDFArray.java index 78eba3bb9..131830328 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.util.Collection; import java.util.List; import org.apache.commons.io.output.CountingOutputStream; @@ -48,7 +47,7 @@ public class PDFArray extends PDFObject { * Create a new, empty array object with no parent. */ public PDFArray() { - this(null); + this((PDFObject) null); } /** @@ -84,13 +83,22 @@ 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, List values) { /* generic creation of PDF object */ super(parent); this.values.addAll(values); } + /** + * Creates an array object made of the given elements. + * + * @param elements the array content + */ + public PDFArray(Object... elements) { + this(null, elements); + } + /** * Create the array object * @param parent the array's parent if any @@ -180,13 +188,9 @@ public class PDFArray extends PDFObject { /** {@inheritDoc} */ @Override - protected int output(OutputStream stream) throws IOException { + public int output(OutputStream stream) throws IOException { CountingOutputStream cout = new CountingOutputStream(stream); StringBuilder textBuffer = new StringBuilder(64); - if (hasObjectNumber()) { - textBuffer.append(getObjectID()); - } - textBuffer.append('['); for (int i = 0; i < values.size(); i++) { if (i > 0) { @@ -196,11 +200,6 @@ public class PDFArray extends PDFObject { formatObject(obj, cout, textBuffer); } textBuffer.append(']'); - - if (hasObjectNumber()) { - textBuffer.append("\nendobj\n"); - } - PDFDocument.flushTextBuffer(textBuffer, cout); return cout.getCount(); } diff --git a/src/java/org/apache/fop/pdf/PDFCIDFont.java b/src/java/org/apache/fop/pdf/PDFCIDFont.java index 459fe2584..46374d869 100644 --- a/src/java/org/apache/fop/pdf/PDFCIDFont.java +++ b/src/java/org/apache/fop/pdf/PDFCIDFont.java @@ -200,7 +200,6 @@ public class PDFCIDFont extends PDFObject { */ public String toPDFString() { StringBuffer p = new StringBuffer(128); - p.append(getObjectID()); p.append("<< /Type /Font"); p.append("\n/BaseFont /"); p.append(this.basefont); @@ -234,14 +233,13 @@ public class PDFCIDFont extends PDFObject { p.append("\n/DW2 ["); // always two values, see p 211 p.append(this.dw2[0]); p.append(this.dw2[1]); - p.append("] \n>>\nendobj\n"); + p.append("]"); } if (w2 != null) { p.append("\n/W2 "); p.append(w2.toPDFString()); - p.append(" \n>>\nendobj\n"); } - p.append(" \n>>\nendobj\n"); + p.append("\n>>"); return p.toString(); } diff --git a/src/java/org/apache/fop/pdf/PDFCMap.java b/src/java/org/apache/fop/pdf/PDFCMap.java index 57d148fc2..34bc0f952 100644 --- a/src/java/org/apache/fop/pdf/PDFCMap.java +++ b/src/java/org/apache/fop/pdf/PDFCMap.java @@ -423,7 +423,7 @@ public class PDFCMap extends PDFStream { } /** {@inheritDoc} */ - protected int output(OutputStream stream) throws IOException { + public int output(OutputStream stream) throws IOException { CMapBuilder builder = createCMapBuilder(getBufferWriter()); builder.writeCMap(); return super.output(stream); diff --git a/src/java/org/apache/fop/pdf/PDFDestination.java b/src/java/org/apache/fop/pdf/PDFDestination.java index 400c4a4b6..21c655832 100644 --- a/src/java/org/apache/fop/pdf/PDFDestination.java +++ b/src/java/org/apache/fop/pdf/PDFDestination.java @@ -50,9 +50,8 @@ public class PDFDestination extends PDFObject { this.idRef = idRef; } - /** {@inheritDoc} */ @Override - protected int output(OutputStream stream) throws IOException { + public int output(OutputStream stream) throws IOException { CountingOutputStream cout = new CountingOutputStream(stream); StringBuilder textBuffer = new StringBuilder(64); diff --git a/src/java/org/apache/fop/pdf/PDFDictionary.java b/src/java/org/apache/fop/pdf/PDFDictionary.java index 5a6724304..6bacd31a3 100644 --- a/src/java/org/apache/fop/pdf/PDFDictionary.java +++ b/src/java/org/apache/fop/pdf/PDFDictionary.java @@ -98,19 +98,10 @@ public class PDFDictionary extends PDFObject { /** {@inheritDoc} */ @Override - protected int output(OutputStream stream) throws IOException { + public int output(OutputStream stream) throws IOException { CountingOutputStream cout = new CountingOutputStream(stream); StringBuilder textBuffer = new StringBuilder(64); - if (hasObjectNumber()) { - textBuffer.append(getObjectID()); - } - writeDictionary(cout, textBuffer); - - if (hasObjectNumber()) { - textBuffer.append("\nendobj\n"); - } - PDFDocument.flushTextBuffer(textBuffer, cout); return cout.getCount(); } diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java index e9886fc37..9850c605e 100644 --- a/src/java/org/apache/fop/pdf/PDFDocument.java +++ b/src/java/org/apache/fop/pdf/PDFDocument.java @@ -26,6 +26,7 @@ import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -37,6 +38,10 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fop.pdf.xref.CrossReferenceStream; +import org.apache.fop.pdf.xref.CrossReferenceTable; +import org.apache.fop.pdf.xref.TrailerDictionary; + /* image support modified from work of BoBoGi */ /* font support based on work by Takayuki Takeuchi */ @@ -63,31 +68,28 @@ import org.apache.commons.logging.LogFactory; */ public class PDFDocument { - private static final Long LOCATION_PLACEHOLDER = new Long(0); - /** the encoding to use when converting strings to PDF commands */ public static final String ENCODING = "ISO-8859-1"; /** the counter for object numbering */ - protected int objectcount = 0; + protected int objectcount; /** the logger instance */ private Log log = LogFactory.getLog("org.apache.fop.pdf"); /** the current character position */ - private long position = 0; - - /** character position of xref table */ - private long xref; + private long position; /** the character position of each object */ - private List location = new ArrayList(); + private List indirectObjectOffsets = new ArrayList(); + + private Collection structureTreeElements; /** List of objects to write in the trailer */ - private List trailerObjects = new ArrayList(); + private List trailerObjects = new ArrayList(); /** the objects themselves */ - private List objects = new LinkedList(); + private List objects = new LinkedList(); /** Controls the PDF version of this document */ private VersionController versionController; @@ -99,7 +101,7 @@ public class PDFDocument { private PDFRoot root; /** The root outline object */ - private PDFOutline outlineRoot = null; + private PDFOutline outlineRoot; /** The /Pages object (mark-fop@inomial.com) */ private PDFPages pages; @@ -118,66 +120,47 @@ public class PDFDocument { = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); /** the counter for Pattern name numbering (e.g. 'Pattern1') */ - private int patternCount = 0; + private int patternCount; /** the counter for Shading name numbering */ - private int shadingCount = 0; + private int shadingCount; /** the counter for XObject numbering */ - private int xObjectCount = 0; + private int xObjectCount; - /** the {@link PDFXObject}s map */ /* TODO: Should be modified (works only for image subtype) */ - private Map xObjectsMap = new HashMap(); - - /** The {@link PDFFont} map */ - private Map fontMap = new HashMap(); + private Map xObjectsMap = new HashMap(); - /** The {@link PDFFilter} map */ - private Map filterMap = new HashMap(); + private Map fontMap = new HashMap(); - /** List of {@link PDFGState}s. */ - private List gstates = new ArrayList(); + private Map> filterMap = new HashMap>(); - /** List of {@link PDFFunction}s. */ - private List functions = new ArrayList(); + private List gstates = new ArrayList(); - /** List of {@link PDFShading}s. */ - private List shadings = new ArrayList(); + private List functions = new ArrayList(); - /** List of {@link PDFPattern}s. */ - private List patterns = new ArrayList(); + private List shadings = new ArrayList(); - /** List of {@link PDFLink}s. */ - private List links = new ArrayList(); + private List patterns = new ArrayList(); - /** List of {@link PDFDestination}s. */ - private List destinations; + private List links = new ArrayList(); - /** List of {@link PDFFileSpec}s. */ - private List filespecs = new ArrayList(); + private List destinations; - /** List of {@link PDFGoToRemote}s. */ - private List gotoremotes = new ArrayList(); + private List filespecs = new ArrayList(); - /** List of {@link PDFGoTo}s. */ - private List gotos = new ArrayList(); + private List gotoremotes = new ArrayList(); - /** List of {@link PDFLaunch}es. */ - private List launches = new ArrayList(); + private List gotos = new ArrayList(); - /** - * The PDFDests object for the name dictionary. - * Note: This object is not a list. - */ - private PDFDests dests; + private List launches = new ArrayList(); private PDFFactory factory; - private boolean encodingOnTheFly = true; - private FileIDGenerator fileIDGenerator; + private boolean accessibilityEnabled; + /** * Creates an empty PDF document. * @@ -265,17 +248,6 @@ public class PDFDocument { return this.factory; } - /** - * Indicates whether stream encoding on-the-fly is enabled. If enabled - * stream can be serialized without the need for a buffer to merely - * calculate the stream length. - * - * @return true if on-the-fly encoding is enabled - */ - public boolean isEncodingOnTheFly() { - return this.encodingOnTheFly; - } - /** * Converts text to a byte array for writing to a PDF file. * @@ -336,7 +308,7 @@ public class PDFDocument { * * @param map the map of filter lists for each stream type */ - public void setFilterMap(Map map) { + public void setFilterMap(Map> map) { this.filterMap = map; } @@ -345,7 +317,7 @@ public class PDFDocument { * * @return the map of filters being used */ - public Map getFilterMap() { + public Map> getFilterMap() { return this.filterMap; } @@ -367,6 +339,37 @@ public class PDFDocument { return this.root; } + /** + * Creates and returns a StructTreeRoot object. + * + * @param parentTree the value of the ParenTree entry + * @return the structure tree root + */ + public PDFStructTreeRoot makeStructTreeRoot(PDFParentTree parentTree) { + PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(parentTree); + assignObjectNumber(structTreeRoot); + addTrailerObject(structTreeRoot); + root.setStructTreeRoot(structTreeRoot); + structureTreeElements = new ArrayList(); + return structTreeRoot; + } + + /** + * Creates and returns a structure element. + * + * @param structureType the structure type of the new element (value for the + * S entry) + * @param parent the parent of the new structure element in the structure + * hierarchy + * @return a dictionary of type StructElem + */ + public PDFStructElem makeStructureElement(PDFName structureType, PDFObject parent) { + PDFStructElem structElem = new PDFStructElem(parent, structureType); + assignObjectNumber(structElem); + structureTreeElements.add(structElem); + return structElem; + } + /** * Get the {@link PDFInfo} object for this document. * @@ -439,39 +442,39 @@ public class PDFDocument { //Add object to special lists where necessary if (obj instanceof PDFFunction) { - this.functions.add(obj); + this.functions.add((PDFFunction) obj); } if (obj instanceof PDFShading) { final String shadingName = "Sh" + (++this.shadingCount); ((PDFShading)obj).setName(shadingName); - this.shadings.add(obj); + this.shadings.add((PDFShading) obj); } if (obj instanceof PDFPattern) { final String patternName = "Pa" + (++this.patternCount); ((PDFPattern)obj).setName(patternName); - this.patterns.add(obj); + this.patterns.add((PDFPattern) obj); } if (obj instanceof PDFFont) { final PDFFont font = (PDFFont)obj; this.fontMap.put(font.getName(), font); } if (obj instanceof PDFGState) { - this.gstates.add(obj); + this.gstates.add((PDFGState) obj); } if (obj instanceof PDFPage) { this.pages.notifyKidRegistered((PDFPage)obj); } if (obj instanceof PDFLaunch) { - this.launches.add(obj); + this.launches.add((PDFLaunch) obj); } if (obj instanceof PDFLink) { - this.links.add(obj); + this.links.add((PDFLink) obj); } if (obj instanceof PDFFileSpec) { - this.filespecs.add(obj); + this.filespecs.add((PDFFileSpec) obj); } if (obj instanceof PDFGoToRemote) { - this.gotoremotes.add(obj); + this.gotoremotes.add((PDFGoToRemote) obj); } } @@ -485,7 +488,7 @@ public class PDFDocument { this.trailerObjects.add(obj); if (obj instanceof PDFGoTo) { - this.gotos.add(obj); + this.gotos.add((PDFGoTo) obj); } } @@ -537,9 +540,8 @@ public class PDFDocument { return this.encryption; } - private Object findPDFObject(List list, PDFObject compare) { - for (Iterator iter = list.iterator(); iter.hasNext();) { - PDFObject obj = (PDFObject) iter.next(); + private Object findPDFObject(List list, PDFObject compare) { + for (PDFObject obj : list) { if (compare.contentEquals(obj)) { return obj; } @@ -589,7 +591,7 @@ public class PDFDocument { * @return PDFFont the requested font, null if it wasn't found */ protected PDFFont findFont(String fontname) { - return (PDFFont)this.fontMap.get(fontname); + return this.fontMap.get(fontname); } /** @@ -601,7 +603,7 @@ public class PDFDocument { protected PDFDestination findDestination(PDFDestination compare) { int index = getDestinationList().indexOf(compare); if (index >= 0) { - return (PDFDestination)getDestinationList().get(index); + return getDestinationList().get(index); } else { return null; } @@ -666,9 +668,9 @@ public class PDFDocument { */ protected PDFGState findGState(PDFGState wanted, PDFGState current) { PDFGState poss; - Iterator iter = this.gstates.iterator(); + Iterator iter = this.gstates.iterator(); while (iter.hasNext()) { - PDFGState avail = (PDFGState)iter.next(); + PDFGState avail = iter.next(); poss = new PDFGState(); poss.addValues(current); poss.addValues(avail); @@ -712,7 +714,7 @@ public class PDFDocument { * * @return the map of fonts used in this document */ - public Map getFontMap() { + public Map getFontMap() { return this.fontMap; } @@ -753,16 +755,7 @@ public class PDFDocument { * @return the PDFXObject for the key if found */ public PDFXObject getXObject(String key) { - return (PDFXObject)this.xObjectsMap.get(key); - } - - /** - * Gets the PDFDests object (which represents the /Dests entry). - * - * @return the PDFDests object (which represents the /Dests entry). - */ - public PDFDests getDests() { - return this.dests; + return this.xObjectsMap.get(key); } /** @@ -771,7 +764,7 @@ public class PDFDocument { */ public void addDestination(PDFDestination destination) { if (this.destinations == null) { - this.destinations = new ArrayList(); + this.destinations = new ArrayList(); } this.destinations.add(destination); } @@ -781,11 +774,11 @@ public class PDFDocument { * * @return the list of named destinations. */ - public List getDestinationList() { + public List getDestinationList() { if (hasDestinations()) { return this.destinations; } else { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } } @@ -900,17 +893,8 @@ public class PDFDocument { return this.resources; } - /** - * Ensure there is room in the locations xref for the number of - * objects that have been created. - * @param objidx the object's index - * @param position the position - */ - private void setLocation(int objidx, long position) { - while (this.location.size() <= objidx) { - this.location.add(LOCATION_PLACEHOLDER); - } - this.location.set(objidx, position); + public void enableAccessibility(boolean enableAccessibility) { + this.accessibilityEnabled = enableAccessibility; } /** @@ -924,23 +908,50 @@ public class PDFDocument { //LinkedList) allows for output() methods to create and register objects //on the fly even during serialization. while (this.objects.size() > 0) { - /* Retrieve first */ - PDFObject object = (PDFObject)this.objects.remove(0); - /* - * add the position of this object to the list of object - * locations - */ - setLocation(object.getObjectNumber() - 1, this.position); - - /* - * output the object and increment the character position - * by the object's length - */ - this.position += object.output(stream); + PDFObject object = this.objects.remove(0); + streamIndirectObject(object, stream); } + } - //Clear all objects written to the file - //this.objects.clear(); + private void streamIndirectObject(PDFObject o, OutputStream stream) throws IOException { + recordObjectOffset(o); + this.position += outputIndirectObject(o, stream); + } + + private void streamIndirectObjects(Collection objects, OutputStream stream) + throws IOException { + for (PDFObject o : objects) { + streamIndirectObject(o, stream); + } + } + + private void recordObjectOffset(PDFObject object) { + int index = object.getObjectNumber() - 1; + while (indirectObjectOffsets.size() <= index) { + indirectObjectOffsets.add(null); + } + indirectObjectOffsets.set(index, position); + } + + /** + * Outputs the given object, wrapped by obj/endobj, to the given stream. + * + * @param object an indirect object, as described in Section 3.2.9 of the PDF 1.5 + * Reference. + * @param stream the stream to which the object must be output + * @throws IllegalArgumentException if the object is not an indirect object + */ + public static int outputIndirectObject(PDFObject object, OutputStream stream) + throws IOException { + if (!object.hasObjectNumber()) { + throw new IllegalArgumentException("Not an indirect object"); + } + byte[] obj = encode(object.getObjectID()); + stream.write(obj); + int length = object.output(stream); + byte[] endobj = encode("\nendobj\n"); + stream.write(endobj); + return obj.length + length + endobj.length; } /** @@ -980,89 +991,102 @@ public class PDFDocument { * @throws IOException if there is an exception writing to the output stream */ public void outputTrailer(OutputStream stream) throws IOException { + createDestinations(); + output(stream); + outputTrailerObjectsAndXref(stream); + } + + private void createDestinations() { if (hasDestinations()) { Collections.sort(this.destinations, new DestinationComparator()); - this.dests = getFactory().makeDests(this.destinations); + PDFDests dests = getFactory().makeDests(this.destinations); if (this.root.getNames() == null) { this.root.setNames(getFactory().makeNames()); } this.root.getNames().setDests(dests); } - output(stream); - for (int count = 0; count < this.trailerObjects.size(); count++) { - PDFObject o = (PDFObject)this.trailerObjects.get(count); - setLocation(o.getObjectNumber() - 1, this.position); - this.position += o.output(stream); + } + + private void outputTrailerObjectsAndXref(OutputStream stream) throws IOException { + TrailerOutputHelper trailerOutputHelper = mayCompressStructureTreeElements() + ? new CompressedTrailerOutputHelper() + : new UncompressedTrailerOutputHelper(); + if (structureTreeElements != null) { + trailerOutputHelper.outputStructureTreeElements(stream); } - /* output the xref table and increment the character position - by the table's length */ - this.position += outputXref(stream); - - /* construct the trailer */ - StringBuffer pdf = new StringBuffer(128); - pdf.append("trailer\n<<\n/Size ") - .append(this.objectcount + 1) - .append("\n/Root ") - .append(this.root.referencePDF()) - .append("\n/Info ") - .append(this.info.referencePDF()) - .append('\n'); - - if (this.isEncryptionActive()) { - pdf.append(this.encryption.getTrailerEntry()); - } else { - byte[] fileID = getFileIDGenerator().getOriginalFileID(); - String fileIDAsString = PDFText.toHex(fileID); - pdf.append("/ID [" + fileIDAsString + " " + fileIDAsString + "]"); + streamIndirectObjects(trailerObjects, stream); + TrailerDictionary trailerDictionary = createTrailerDictionary(); + long startxref = trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary); + String trailer = "startxref\n" + startxref + "\n%%EOF\n"; + stream.write(encode(trailer)); + } + + private boolean mayCompressStructureTreeElements() { + return accessibilityEnabled + && versionController.getPDFVersion().compareTo(Version.V1_5) >= 0; + } + + private TrailerDictionary createTrailerDictionary() { + FileIDGenerator gen = getFileIDGenerator(); + TrailerDictionary trailerDictionary = new TrailerDictionary(this) + .setRoot(root) + .setInfo(info) + .setFileID(gen.getOriginalFileID(), gen.getUpdatedFileID()); + if (isEncryptionActive()) { + trailerDictionary.setEncryption(encryption); } + return trailerDictionary; + } - pdf.append("\n>>\nstartxref\n") - .append(this.xref) - .append("\n%%EOF\n"); + private interface TrailerOutputHelper { - /* write the trailer */ - stream.write(encode(pdf.toString())); + void outputStructureTreeElements(OutputStream stream) throws IOException; + + /** + * @return the offset of the cross-reference object (the value of startxref) + */ + long outputCrossReferenceObject(OutputStream stream, TrailerDictionary trailerDictionary) + throws IOException; } - /** - * Write the xref table - * - * @param stream the OutputStream to write the xref table to - * @return the number of characters written - * @throws IOException in case of an error writing the result to - * the parameter stream - */ - private int outputXref(OutputStream stream) throws IOException { - - /* remember position of xref table */ - this.xref = this.position; - - /* construct initial part of xref */ - StringBuffer pdf = new StringBuffer(128); - pdf.append("xref\n0 "); - pdf.append(this.objectcount + 1); - pdf.append("\n0000000000 65535 f \n"); - - String s; - String loc; - for (int count = 0; count < this.location.size(); count++) { - final String padding = "0000000000"; - s = this.location.get(count).toString(); - if (s.length() > 10) { - throw new IOException("PDF file too large. PDF cannot grow beyond approx. 9.3GB."); - } + private class UncompressedTrailerOutputHelper implements TrailerOutputHelper { - /* contruct xref entry for object */ - loc = padding.substring(s.length()) + s; + public void outputStructureTreeElements(OutputStream stream) + throws IOException { + streamIndirectObjects(structureTreeElements, stream); + } - /* append to xref table */ - pdf = pdf.append(loc).append(" 00000 n \n"); + public long outputCrossReferenceObject(OutputStream stream, + TrailerDictionary trailerDictionary) throws IOException { + new CrossReferenceTable(trailerDictionary, position, + indirectObjectOffsets).output(stream); + return position; } + } + + private class CompressedTrailerOutputHelper implements TrailerOutputHelper { + + private ObjectStreamManager structureTreeObjectStreams; - /* write the xref table and return the character length */ - byte[] pdfBytes = encode(pdf.toString()); - stream.write(pdfBytes); - return pdfBytes.length; + public void outputStructureTreeElements(OutputStream stream) + throws IOException { + assert structureTreeElements.size() > 0; + structureTreeObjectStreams = new ObjectStreamManager(PDFDocument.this); + for (PDFStructElem structElem : structureTreeElements) { + structureTreeObjectStreams.add(structElem); + } + } + + public long outputCrossReferenceObject(OutputStream stream, + TrailerDictionary trailerDictionary) throws IOException { + // Outputting the object streams should not have created new indirect objects + assert objects.isEmpty(); + new CrossReferenceStream(PDFDocument.this, ++objectcount, trailerDictionary, position, + indirectObjectOffsets, + structureTreeObjectStreams.getCompressedObjectReferences()) + .output(stream); + return position; + } } long getCurrentFileSize() { diff --git a/src/java/org/apache/fop/pdf/PDFEncryption.java b/src/java/org/apache/fop/pdf/PDFEncryption.java index 277cf0a94..fcb56e50b 100644 --- a/src/java/org/apache/fop/pdf/PDFEncryption.java +++ b/src/java/org/apache/fop/pdf/PDFEncryption.java @@ -40,8 +40,10 @@ public interface PDFEncryption { byte[] encrypt(byte[] data, PDFObject refObj); /** - * Returns the trailer entry for encryption. - * @return the trailer entry + * Returns the /Encrypt entry in the file trailer dictionary. + * + * @return the string "/Encrypt n g R\n" where n and g are the number and generation + * of the document's encryption dictionary */ String getTrailerEntry(); } diff --git a/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java b/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java index c9b9c58ba..2ef3b93da 100644 --- a/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java +++ b/src/java/org/apache/fop/pdf/PDFEncryptionJCE.java @@ -69,8 +69,9 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption { ? new Rev2Engine(encryptionSettings) : new Rev3Engine(encryptionSettings); initializationEngine.run(); - encryptionDictionary = createEncryptionDictionary(getObjectID(), permissions, - initializationEngine.oValue, initializationEngine.uValue); + encryptionDictionary = createEncryptionDictionary(permissions, + initializationEngine.oValue, + initializationEngine.uValue); } private void determineEncryptionAlgorithm() { @@ -91,18 +92,16 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption { && encryptionParams.isAllowPrintHq(); } - private String createEncryptionDictionary(final String objectId, final int permissions, - final byte[] oValue, final byte[] uValue) { - return objectId - + "<< /Filter /Standard\n" + private String createEncryptionDictionary(final int permissions, final byte[] oValue, + final byte[] uValue) { + return "<< /Filter /Standard\n" + "/V " + version + "\n" + "/R " + revision + "\n" + "/Length " + encryptionLength + "\n" + "/P " + permissions + "\n" + "/O " + PDFText.toHex(oValue) + "\n" + "/U " + PDFText.toHex(uValue) + "\n" - + ">>\n" - + "endobj\n"; + + ">>"; } } @@ -488,14 +487,7 @@ public final class PDFEncryptionJCE extends PDFObject implements PDFEncryption { /** {@inheritDoc} */ public String getTrailerEntry() { - PDFDocument doc = getDocumentSafely(); - FileIDGenerator gen = doc.getFileIDGenerator(); - return "/Encrypt " + getObjectNumber() + " " - + getGeneration() + " R\n" - + "/ID[" - + PDFText.toHex(gen.getOriginalFileID()) - + PDFText.toHex(gen.getUpdatedFileID()) - + "]\n"; + return "/Encrypt " + getObjectNumber() + " " + getGeneration() + " R\n"; } private static byte[] encryptWithKey(byte[] key, byte[] data) { diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index a981dae88..2c347031f 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -912,35 +912,6 @@ public class PDFFactory { return pageLabels; } - /** - * Creates and returns a StructTreeRoot object. Used for accessibility. - * @param parentTree the value of the ParenTree entry - * @return structure Tree Root element - */ - public PDFStructTreeRoot makeStructTreeRoot(PDFParentTree parentTree) { - PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(parentTree); - getDocument().assignObjectNumber(structTreeRoot); - getDocument().addTrailerObject(structTreeRoot); - getDocument().getRoot().setStructTreeRoot(structTreeRoot); - return structTreeRoot; - } - - /** - * Creates and returns a StructElem object. - * - * @param structureType the structure type of the new element (value for the - * S entry) - * @param parent the parent of the new structure element in the structure - * hierarchy - * @return the newly created element - */ - public PDFStructElem makeStructureElement(PDFName structureType, PDFObject parent) { - PDFStructElem structElem = new PDFStructElem(parent, structureType); - getDocument().assignObjectNumber(structElem); - getDocument().addTrailerObject(structElem); - return structElem; - } - /** * Make a the head object of the name dictionary (the /Dests object). * diff --git a/src/java/org/apache/fop/pdf/PDFFilterList.java b/src/java/org/apache/fop/pdf/PDFFilterList.java index 3025b8788..3f26f454c 100644 --- a/src/java/org/apache/fop/pdf/PDFFilterList.java +++ b/src/java/org/apache/fop/pdf/PDFFilterList.java @@ -21,6 +21,7 @@ package org.apache.fop.pdf; import java.io.IOException; import java.io.OutputStream; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -47,7 +48,7 @@ public class PDFFilterList { /** Key for the filter used for metadata */ public static final String METADATA_FILTER = "metadata"; - private List filters = new java.util.ArrayList(); + private List filters = new java.util.ArrayList(); private boolean ignoreASCIIFilters = false; @@ -197,6 +198,10 @@ public class PDFFilterList { } } + List getFilters() { + return Collections.unmodifiableList(filters); + } + /** * Apply the filters to the data * in the order given and return the /Filter and /DecodeParms @@ -206,7 +211,7 @@ public class PDFFilterList { * @return a String representing the filter list */ protected String buildFilterDictEntries() { - if (filters != null && filters.size() > 0) { + if (filters.size() > 0) { List names = new java.util.ArrayList(); List parms = new java.util.ArrayList(); @@ -229,7 +234,7 @@ public class PDFFilterList { * @param dict the PDFDictionary to set the entries on */ protected void putFilterDictEntries(PDFDictionary dict) { - if (filters != null && filters.size() > 0) { + if (filters.size() > 0) { List names = new java.util.ArrayList(); List parms = new java.util.ArrayList(); @@ -358,7 +363,7 @@ public class PDFFilterList { */ public OutputStream applyFilters(OutputStream stream) throws IOException { OutputStream out = stream; - if (filters != null && !isDisableAllFilters()) { + if (!isDisableAllFilters()) { for (int count = filters.size() - 1; count >= 0; count--) { PDFFilter filter = (PDFFilter)filters.get(count); out = filter.applyFilter(out); diff --git a/src/java/org/apache/fop/pdf/PDFFont.java b/src/java/org/apache/fop/pdf/PDFFont.java index 2808e5ba7..191fd223e 100644 --- a/src/java/org/apache/fop/pdf/PDFFont.java +++ b/src/java/org/apache/fop/pdf/PDFFont.java @@ -174,7 +174,7 @@ public class PDFFont extends PDFDictionary { } /** {@inheritDoc} */ - protected int output(OutputStream stream) throws IOException { + public int output(OutputStream stream) throws IOException { validate(); return super.output(stream); } diff --git a/src/java/org/apache/fop/pdf/PDFFormXObject.java b/src/java/org/apache/fop/pdf/PDFFormXObject.java index 2ccc17086..bc81a0a35 100644 --- a/src/java/org/apache/fop/pdf/PDFFormXObject.java +++ b/src/java/org/apache/fop/pdf/PDFFormXObject.java @@ -44,7 +44,7 @@ public class PDFFormXObject extends PDFXObject { * @param resources the resource PDF reference */ public PDFFormXObject(int xnumber, PDFStream contents, PDFReference resources) { - super(); + super(contents.getDictionary()); put("Name", new PDFName("Form" + xnumber)); this.contents = contents; @@ -160,7 +160,7 @@ public class PDFFormXObject extends PDFXObject { } /** {@inheritDoc} */ - protected int output(OutputStream stream) throws IOException { + public int output(OutputStream stream) throws IOException { final int len = super.output(stream); //Now that the data has been written, it can be discarded. diff --git a/src/java/org/apache/fop/pdf/PDFFunction.java b/src/java/org/apache/fop/pdf/PDFFunction.java index 9e93ebee8..44198105b 100644 --- a/src/java/org/apache/fop/pdf/PDFFunction.java +++ b/src/java/org/apache/fop/pdf/PDFFunction.java @@ -380,8 +380,7 @@ public class PDFFunction extends PDFObject { int numberOfFunctions = 0; int tempInt = 0; StringBuffer p = new StringBuffer(256); - p.append(getObjectID() - + "<< \n/FunctionType " + this.functionType + " \n"); + p.append("<< \n/FunctionType " + this.functionType + " \n"); // FunctionType 0 if (this.functionType == 0) { @@ -482,15 +481,14 @@ public class PDFFunction extends PDFObject { p.append("] \n"); } } - p.append(">> \n"); + p.append(">>"); // stream representing the function if (this.functionDataStream != null) { - p.append("stream\n" + this.functionDataStream - + "\nendstream\n"); + p.append("\nstream\n" + this.functionDataStream + + "\nendstream"); } - p.append("endobj\n"); // end of if FunctionType 0 } else if (this.functionType == 2) { @@ -550,7 +548,7 @@ public class PDFFunction extends PDFObject { + PDFNumber.doubleOut(new Double(this.interpolationExponentN)) + " \n"); - p.append(">> \nendobj\n"); + p.append(">>"); } else if (this.functionType == 3) { // fix this up when my eyes uncross @@ -643,10 +641,7 @@ public class PDFFunction extends PDFObject { } } - p.append("] \n"); - - - p.append(">> \nendobj\n"); + p.append("]\n>>"); } else if (this.functionType == 4) { // fix this up when my eyes uncross // DOMAIN @@ -681,15 +676,14 @@ public class PDFFunction extends PDFObject { + " \n"); } - p.append(">> \n"); + p.append(">>"); // stream representing the function if (this.functionDataStream != null) { - p.append("stream\n{ " + this.functionDataStream - + " } \nendstream\n"); + p.append("\nstream\n{ " + this.functionDataStream + + " }\nendstream"); } - p.append("endobj\n"); } diff --git a/src/java/org/apache/fop/pdf/PDFGState.java b/src/java/org/apache/fop/pdf/PDFGState.java index fe57e39c2..7306218ae 100644 --- a/src/java/org/apache/fop/pdf/PDFGState.java +++ b/src/java/org/apache/fop/pdf/PDFGState.java @@ -149,12 +149,10 @@ public class PDFGState extends PDFObject { */ public String toPDFString() { StringBuffer sb = new StringBuffer(64); - sb.append(getObjectID()); sb.append("<<\n/Type /ExtGState\n"); appendVal(sb, GSTATE_ALPHA_NONSTROKE); appendVal(sb, GSTATE_ALPHA_STROKE); - - sb.append(">>\nendobj\n"); + sb.append(">>"); return sb.toString(); } diff --git a/src/java/org/apache/fop/pdf/PDFGoTo.java b/src/java/org/apache/fop/pdf/PDFGoTo.java index 0626f0fcd..784aa4f95 100644 --- a/src/java/org/apache/fop/pdf/PDFGoTo.java +++ b/src/java/org/apache/fop/pdf/PDFGoTo.java @@ -124,9 +124,7 @@ public class PDFGoTo extends PDFAction { } else { dest = "/D [" + this.pageReference + " " + destination + "]\n"; } - return getObjectID() - + "<< /Type /Action\n/S /GoTo\n" + dest - + ">>\nendobj\n"; + return "<< /Type /Action\n/S /GoTo\n" + dest + ">>"; } /* diff --git a/src/java/org/apache/fop/pdf/PDFGoToRemote.java b/src/java/org/apache/fop/pdf/PDFGoToRemote.java index 93fbe47de..2dccdafce 100644 --- a/src/java/org/apache/fop/pdf/PDFGoToRemote.java +++ b/src/java/org/apache/fop/pdf/PDFGoToRemote.java @@ -106,7 +106,6 @@ public class PDFGoToRemote extends PDFAction { */ public String toPDFString() { StringBuffer sb = new StringBuffer(64); - sb.append(getObjectID()); sb.append("<<\n/S /GoToR\n/F "); sb.append(pdfFileSpec.toString()); sb.append("\n"); @@ -121,7 +120,7 @@ public class PDFGoToRemote extends PDFAction { sb.append("/NewWindow true"); } - sb.append(" \n>>\nendobj\n"); + sb.append("\n>>"); return sb.toString(); } diff --git a/src/java/org/apache/fop/pdf/PDFICCBasedColorSpace.java b/src/java/org/apache/fop/pdf/PDFICCBasedColorSpace.java index 87e8f5475..b86ba29f5 100644 --- a/src/java/org/apache/fop/pdf/PDFICCBasedColorSpace.java +++ b/src/java/org/apache/fop/pdf/PDFICCBasedColorSpace.java @@ -99,9 +99,7 @@ public class PDFICCBasedColorSpace extends PDFObject implements PDFColorSpace { @Override protected String toPDFString() { StringBuffer sb = new StringBuffer(64); - sb.append(getObjectID()); sb.append("[/ICCBased ").append(getICCStream().referencePDF()).append("]"); - sb.append("\nendobj\n"); return sb.toString(); } diff --git a/src/java/org/apache/fop/pdf/PDFICCStream.java b/src/java/org/apache/fop/pdf/PDFICCStream.java index 9002fcc94..33b81307b 100644 --- a/src/java/org/apache/fop/pdf/PDFICCStream.java +++ b/src/java/org/apache/fop/pdf/PDFICCStream.java @@ -64,7 +64,7 @@ public class PDFICCStream extends PDFStream { * {@inheritDoc} */ @Override - protected int output(java.io.OutputStream stream) + public int output(java.io.OutputStream stream) throws java.io.IOException { int length = super.output(stream); this.cp = null; //Free ICC stream when it's not used anymore diff --git a/src/java/org/apache/fop/pdf/PDFImageXObject.java b/src/java/org/apache/fop/pdf/PDFImageXObject.java index 9ec8f1595..acab4c19c 100644 --- a/src/java/org/apache/fop/pdf/PDFImageXObject.java +++ b/src/java/org/apache/fop/pdf/PDFImageXObject.java @@ -61,7 +61,7 @@ public class PDFImageXObject extends PDFXObject { * @throws IOException if there is an error writing the data * @return the length of the data written */ - protected int output(OutputStream stream) throws IOException { + public int output(OutputStream stream) throws IOException { int length = super.output(stream); // let it gc @@ -137,7 +137,7 @@ public class PDFImageXObject extends PDFXObject { put("SMask", ref); } //Important: do this at the end so previous values can be overwritten. - pdfimage.populateXObjectDictionary(this); + pdfimage.populateXObjectDictionary(getDictionary()); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/pdf/PDFInfo.java b/src/java/org/apache/fop/pdf/PDFInfo.java index 3f3fb3b46..d457c2888 100644 --- a/src/java/org/apache/fop/pdf/PDFInfo.java +++ b/src/java/org/apache/fop/pdf/PDFInfo.java @@ -169,7 +169,6 @@ public class PDFInfo extends PDFObject { PDFProfile profile = getDocumentSafely().getProfile(); ByteArrayOutputStream bout = new ByteArrayOutputStream(128); try { - bout.write(encode(getObjectID())); bout.write(encode("<<\n")); if (title != null && title.length() > 0) { bout.write(encode("/Title ")); @@ -229,7 +228,7 @@ public class PDFInfo extends PDFObject { bout.write(encode("/Trapped /False\n")); } - bout.write(encode(">>\nendobj\n")); + bout.write(encode(">>")); } catch (IOException ioe) { log.error("Ignored I/O exception", ioe); } diff --git a/src/java/org/apache/fop/pdf/PDFLaunch.java b/src/java/org/apache/fop/pdf/PDFLaunch.java index e62da5279..7d80ddb43 100644 --- a/src/java/org/apache/fop/pdf/PDFLaunch.java +++ b/src/java/org/apache/fop/pdf/PDFLaunch.java @@ -54,10 +54,9 @@ public class PDFLaunch extends PDFAction { /** {@inheritDoc} */ public String toPDFString() { StringBuffer sb = new StringBuffer(64); - sb.append(getObjectID()); sb.append("<<\n/S /Launch\n/F "); sb.append(externalFileSpec.toString()); - sb.append(" \n>>\nendobj\n"); + sb.append("\n>>"); return sb.toString(); } diff --git a/src/java/org/apache/fop/pdf/PDFLink.java b/src/java/org/apache/fop/pdf/PDFLink.java index 934932648..ffec6f09c 100644 --- a/src/java/org/apache/fop/pdf/PDFLink.java +++ b/src/java/org/apache/fop/pdf/PDFLink.java @@ -92,15 +92,14 @@ public class PDFLink extends PDFObject { f |= 1 << (5 - 1); //NoRotate, bit 5 fFlag = "/F " + f; } - String s = getObjectID() - + "<< /Type /Annot\n" + "/Subtype /Link\n" + "/Rect [ " + String s = "<< /Type /Annot\n" + "/Subtype /Link\n" + "/Rect [ " + (ulx) + " " + (uly) + " " + (brx) + " " + (bry) + " ]\n" + "/C [ " + this.color + " ]\n" + "/Border [ 0 0 0 ]\n" + "/A " + this.action.getAction() + "\n" + "/H /I\n" + (this.structParent != null ? "/StructParent " + this.structParent.toString() + "\n" : "") - + fFlag + "\n>>\nendobj\n"; + + fFlag + "\n>>"; return s; } diff --git a/src/java/org/apache/fop/pdf/PDFMetadata.java b/src/java/org/apache/fop/pdf/PDFMetadata.java index b9612d11b..9b1b8165b 100644 --- a/src/java/org/apache/fop/pdf/PDFMetadata.java +++ b/src/java/org/apache/fop/pdf/PDFMetadata.java @@ -79,7 +79,7 @@ public class PDFMetadata extends PDFStream { * byte arrays around so much * {@inheritDoc} */ - protected int output(java.io.OutputStream stream) + public int output(java.io.OutputStream stream) throws java.io.IOException { int length = super.output(stream); this.xmpMetadata = null; //Release DOM when it's not used anymore diff --git a/src/java/org/apache/fop/pdf/PDFName.java b/src/java/org/apache/fop/pdf/PDFName.java index 7fa6842fa..23294cc54 100644 --- a/src/java/org/apache/fop/pdf/PDFName.java +++ b/src/java/org/apache/fop/pdf/PDFName.java @@ -108,22 +108,11 @@ public class PDFName extends PDFObject { return name.hashCode(); } - - /** {@inheritDoc} */ @Override - protected int output(OutputStream stream) throws IOException { + public int output(OutputStream stream) throws IOException { CountingOutputStream cout = new CountingOutputStream(stream); StringBuilder textBuffer = new StringBuilder(64); - if (hasObjectNumber()) { - textBuffer.append(getObjectID()); - } - textBuffer.append(toString()); - - if (hasObjectNumber()) { - textBuffer.append("\nendobj\n"); - } - PDFDocument.flushTextBuffer(textBuffer, cout); return cout.getCount(); } diff --git a/src/java/org/apache/fop/pdf/PDFNumber.java b/src/java/org/apache/fop/pdf/PDFNumber.java index 5bc648ced..95242305c 100644 --- a/src/java/org/apache/fop/pdf/PDFNumber.java +++ b/src/java/org/apache/fop/pdf/PDFNumber.java @@ -85,13 +85,7 @@ public class PDFNumber extends PDFObject { "The number of this PDFNumber must not be empty"); } StringBuffer sb = new StringBuffer(64); - if (hasObjectNumber()) { - sb.append(getObjectID()); - } sb.append(doubleOut(getNumber().doubleValue(), 10)); - if (hasObjectNumber()) { - sb.append("\nendobj\n"); - } return sb.toString(); } diff --git a/src/java/org/apache/fop/pdf/PDFNumsArray.java b/src/java/org/apache/fop/pdf/PDFNumsArray.java index ecd301647..e9e1855b0 100644 --- a/src/java/org/apache/fop/pdf/PDFNumsArray.java +++ b/src/java/org/apache/fop/pdf/PDFNumsArray.java @@ -88,13 +88,9 @@ public class PDFNumsArray extends PDFObject { /** {@inheritDoc} */ @Override - protected int output(OutputStream stream) throws IOException { + public int output(OutputStream stream) throws IOException { CountingOutputStream cout = new CountingOutputStream(stream); StringBuilder textBuffer = new StringBuilder(64); - if (hasObjectNumber()) { - textBuffer.append(getObjectID()); - } - textBuffer.append('['); boolean first = true; for (Map.Entry entry : this.map.entrySet()) { @@ -107,11 +103,6 @@ public class PDFNumsArray extends PDFObject { formatObject(entry.getValue(), cout, textBuffer); } textBuffer.append(']'); - - if (hasObjectNumber()) { - textBuffer.append("\nendobj\n"); - } - 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 5abb5a142..1b9c4eea7 100644 --- a/src/java/org/apache/fop/pdf/PDFObject.java +++ b/src/java/org/apache/fop/pdf/PDFObject.java @@ -204,7 +204,7 @@ public abstract class PDFObject implements PDFWritable { * @throws IOException if there is an error writing to the stream * @return the number of bytes written */ - protected int output(OutputStream stream) throws IOException { + public int output(OutputStream stream) throws IOException { byte[] pdf = this.toPDF(); stream.write(pdf); return pdf.length; diff --git a/src/java/org/apache/fop/pdf/PDFOutline.java b/src/java/org/apache/fop/pdf/PDFOutline.java index 66116c681..61c562548 100644 --- a/src/java/org/apache/fop/pdf/PDFOutline.java +++ b/src/java/org/apache/fop/pdf/PDFOutline.java @@ -131,7 +131,6 @@ public class PDFOutline extends PDFObject { protected byte[] toPDF() { ByteArrayOutputStream bout = new ByteArrayOutputStream(128); try { - bout.write(encode(getObjectID())); bout.write(encode("<<")); if (parent == null) { // root Outlines object @@ -164,7 +163,7 @@ public class PDFOutline extends PDFObject { bout.write(encode(" /A " + actionRef + "\n")); } } - bout.write(encode(">> endobj\n")); + bout.write(encode(">>")); } catch (IOException ioe) { log.error("Ignored I/O exception", ioe); } diff --git a/src/java/org/apache/fop/pdf/PDFOutputIntent.java b/src/java/org/apache/fop/pdf/PDFOutputIntent.java index ea073829b..1c0373944 100644 --- a/src/java/org/apache/fop/pdf/PDFOutputIntent.java +++ b/src/java/org/apache/fop/pdf/PDFOutputIntent.java @@ -130,7 +130,6 @@ public class PDFOutputIntent extends PDFObject { public byte[] toPDF() { ByteArrayOutputStream bout = new ByteArrayOutputStream(128); try { - bout.write(encode(getObjectID())); bout.write(encode("<<\n")); bout.write(encode("/Type /OutputIntent\n")); @@ -164,7 +163,7 @@ public class PDFOutputIntent extends PDFObject { bout.write(encode("/DestOutputProfile " + destOutputProfile.referencePDF() + "\n")); } - bout.write(encode(">>\nendobj\n")); + bout.write(encode(">>")); } catch (IOException ioe) { log.error("Ignored I/O exception", ioe); } diff --git a/src/java/org/apache/fop/pdf/PDFPages.java b/src/java/org/apache/fop/pdf/PDFPages.java index 61d860eaa..98c293a97 100644 --- a/src/java/org/apache/fop/pdf/PDFPages.java +++ b/src/java/org/apache/fop/pdf/PDFPages.java @@ -109,10 +109,9 @@ public class PDFPages extends PDFObject { */ public String toPDFString() { StringBuffer sb = new StringBuffer(64); - sb.append(getObjectID()) - .append("<< /Type /Pages\n/Count ") - .append(this.getCount()) - .append("\n/Kids ["); + sb.append("<< /Type /Pages\n/Count ") + .append(this.getCount()) + .append("\n/Kids ["); for (int i = 0; i < kids.size(); i++) { Object kid = kids.get(i); if (kid == null) { @@ -120,7 +119,7 @@ public class PDFPages extends PDFObject { } sb.append(kid).append(" "); } - sb.append("] >>\nendobj\n"); + sb.append("] >>"); return sb.toString(); } diff --git a/src/java/org/apache/fop/pdf/PDFPattern.java b/src/java/org/apache/fop/pdf/PDFPattern.java index 378b1cf8b..88a2ff492 100644 --- a/src/java/org/apache/fop/pdf/PDFPattern.java +++ b/src/java/org/apache/fop/pdf/PDFPattern.java @@ -210,13 +210,12 @@ public class PDFPattern extends PDFPathPaint { * @throws IOException if there is an error writing to the stream * @return the PDF string. */ - protected int output(OutputStream stream) throws IOException { + public int output(OutputStream stream) throws IOException { int vectorSize = 0; int tempInt = 0; byte[] buffer; StringBuffer p = new StringBuffer(64); - p.append(getObjectID()); p.append("<< \n/Type /Pattern \n"); if (this.resources != null) { @@ -323,10 +322,6 @@ public class PDFPattern extends PDFPathPaint { length += pdfStream.outputStreamData(encodedStream, stream); } - buffer = encode("\nendobj\n"); - stream.write(buffer); - length += buffer.length; - return length; } diff --git a/src/java/org/apache/fop/pdf/PDFResources.java b/src/java/org/apache/fop/pdf/PDFResources.java index 70f9af504..df8647bf4 100644 --- a/src/java/org/apache/fop/pdf/PDFResources.java +++ b/src/java/org/apache/fop/pdf/PDFResources.java @@ -192,8 +192,8 @@ public class PDFResources extends PDFDictionary { return cs; } - /** {@inheritDoc} */ - protected int output(OutputStream stream) throws IOException { + @Override + public int output(OutputStream stream) throws IOException { populateDictionary(); return super.output(stream); } diff --git a/src/java/org/apache/fop/pdf/PDFRoot.java b/src/java/org/apache/fop/pdf/PDFRoot.java index 81b93b159..e74c77478 100644 --- a/src/java/org/apache/fop/pdf/PDFRoot.java +++ b/src/java/org/apache/fop/pdf/PDFRoot.java @@ -76,7 +76,7 @@ public class PDFRoot extends PDFDictionary { } /** {@inheritDoc} */ - protected int output(OutputStream stream) throws IOException { + public int output(OutputStream stream) throws IOException { getDocument().getProfile().verifyTaggedPDF(); return super.output(stream); } diff --git a/src/java/org/apache/fop/pdf/PDFShading.java b/src/java/org/apache/fop/pdf/PDFShading.java index 2f955b850..90953968c 100644 --- a/src/java/org/apache/fop/pdf/PDFShading.java +++ b/src/java/org/apache/fop/pdf/PDFShading.java @@ -342,8 +342,7 @@ public class PDFShading extends PDFObject { int vectorSize; int tempInt; StringBuffer p = new StringBuffer(128); - p.append(getObjectID() - + "<< \n/ShadingType " + this.shadingType + " \n"); + p.append("<<\n/ShadingType " + this.shadingType + " \n"); if (this.colorSpace != null) { p.append("/ColorSpace /" + this.colorSpace.getName() + " \n"); @@ -528,7 +527,7 @@ public class PDFShading extends PDFObject { } - p.append(">> \nendobj\n"); + p.append(">>"); return (p.toString()); } diff --git a/src/java/org/apache/fop/pdf/PDFStream.java b/src/java/org/apache/fop/pdf/PDFStream.java index 5f74a2613..a0b990ec5 100644 --- a/src/java/org/apache/fop/pdf/PDFStream.java +++ b/src/java/org/apache/fop/pdf/PDFStream.java @@ -21,6 +21,7 @@ package org.apache.fop.pdf; import java.io.IOException; import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.io.Writer; /** @@ -44,16 +45,33 @@ public class PDFStream extends AbstractPDFStream { * Create an empty stream object */ public PDFStream() { - super(); + setUp(); + } + + public PDFStream(PDFDictionary dictionary) { + super(dictionary); + setUp(); + } + + public PDFStream(PDFDictionary dictionary, boolean encodeOnTheFly) { + super(dictionary, encodeOnTheFly); + setUp(); + } + + public PDFStream(boolean encodeOnTheFly) { + super(encodeOnTheFly); + setUp(); + } + + private void setUp() { try { data = StreamCacheFactory.getInstance().createStreamCache(); - this.streamWriter = new java.io.OutputStreamWriter( + this.streamWriter = new OutputStreamWriter( getBufferOutputStream(), PDFDocument.ENCODING); //Buffer to minimize calls to the converter this.streamWriter = new java.io.BufferedWriter(this.streamWriter); - } catch (IOException ex) { - //TODO throw the exception and catch it elsewhere - ex.printStackTrace(); + } catch (IOException e) { + throw new RuntimeException(e); } } @@ -136,7 +154,7 @@ public class PDFStream extends AbstractPDFStream { /** * {@inheritDoc} */ - protected int output(OutputStream stream) throws IOException { + public int output(OutputStream stream) throws IOException { final int len = super.output(stream); //Now that the data has been written, it can be discarded. diff --git a/src/java/org/apache/fop/pdf/PDFStructElem.java b/src/java/org/apache/fop/pdf/PDFStructElem.java index e35a860d9..90a41fb72 100644 --- a/src/java/org/apache/fop/pdf/PDFStructElem.java +++ b/src/java/org/apache/fop/pdf/PDFStructElem.java @@ -31,7 +31,7 @@ import org.apache.fop.util.LanguageTags; /** * Class representing a PDF Structure Element. */ -public class PDFStructElem extends PDFDictionary implements StructureTreeElement { +public class PDFStructElem extends PDFDictionary implements StructureTreeElement, CompressedObject { private PDFStructElem parentElement; diff --git a/src/java/org/apache/fop/pdf/PDFT1Stream.java b/src/java/org/apache/fop/pdf/PDFT1Stream.java index d723625eb..2bae5dca3 100644 --- a/src/java/org/apache/fop/pdf/PDFT1Stream.java +++ b/src/java/org/apache/fop/pdf/PDFT1Stream.java @@ -46,7 +46,7 @@ public class PDFT1Stream extends AbstractPDFFontStream { * byte arrays around so much * {@inheritDoc} */ - protected int output(java.io.OutputStream stream) + public int output(java.io.OutputStream stream) throws java.io.IOException { if (pfb == null) { throw new IllegalStateException("pfb must not be null at this point"); diff --git a/src/java/org/apache/fop/pdf/PDFTTFStream.java b/src/java/org/apache/fop/pdf/PDFTTFStream.java index 643ddb1e8..998e14f28 100644 --- a/src/java/org/apache/fop/pdf/PDFTTFStream.java +++ b/src/java/org/apache/fop/pdf/PDFTTFStream.java @@ -53,7 +53,7 @@ public class PDFTTFStream extends AbstractPDFFontStream { * byte arrays around so much * {@inheritDoc} */ - protected int output(java.io.OutputStream stream) + public int output(java.io.OutputStream stream) throws java.io.IOException { if (log.isDebugEnabled()) { log.debug("Writing " + origLength + " bytes of TTF font data"); diff --git a/src/java/org/apache/fop/pdf/PDFText.java b/src/java/org/apache/fop/pdf/PDFText.java index 9566f60da..3cddfe426 100644 --- a/src/java/org/apache/fop/pdf/PDFText.java +++ b/src/java/org/apache/fop/pdf/PDFText.java @@ -60,11 +60,9 @@ public class PDFText extends PDFObject { "The text of this PDFText must not be empty"); } StringBuffer sb = new StringBuffer(64); - sb.append(getObjectID()); sb.append("("); sb.append(escapeText(getText())); sb.append(")"); - sb.append("\nendobj\n"); return sb.toString(); } diff --git a/src/java/org/apache/fop/pdf/PDFUri.java b/src/java/org/apache/fop/pdf/PDFUri.java index a6124ec03..b3d377ff1 100644 --- a/src/java/org/apache/fop/pdf/PDFUri.java +++ b/src/java/org/apache/fop/pdf/PDFUri.java @@ -55,8 +55,7 @@ public class PDFUri extends PDFAction { /** {@inheritDoc} */ public String toPDFString() { //TODO Convert this class into a dictionary - return getObjectID() + getDictString() + "\nendobj\n"; - //throw new UnsupportedOperationException("This method should not be called"); + return getDictString(); } } diff --git a/src/java/org/apache/fop/pdf/PDFXObject.java b/src/java/org/apache/fop/pdf/PDFXObject.java index d1ce6d75e..c2b702650 100644 --- a/src/java/org/apache/fop/pdf/PDFXObject.java +++ b/src/java/org/apache/fop/pdf/PDFXObject.java @@ -41,6 +41,10 @@ public abstract class PDFXObject extends AbstractPDFStream { super(); } + protected PDFXObject(PDFDictionary dictionary) { + super(dictionary); + } + /** * Returns the XObject's name. * @return the name of the XObject diff --git a/src/java/org/apache/fop/pdf/Version.java b/src/java/org/apache/fop/pdf/Version.java index 0df63d312..4bdc7a1b4 100644 --- a/src/java/org/apache/fop/pdf/Version.java +++ b/src/java/org/apache/fop/pdf/Version.java @@ -48,12 +48,13 @@ public enum Version { } /** - * Given the PDF version as a String, returns the corresponding enumerated type. The String - * should be in the format "1.x" for PDF v1.x. + * Given the PDF version as a String, returns the corresponding enumerated type. The + * String should be in the format "1.x" for PDF v1.x. * * @param version a version number * @return the corresponding Version instance - * @throws IllegalArgumentException if the argument does not correspond to any existing PDF version + * @throws IllegalArgumentException if the argument does not correspond to any + * existing PDF version */ public static Version getValueOf(String version) { for (Version pdfVersion : Version.values()) { diff --git a/src/java/org/apache/fop/pdf/xref/CompressedObjectReference.java b/src/java/org/apache/fop/pdf/xref/CompressedObjectReference.java new file mode 100644 index 000000000..eb619fcc6 --- /dev/null +++ b/src/java/org/apache/fop/pdf/xref/CompressedObjectReference.java @@ -0,0 +1,66 @@ +/* + * 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.xref; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * A reference to an indirect object stored in an object stream. Contains the relevant + * information to add to a cross-reference stream. + */ +public class CompressedObjectReference implements ObjectReference { + + private final int objectNumber; + + private final int objectStreamNumber; + + private final int index; + + /** + * Creates a new reference. + * + * @param objectNumber the number of the compressed object being referenced + * @param objectStreamNumber the number of the object stream in which the compressed + * object is to be found + * @param index the index of the compressed object in the object stream + */ + public CompressedObjectReference(int objectNumber, int objectStreamNumber, int index) { + this.objectNumber = objectNumber; + this.objectStreamNumber = objectStreamNumber; + this.index = index; + } + + public void output(DataOutputStream out) throws IOException { + out.write(2); + out.writeLong(objectStreamNumber); + out.write(0); + out.write(index); + } + + public int getObjectNumber() { + return objectNumber; + } + + public int getObjectStreamNumber() { + return objectStreamNumber; + } + +} diff --git a/src/java/org/apache/fop/pdf/xref/CrossReferenceObject.java b/src/java/org/apache/fop/pdf/xref/CrossReferenceObject.java new file mode 100644 index 000000000..3db50eca9 --- /dev/null +++ b/src/java/org/apache/fop/pdf/xref/CrossReferenceObject.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.pdf.xref; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * A representation of the cross-reference data to be output at the end of a PDF file. + */ +public abstract class CrossReferenceObject { + + protected final TrailerDictionary trailerDictionary; + + protected final long startxref; + + CrossReferenceObject(TrailerDictionary trailerDictionary, long startxref) { + this.trailerDictionary = trailerDictionary; + this.startxref = startxref; + } + + /** + * Writes the cross reference data to a PDF stream + * + * @param stream the stream to write the cross reference to + * @throws IOException if an I/O exception occurs while writing the data + */ + public abstract void output(OutputStream stream) throws IOException; +} diff --git a/src/java/org/apache/fop/pdf/xref/CrossReferenceStream.java b/src/java/org/apache/fop/pdf/xref/CrossReferenceStream.java new file mode 100644 index 000000000..32c31573f --- /dev/null +++ b/src/java/org/apache/fop/pdf/xref/CrossReferenceStream.java @@ -0,0 +1,107 @@ +/* + * 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.xref; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.fop.pdf.PDFArray; +import org.apache.fop.pdf.PDFDictionary; +import org.apache.fop.pdf.PDFDocument; +import org.apache.fop.pdf.PDFFilterList; +import org.apache.fop.pdf.PDFName; +import org.apache.fop.pdf.PDFStream; + +/** + * A cross-reference stream, as described in Section 3.4.7 of the PDF 1.5 Reference. + */ +public class CrossReferenceStream extends CrossReferenceObject { + + private static final PDFName XREF = new PDFName("XRef"); + + private final PDFDocument document; + + private final int objectNumber; + + private final List objectReferences; + + public CrossReferenceStream(PDFDocument document, + int objectNumber, + TrailerDictionary trailerDictionary, + long startxref, + List uncompressedObjectReferences, + List compressedObjectReferences) { + super(trailerDictionary, startxref); + this.document = document; + this.objectNumber = objectNumber; + this.objectReferences = new ArrayList(uncompressedObjectReferences.size()); + for (Long offset : uncompressedObjectReferences) { + objectReferences.add(offset == null ? null : new UncompressedObjectReference(offset)); + } + for (CompressedObjectReference ref : compressedObjectReferences) { + this.objectReferences.set(ref.getObjectNumber() - 1, ref); + } + } + + /** {@inheritDoc} */ + public void output(OutputStream stream) throws IOException { + populateDictionary(); + PDFStream helperStream = new PDFStream(trailerDictionary.getDictionary(), false) { + + @Override + protected void setupFilterList() { + PDFFilterList filterList = getFilterList(); + assert !filterList.isInitialized(); + filterList.addDefaultFilters(document.getFilterMap(), getDefaultFilterName()); + } + + }; + helperStream.setObjectNumber(objectNumber); + helperStream.setDocument(document); + ByteArrayOutputStream byteArray = new ByteArrayOutputStream(); + DataOutputStream data = new DataOutputStream(byteArray); + addFreeEntryForObject0(data); + for (ObjectReference objectReference : objectReferences) { + assert objectReference != null; + objectReference.output(data); + } + new UncompressedObjectReference(startxref).output(data); + data.close(); + helperStream.setData(byteArray.toByteArray()); + PDFDocument.outputIndirectObject(helperStream, stream); + } + + private void populateDictionary() throws IOException { + int objectCount = objectReferences.size() + 1; + PDFDictionary dictionary = trailerDictionary.getDictionary(); + dictionary.put("/Type", XREF); + dictionary.put("/Size", objectCount + 1); + dictionary.put("/W", new PDFArray(1, 8, 2)); + } + + private void addFreeEntryForObject0(DataOutputStream data) throws IOException { + data.write(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xff, (byte) 0xff}); + } + +} diff --git a/src/java/org/apache/fop/pdf/xref/CrossReferenceTable.java b/src/java/org/apache/fop/pdf/xref/CrossReferenceTable.java new file mode 100644 index 000000000..41a1146ca --- /dev/null +++ b/src/java/org/apache/fop/pdf/xref/CrossReferenceTable.java @@ -0,0 +1,73 @@ +/* + * 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.xref; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; + +import org.apache.fop.pdf.PDFDictionary; +import org.apache.fop.pdf.PDFDocument; + +/** + * A cross-reference table, as described in Section 3.4.3 of the PDF 1.5 Reference. + */ +public class CrossReferenceTable extends CrossReferenceObject { + + private final List objectReferences; + + private final StringBuilder pdf = new StringBuilder(256); + + public CrossReferenceTable(TrailerDictionary trailerDictionary, long startxref, + List location) { + super(trailerDictionary, startxref); + this.objectReferences = location; + } + + public void output(OutputStream stream) throws IOException { + outputXref(); + writeTrailer(stream); + } + + private void outputXref() throws IOException { + pdf.append("xref\n0 "); + pdf.append(objectReferences.size() + 1); + pdf.append("\n0000000000 65535 f \n"); + for (Long objectReference : objectReferences) { + final String padding = "0000000000"; + String s = String.valueOf(objectReference); + if (s.length() > 10) { + throw new IOException("PDF file too large." + + " PDF 1.4 cannot grow beyond approx. 9.3GB."); + } + String loc = padding.substring(s.length()) + s; + pdf.append(loc).append(" 00000 n \n"); + } + } + + private void writeTrailer(OutputStream stream) throws IOException { + pdf.append("trailer\n"); + stream.write(PDFDocument.encode(pdf.toString())); + PDFDictionary dictionary = trailerDictionary.getDictionary(); + dictionary.put("/Size", objectReferences.size() + 1); + dictionary.output(stream); + } + +} diff --git a/src/java/org/apache/fop/pdf/xref/ObjectReference.java b/src/java/org/apache/fop/pdf/xref/ObjectReference.java new file mode 100644 index 000000000..894a66ce6 --- /dev/null +++ b/src/java/org/apache/fop/pdf/xref/ObjectReference.java @@ -0,0 +1,39 @@ +/* + * 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.xref; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * A reference to an indirect object. + */ +interface ObjectReference { + + /** + * Outputs this reference to the given stream, in the cross-reference stream format. + * For example, a object may output the bytes 01 00 00 00 00 00 00 01 ff 00 to + * indicate a non-compressed object (01), at offset 511 from the beginning of the file + * (00 00 00 00 00 00 01 ff), of generation number 0 (00). + * + * @param out the stream to which to output the reference + */ + void output(DataOutputStream out) throws IOException; +} diff --git a/src/java/org/apache/fop/pdf/xref/TrailerDictionary.java b/src/java/org/apache/fop/pdf/xref/TrailerDictionary.java new file mode 100644 index 000000000..d5d62522f --- /dev/null +++ b/src/java/org/apache/fop/pdf/xref/TrailerDictionary.java @@ -0,0 +1,94 @@ +/* + * 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.xref; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.fop.pdf.PDFArray; +import org.apache.fop.pdf.PDFDictionary; +import org.apache.fop.pdf.PDFDocument; +import org.apache.fop.pdf.PDFEncryption; +import org.apache.fop.pdf.PDFInfo; +import org.apache.fop.pdf.PDFRoot; +import org.apache.fop.pdf.PDFText; +import org.apache.fop.pdf.PDFWritable; + +/** + * A data class representing entries of the file trailer dictionary. + */ +public class TrailerDictionary { + + private final PDFDictionary dictionary; + + public TrailerDictionary(PDFDocument pdfDocument) { + this.dictionary = new PDFDictionary(); + this.dictionary.setDocument(pdfDocument); + } + + /** Sets the value of the Root entry. */ + public TrailerDictionary setRoot(PDFRoot root) { + dictionary.put("/Root", root); + return this; + } + + /** Sets the value of the Info entry. */ + public TrailerDictionary setInfo(PDFInfo info) { + dictionary.put("/Info", info); + return this; + } + + /** Sets the value of the Encrypt entry. */ + public TrailerDictionary setEncryption(PDFEncryption encryption) { + dictionary.put("/Encrypt", encryption); + return this; + } + + /** Sets the value of the ID entry. */ + public TrailerDictionary setFileID(byte[] originalFileID, byte[] updatedFileID) { + // TODO this is ugly! Used to circumvent the fact that the file ID will be + // encrypted if directly stored as a byte array + class FileID implements PDFWritable { + + private final byte[] fileID; + + FileID(byte[] id) { + fileID = id; + } + + public void outputInline(OutputStream out, StringBuilder textBuffer) + throws IOException { + PDFDocument.flushTextBuffer(textBuffer, out); + String hex = PDFText.toHex(fileID, true); + byte[] encoded = hex.getBytes("US-ASCII"); + out.write(encoded); + } + + } + PDFArray fileID = new PDFArray(new FileID(originalFileID), new FileID(updatedFileID)); + dictionary.put("/ID", fileID); + return this; + } + + PDFDictionary getDictionary() { + return dictionary; + } + +} diff --git a/src/java/org/apache/fop/pdf/xref/UncompressedObjectReference.java b/src/java/org/apache/fop/pdf/xref/UncompressedObjectReference.java new file mode 100644 index 000000000..a54ec62e7 --- /dev/null +++ b/src/java/org/apache/fop/pdf/xref/UncompressedObjectReference.java @@ -0,0 +1,48 @@ +/* + * 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.xref; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * A reference to an indirect object that is not stored in an object stream. + */ +class UncompressedObjectReference implements ObjectReference { + + final long offset; + + /** + * Creates a new reference. + * + * @param offset offset of the object from the beginning of the PDF file + */ + UncompressedObjectReference(long offset) { + this.offset = offset; + } + + public void output(DataOutputStream out) throws IOException { + out.write(1); + out.writeLong(offset); + out.write(0); + out.write(0); + } + +} diff --git a/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java b/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java index 88a6e9c22..1c9f9b49d 100644 --- a/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java @@ -95,15 +95,15 @@ class PDFLogicalStructureHandler { */ PDFLogicalStructureHandler(PDFDocument pdfDoc) { this.pdfDoc = pdfDoc; - PDFStructTreeRoot structTreeRoot = pdfDoc.getFactory().makeStructTreeRoot(parentTree); - rootStructureElement = pdfDoc.getFactory().makeStructureElement( + PDFStructTreeRoot structTreeRoot = pdfDoc.makeStructTreeRoot(parentTree); + rootStructureElement = pdfDoc.makeStructureElement( FOToPDFRoleMap.mapFormattingObject("root", structTreeRoot), structTreeRoot); structTreeRoot.addKid(rootStructureElement); } PDFStructElem createPageSequence(Locale language) { - PDFStructElem structElemPart = pdfDoc.getFactory().makeStructureElement( + PDFStructElem structElemPart = pdfDoc.makeStructureElement( FOToPDFRoleMap.mapFormattingObject("page-sequence", rootStructureElement), rootStructureElement); rootStructureElement.addKid(structElemPart); diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java index 83f6ccab6..53d259677 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java @@ -385,8 +385,8 @@ class PDFRenderingUtil implements PDFConfigurationConstants { if (maxPDFVersion == null) { this.pdfDoc = new PDFDocument(producer); } else { - VersionController controller = - VersionController.getFixedVersionController(maxPDFVersion); + VersionController controller + = VersionController.getFixedVersionController(maxPDFVersion); this.pdfDoc = new PDFDocument(producer, controller); } updateInfo(); @@ -411,6 +411,9 @@ class PDFRenderingUtil implements PDFConfigurationConstants { log.debug("PDF/A is active. Conformance Level: " + pdfAMode); addPDFA1OutputIntent(); } + + this.pdfDoc.enableAccessibility(userAgent.isAccessibilityEnabled()); + return this.pdfDoc; } diff --git a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java index 11eba4ea4..2a2a4a392 100644 --- a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java +++ b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java @@ -65,7 +65,7 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler { PDFStructElem parent = ancestors.getFirst(); String role = attributes.getValue("role"); PDFStructElem created; - created = pdfFactory.makeStructureElement( + created = pdfFactory.getDocument().makeStructureElement( FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster), parent); parent.addKid(created); ancestors.addFirst(created); @@ -84,7 +84,7 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler { PDFStructElem parent = ancestors.getFirst(); String role = attributes.getValue("role"); PDFStructElem created; - created = pdfFactory.makeStructureElement( + created = pdfFactory.getDocument().makeStructureElement( FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster), parent); parent.addKid(created); String altTextNode = attributes.getValue(ExtensionElementMapping.URI, "alt-text"); @@ -104,7 +104,7 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler { if ("#PCDATA".equals(name)) { created = new PDFStructElem.Placeholder(parent, name); } else { - created = pdfFactory.makeStructureElement( + created = pdfFactory.getDocument().makeStructureElement( FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster), parent); } diff --git a/test/accessibility/README b/test/accessibility/README deleted file mode 100644 index 1d2e04361..000000000 --- a/test/accessibility/README +++ /dev/null @@ -1,41 +0,0 @@ -This directory contains sample FO files for testing the accessibility features -of FOP. - -Every FO file in this directory has a corresponding PDF file in the pdf/ -sub-directory. The fop.xconf configuration file was used. - -The PDF files have been checked with Adobe Acrobat Professional 9, using both -the full accessibility checker and the read-aloud feature. The checker reports -no error /and/ the entire document can be read aloud. - - -!! DO NOT MODIFY THOSE FILES, NEITHER THE FO NOR THE PDF !! - - -... Or at least, know what you are doing -If the FO files are modified, the resulting PDFs must be checked again, both -with the checker and the read-aloud feature. (Sometimes the checker reports no -problem yet part or all of the document cannot be read aloud.) - -The purpose of this infrastructure is to be able to quickly re-test the -accessibility processing chain when any change has been made to it. The -configuration file disables the compression of the PDF streams, so it is -possible to compare a re-generated PDF with the original one by using a simple -diff tool. The files will not be identical because of the different creation -dates (and the ID key in the trailer), but apart from that there should be no -difference. - -The rationale is that using a diff tool is much quicker and less tedious than -running Acrobat's accessibility checker and read-aloud feature every time. - - -To re-generate the PDF files: - ../../fop -c fop.xconf text_1.fo pdf/text_1.new.pdf - diff pdf/text_1_painter.pdf pdf/text_1.new.pdf -Or, going through the intermediate format: - ../../fop -c fop.xconf text_1.fo -if application/pdf text_1_if.xml - ../../fop -c fop.xconf -ifin text_1_if.xml pdf/text_1.new.pdf - diff pdf/text_1.pdf pdf/text_1.new.pdf - - -$Id$ diff --git a/test/accessibility/background-image_jpg_repeat.fo b/test/accessibility/background-image_jpg_repeat.fo deleted file mode 100644 index 727162e49..000000000 --- a/test/accessibility/background-image_jpg_repeat.fo +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL - formatting objects (XSL-FO) and an output independent formatter. It is a Java application - that reads a formatting object (FO) tree and renders the resulting pages to a specified - output. - - - diff --git a/test/accessibility/background-image_jpg_single.fo b/test/accessibility/background-image_jpg_single.fo deleted file mode 100644 index 335353e03..000000000 --- a/test/accessibility/background-image_jpg_single.fo +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL - formatting objects (XSL-FO) and an output independent formatter. It is a Java application - that reads a formatting object (FO) tree and renders the resulting pages to a specified - output. - - - diff --git a/test/accessibility/background-image_png_repeat.fo b/test/accessibility/background-image_png_repeat.fo deleted file mode 100644 index 5e4a8ba0d..000000000 --- a/test/accessibility/background-image_png_repeat.fo +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL - formatting objects (XSL-FO) and an output independent formatter. It is a Java application - that reads a formatting object (FO) tree and renders the resulting pages to a specified - output. - - - diff --git a/test/accessibility/background-image_png_single.fo b/test/accessibility/background-image_png_single.fo deleted file mode 100644 index 90067ec53..000000000 --- a/test/accessibility/background-image_png_single.fo +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL - formatting objects (XSL-FO) and an output independent formatter. It is a Java application - that reads a formatting object (FO) tree and renders the resulting pages to a specified - output. - - - diff --git a/test/accessibility/background-image_svg_repeat.fo b/test/accessibility/background-image_svg_repeat.fo deleted file mode 100644 index 02520b6cf..000000000 --- a/test/accessibility/background-image_svg_repeat.fo +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL - formatting objects (XSL-FO) and an output independent formatter. It is a Java application - that reads a formatting object (FO) tree and renders the resulting pages to a specified - output. - - - diff --git a/test/accessibility/background-image_svg_single.fo b/test/accessibility/background-image_svg_single.fo deleted file mode 100644 index 3029f32ec..000000000 --- a/test/accessibility/background-image_svg_single.fo +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL - formatting objects (XSL-FO) and an output independent formatter. It is a Java application - that reads a formatting object (FO) tree and renders the resulting pages to a specified - output. - - - diff --git a/test/accessibility/complete.fo b/test/accessibility/complete.fo deleted file mode 100644 index 75684750c..000000000 --- a/test/accessibility/complete.fo +++ /dev/null @@ -1,207 +0,0 @@ - - - - - - - - - - - - - This is the page headerPage - - - - - - - (There’s another page sequence below.) - About Apache FOP - It is a print formatter driven by XSL formatting objects (XSL-FO) and an output - independent formatter1See the FOP - website for more - information. FOP has a nice logo: - - - - - - Header 1.1 - - - Header 1.2 - - - - - - - Cell 1.1 - - - Cell 1.2 - - - - - Cell 2.1 - - - Cell 2.2 - - - - - Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL - formatting objects (XSL-FO) and an output independent formatter. It is a Java application - that reads a formatting object (FO) tree and renders the resulting pages to a specified - output. - This fo:block element spans all the columns of the - document. This is intended to test the abilities of the text-to-speech program. - And now we are back to normal content flowing in two columns. Let’s start a numbered - list: - - - - 1. - - - - Line 1 of item 1 - Line 2 of item 1 - Line 3 of item 1 - - - - - - 2. - - - - Line 1 of item 2 - Line 2 of item 2 - Line 3 of item 2 - - - - - And now we are going to see how a second page sequence is handled. - - - - - This is the page headerPage - - - - - - - Apache FOP (Formatting Objects Processor) is a print formatter driven by - XSL formatting objects (XSL-FO) and an output independent formatter1See the - FOP - website for more - information. It is a Java application that - reads a formatting object (FO) tree and renders the resulting pages to a specified - output. - - - - - Header 1.1 - - - Header 1.2 - - - - - - - Cell 1.1 - - - Cell 1.2 - - - - - Cell 2.1 - - - Cell 2.2 - - - - - Apache FOP (Formatting Objects Processor) est une - application de mise en page de documents respectant le standard XSL-FO. À partir d’un - document au format XSL-FO, cette application écrite en Java effectue une mise en page et - renvoie un document prêt pour impression. - This fo:block element spans all the columns of the - document. This is intended to test the abilities of the text-to-speech program. - And now we are back to normal content flowing in two columns. Let’s start a numbered - list: - - - - 1. - - - - Line 1 of item 1 - Line 2 of item 1 - Line 3 of item 1 - - - - - - 2. - - - - Line 1 of item 2 - Line 2 of item 2 - Line 3 of item 2 - - - - - The end of the document has now been reached. - - - diff --git a/test/accessibility/fop.xconf b/test/accessibility/fop.xconf deleted file mode 100644 index 8c5dc2bd5..000000000 --- a/test/accessibility/fop.xconf +++ /dev/null @@ -1,23 +0,0 @@ - - - true - 144 - false - ../resources/fonts/ - - - - null - - - flate - ascii-85 - - - - - - - - - diff --git a/test/accessibility/image_jpg.fo b/test/accessibility/image_jpg.fo deleted file mode 100644 index 5fe36f61a..000000000 --- a/test/accessibility/image_jpg.fo +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - This document contains an image in the JPEG format: . Here is the end of the text. - - - diff --git a/test/accessibility/image_png.fo b/test/accessibility/image_png.fo deleted file mode 100644 index b529aa8c8..000000000 --- a/test/accessibility/image_png.fo +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - This document contains an image in the PNG format: . Here is the end of the text. - - - diff --git a/test/accessibility/image_svg.fo b/test/accessibility/image_svg.fo deleted file mode 100644 index bbc77fe65..000000000 --- a/test/accessibility/image_svg.fo +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - This document contains an image in the SVG format: . And here is the same image as an instream-foreign-object: - - - - - - - - - . - - - diff --git a/test/accessibility/image_wmf.fo b/test/accessibility/image_wmf.fo deleted file mode 100644 index 1a4de777b..000000000 --- a/test/accessibility/image_wmf.fo +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - This document contains an image in the WMF format: Here is the end of the text. - - - diff --git a/test/accessibility/leader.fo b/test/accessibility/leader.fo deleted file mode 100644 index ffd768021..000000000 --- a/test/accessibility/leader.fo +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - This is a text followed by a leader with leader-pattern=​"use-content", the - content being text: • - 1 - This is a text followed by a leader with - leader-pattern=​"use-content", the content being images:1 - - - diff --git a/test/accessibility/links.fo b/test/accessibility/links.fo deleted file mode 100644 index 36250e332..000000000 --- a/test/accessibility/links.fo +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - This is a link to the next - paragraph. - Apache FOP (Formatting Objects Processor) is a print formatter driven by - XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java - application that reads a formatting object (FO) tree and renders the resulting pages to a - specified output. - For more information, see the FOP - website. - - - diff --git a/test/accessibility/pdf/background-image_jpg_repeat.pdf b/test/accessibility/pdf/background-image_jpg_repeat.pdf deleted file mode 100644 index 221761757..000000000 Binary files a/test/accessibility/pdf/background-image_jpg_repeat.pdf and /dev/null differ diff --git a/test/accessibility/pdf/background-image_jpg_single.pdf b/test/accessibility/pdf/background-image_jpg_single.pdf deleted file mode 100644 index 232afdd05..000000000 Binary files a/test/accessibility/pdf/background-image_jpg_single.pdf and /dev/null differ diff --git a/test/accessibility/pdf/background-image_png_repeat.pdf b/test/accessibility/pdf/background-image_png_repeat.pdf deleted file mode 100644 index 52da4020a..000000000 Binary files a/test/accessibility/pdf/background-image_png_repeat.pdf and /dev/null differ diff --git a/test/accessibility/pdf/background-image_png_single.pdf b/test/accessibility/pdf/background-image_png_single.pdf deleted file mode 100644 index 356e4885c..000000000 Binary files a/test/accessibility/pdf/background-image_png_single.pdf and /dev/null differ diff --git a/test/accessibility/pdf/background-image_svg_repeat.pdf b/test/accessibility/pdf/background-image_svg_repeat.pdf deleted file mode 100644 index 0bce2404d..000000000 Binary files a/test/accessibility/pdf/background-image_svg_repeat.pdf and /dev/null differ diff --git a/test/accessibility/pdf/background-image_svg_single.pdf b/test/accessibility/pdf/background-image_svg_single.pdf deleted file mode 100644 index 3e4afcdcf..000000000 Binary files a/test/accessibility/pdf/background-image_svg_single.pdf and /dev/null differ diff --git a/test/accessibility/pdf/complete.pdf b/test/accessibility/pdf/complete.pdf deleted file mode 100644 index cffb9e2cb..000000000 Binary files a/test/accessibility/pdf/complete.pdf and /dev/null differ diff --git a/test/accessibility/pdf/image_jpg.pdf b/test/accessibility/pdf/image_jpg.pdf deleted file mode 100644 index cb004bbd2..000000000 Binary files a/test/accessibility/pdf/image_jpg.pdf and /dev/null differ diff --git a/test/accessibility/pdf/image_png.pdf b/test/accessibility/pdf/image_png.pdf deleted file mode 100644 index c3289374f..000000000 Binary files a/test/accessibility/pdf/image_png.pdf and /dev/null differ diff --git a/test/accessibility/pdf/image_svg.pdf b/test/accessibility/pdf/image_svg.pdf deleted file mode 100644 index c3fce5b0e..000000000 Binary files a/test/accessibility/pdf/image_svg.pdf and /dev/null differ diff --git a/test/accessibility/pdf/image_wmf.pdf b/test/accessibility/pdf/image_wmf.pdf deleted file mode 100644 index b9ec8c55a..000000000 Binary files a/test/accessibility/pdf/image_wmf.pdf and /dev/null differ diff --git a/test/accessibility/pdf/leader.pdf b/test/accessibility/pdf/leader.pdf deleted file mode 100644 index c7432e751..000000000 Binary files a/test/accessibility/pdf/leader.pdf and /dev/null differ diff --git a/test/accessibility/pdf/links.pdf b/test/accessibility/pdf/links.pdf deleted file mode 100644 index 91d7c2592..000000000 Binary files a/test/accessibility/pdf/links.pdf and /dev/null differ diff --git a/test/accessibility/pdf/role.pdf b/test/accessibility/pdf/role.pdf deleted file mode 100644 index acb435027..000000000 Binary files a/test/accessibility/pdf/role.pdf and /dev/null differ diff --git a/test/accessibility/pdf/role_non-standard.pdf b/test/accessibility/pdf/role_non-standard.pdf deleted file mode 100644 index fcf614ed1..000000000 Binary files a/test/accessibility/pdf/role_non-standard.pdf and /dev/null differ diff --git a/test/accessibility/pdf/text_1.pdf b/test/accessibility/pdf/text_1.pdf deleted file mode 100644 index 596419c9d..000000000 Binary files a/test/accessibility/pdf/text_1.pdf and /dev/null differ diff --git a/test/accessibility/pdf/text_2.pdf b/test/accessibility/pdf/text_2.pdf deleted file mode 100644 index 19fff21a4..000000000 Binary files a/test/accessibility/pdf/text_2.pdf and /dev/null differ diff --git a/test/accessibility/pdf/text_font-embedding.pdf b/test/accessibility/pdf/text_font-embedding.pdf deleted file mode 100644 index 0288449d5..000000000 Binary files a/test/accessibility/pdf/text_font-embedding.pdf and /dev/null differ diff --git a/test/accessibility/role.fo b/test/accessibility/role.fo deleted file mode 100644 index ced8a4d44..000000000 --- a/test/accessibility/role.fo +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - - - - - - - Title 1: To Start With - Title 2: A Sub-Title - Apache FOP (Formatting Objects Processor) is a print formatter - driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java - application that reads a formatting object (FO) tree and renders the resulting pages to a - specified output. - Title 2: Another Sub-Title - Apache FOP (Formatting Objects Processor) is a print formatter - driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java - application that reads a formatting object (FO) tree and renders the resulting pages to a - specified output. - Title 1: Second Title - Title 2: A Sample Table - See data below: - - - - - Header 1 - - - Header 2 - - - - - - - Footer 1 - - - Footer 2 - - - - - - - Cell 1.1 - - - Cell 1.2 - - - - - Cell 2.1 - - - Cell 2.2 - - - - - That’s all folks. - - - diff --git a/test/accessibility/role_non-standard.fo b/test/accessibility/role_non-standard.fo deleted file mode 100644 index d3e1a9852..000000000 --- a/test/accessibility/role_non-standard.fo +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - - - - - - - Title 1: To Start With - A Sub-Title With a Non-Standard Role - Apache FOP (Formatting Objects Processor) is a print formatter - driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java - application that reads a formatting object (FO) tree and renders the resulting pages to a - specified output. - Title 2: Another Sub-Title - Apache FOP (Formatting Objects Processor) is a print formatter - driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java - application that reads a formatting object (FO) tree and renders the resulting pages to a - specified output. - Title 1: Second Title - Title 2: A Sample Table - See data below: - - - - - Header 1 - - - Header 2 - - - - - - - Footer 1 - - - Footer 2 - - - - - - - Cell 1.1 - - - Cell 1.2 - - - - - Cell 2.1 - - - Cell 2.2 - - - - - That’s all folks. - - - diff --git a/test/accessibility/text_1.fo b/test/accessibility/text_1.fo deleted file mode 100644 index 31ad31514..000000000 --- a/test/accessibility/text_1.fo +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL - formatting objects (XSL-FO) and an output independent formatter. It is a Java application - that reads a formatting object (FO) tree and renders the resulting pages to a specified - output. - - - diff --git a/test/accessibility/text_2.fo b/test/accessibility/text_2.fo deleted file mode 100644 index f5693110e..000000000 --- a/test/accessibility/text_2.fo +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL - formatting objects (XSL-FO) and an output independent formatter. It is a Java application - that reads a formatting object (FO) tree and renders the resulting pages to a specified - output. - Apache FOP (Formatting Objects Processor) est une - application de mise en page de documents respectant le standard XSL-FO. À partir d’un - document au format XSL-FO, cette application écrite en Java effectue une mise en page et - renvoie un document prêt pour impression. - Back to English and let’s say it again: Apache FOP (Formatting Objects Processor) is - a print formatter driven by XSL formatting objects (XSL-FO) and an output independent - formatter. - - - diff --git a/test/accessibility/text_font-embedding.fo b/test/accessibility/text_font-embedding.fo deleted file mode 100644 index 10c1c99d9..000000000 --- a/test/accessibility/text_font-embedding.fo +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL - formatting objects (XSL-FO) and an output independent formatter. It is a Java application - that reads a formatting object (FO) tree and renders the resulting pages to a specified - output. - - - diff --git a/test/java/org/apache/fop/StandardTestSuite.java b/test/java/org/apache/fop/StandardTestSuite.java index eecdeb671..8649fdfa8 100644 --- a/test/java/org/apache/fop/StandardTestSuite.java +++ b/test/java/org/apache/fop/StandardTestSuite.java @@ -34,9 +34,10 @@ import org.apache.fop.fonts.type1.AdobeStandardEncodingTestCase; import org.apache.fop.image.loader.batik.ImageLoaderTestCase; import org.apache.fop.image.loader.batik.ImagePreloaderTestCase; import org.apache.fop.intermediate.IFMimickingTestCase; +import org.apache.fop.layoutmgr.PageSequenceLayoutManagerTestCase; +import org.apache.fop.pdf.PDFLibraryTestSuite; import org.apache.fop.render.extensions.prepress.PageBoundariesTestCase; import org.apache.fop.render.extensions.prepress.PageScaleTestCase; -import org.apache.fop.layoutmgr.PageSequenceLayoutManagerTestCase; import org.apache.fop.render.pdf.PDFAConformanceTestCase; import org.apache.fop.render.pdf.PDFCMapTestCase; import org.apache.fop.render.pdf.PDFEncodingTestCase; @@ -45,7 +46,6 @@ 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. diff --git a/test/java/org/apache/fop/pdf/AbstractPDFStreamTestCase.java b/test/java/org/apache/fop/pdf/AbstractPDFStreamTestCase.java index b930a8b6d..95d5c0a1d 100644 --- a/test/java/org/apache/fop/pdf/AbstractPDFStreamTestCase.java +++ b/test/java/org/apache/fop/pdf/AbstractPDFStreamTestCase.java @@ -48,10 +48,10 @@ public class AbstractPDFStreamTestCase extends PDFObjectTestCase { 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"; + private String startStream = "<< /Length 5 0 R /Filter /FlateDecode >>\n" + + "stream\n"; + + private String endStream = "endstream"; @Before public void setUp() { diff --git a/test/java/org/apache/fop/pdf/ObjectStreamManagerTestCase.java b/test/java/org/apache/fop/pdf/ObjectStreamManagerTestCase.java new file mode 100644 index 000000000..89d980029 --- /dev/null +++ b/test/java/org/apache/fop/pdf/ObjectStreamManagerTestCase.java @@ -0,0 +1,113 @@ +/* + * 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.assertTrue; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; + +import org.junit.Test; + +import org.apache.fop.pdf.xref.CompressedObjectReference; + +public class ObjectStreamManagerTestCase { + + private List compressedObjectReferences; + + private MockPdfDocument pdfDocument; + + @Test + public void add() { + final int expectedCapacity = 100; + final int numCompressedObjects = expectedCapacity * 2 + 1; + createCompressObjectReferences(numCompressedObjects); + assertEquals(numCompressedObjects, compressedObjectReferences.size()); + int objectStreamNumber1 = assertSameObjectStream(0, expectedCapacity); + int objectStreamNumber2 = assertSameObjectStream(expectedCapacity, expectedCapacity * 2); + int objectStreamNumber3 = assertSameObjectStream(expectedCapacity * 2, numCompressedObjects); + assertDifferent(objectStreamNumber1, objectStreamNumber2, objectStreamNumber3); + assertEquals(objectStreamNumber3, pdfDocument.previous.getObjectNumber()); + } + + private void createCompressObjectReferences(int numObjects) { + pdfDocument = new MockPdfDocument(); + ObjectStreamManager sut = new ObjectStreamManager(pdfDocument); + for (int obNum = 1; obNum <= numObjects; obNum++) { + sut.add(createCompressedObject(obNum)); + } + compressedObjectReferences = sut.getCompressedObjectReferences(); + } + + private static class MockPdfDocument extends PDFDocument { + + private ObjectStream previous; + + public MockPdfDocument() { + super(""); + } + + public void assignObjectNumber(PDFObject obj) { + super.assignObjectNumber(obj); + if (obj instanceof ObjectStream) { + ObjectStream objStream = (ObjectStream) obj; + ObjectStream previous = (ObjectStream) objStream.get("Extends"); + if (previous == null) { + assertEquals(this.previous, previous); + } + this.previous = objStream; + } + } + } + + private CompressedObject createCompressedObject(final int objectNumber) { + return new CompressedObject() { + + public int getObjectNumber() { + return objectNumber; + } + + public int output(OutputStream outputStream) throws IOException { + throw new UnsupportedOperationException(); + } + }; + } + + private int assertSameObjectStream(int from, int to) { + int objectStreamNumber = getObjectStreamNumber(from); + for (int i = from + 1; i < to; i++) { + assertEquals(objectStreamNumber, getObjectStreamNumber(i)); + } + return objectStreamNumber; + } + + private int getObjectStreamNumber(int index) { + return compressedObjectReferences.get(index).getObjectStreamNumber(); + } + + private void assertDifferent(int objectStreamNumber1, int objectStreamNumber2, + int objectStreamNumber3) { + assertTrue(objectStreamNumber1 != objectStreamNumber2); + assertTrue(objectStreamNumber1 != objectStreamNumber3); + assertTrue(objectStreamNumber2 != objectStreamNumber3); + } +} diff --git a/test/java/org/apache/fop/pdf/ObjectStreamTestCase.java b/test/java/org/apache/fop/pdf/ObjectStreamTestCase.java new file mode 100644 index 000000000..317828e4b --- /dev/null +++ b/test/java/org/apache/fop/pdf/ObjectStreamTestCase.java @@ -0,0 +1,131 @@ +/* + * 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 java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +public class ObjectStreamTestCase { + + private static final String OBJECT_CONTENT = "<<\n /Foo True\n /Bar False\n>>\n"; + + private PDFDocument pdfDocument; + + private ObjectStream objectStream; + + private List compressedObjects; + + @Before + public void setUp() throws Exception { + pdfDocument = new PDFDocument("PDFObjectStreamTestCase"); + objectStream = new ObjectStream(); + pdfDocument.assignObjectNumber(objectStream); + compressedObjects = Arrays.asList(new MockCompressedObject(), new MockCompressedObject()); + } + + @Test + public void testSingleObjectStream() throws IOException { + populateObjectStream(); + testOutput(); + } + + @Test + public void testObjectStreamCollection() throws IOException { + objectStream = new ObjectStream(objectStream); + pdfDocument.assignObjectNumber(objectStream); + populateObjectStream(); + testOutput(); + } + + @Test(expected = IllegalStateException.class) + public void directObjectsAreNotAllowed() throws Exception { + objectStream.addObject(new MockCompressedObject()); + } + + @Test(expected = NullPointerException.class) + public void nullObjectsAreNotAllowed() throws Exception { + objectStream.addObject(null); + } + + private void testOutput() throws IOException { + String expected = getExpectedOutput(); + String actual = getActualOutput(); + assertEquals(expected, actual); + } + + private void populateObjectStream() { + for (MockCompressedObject obj : compressedObjects) { + pdfDocument.assignObjectNumber(obj); + objectStream.addObject(obj); + } + } + + private String getExpectedOutput() { + int numObs = compressedObjects.size(); + int objectStreamNumber = objectStream.getObjectNumber(); + int offsetsLength = 9; + StringBuilder expected = new StringBuilder(); + expected.append("<<\n"); + ObjectStream previous = (ObjectStream) objectStream.get("Extends"); + if (previous != null) { + expected.append(" /Extends ").append(previous.getObjectNumber()).append(" 0 R\n"); + } + expected.append(" /Type /ObjStm\n") + .append(" /N ").append(numObs).append("\n") + .append(" /First ").append(offsetsLength).append('\n') + .append(" /Length ").append(OBJECT_CONTENT.length() * 2 + offsetsLength + 1).append('\n') + .append(">>\n") + .append("stream\n"); + int offset = 0; + int num = 1; + for (PDFObject ob : compressedObjects) { + expected.append(objectStreamNumber + num++).append(' ').append(offset).append('\n'); + offset += ob.toPDFString().length(); + } + for (PDFObject ob : compressedObjects) { + expected.append(ob.toPDFString()); + } + expected.append("\nendstream"); + return expected.toString(); + } + + private String getActualOutput() throws IOException { + ByteArrayOutputStream actual = new ByteArrayOutputStream(); + objectStream.getFilterList().setDisableAllFilters(true); + objectStream.output(actual); + return actual.toString("US-ASCII"); + } + + private static class MockCompressedObject extends PDFObject implements CompressedObject { + + @Override + protected String toPDFString() { + return OBJECT_CONTENT; + } + } + +} diff --git a/test/java/org/apache/fop/pdf/PDFEncryptionJCETestCase.java b/test/java/org/apache/fop/pdf/PDFEncryptionJCETestCase.java index 235db7045..db10e656e 100644 --- a/test/java/org/apache/fop/pdf/PDFEncryptionJCETestCase.java +++ b/test/java/org/apache/fop/pdf/PDFEncryptionJCETestCase.java @@ -223,8 +223,6 @@ public class PDFEncryptionJCETestCase { final String digits = "\\d+"; final String hexDigits = "\\p{XDigit}+"; - dictionary.mustContain("1" + whitespace + "0" + whitespace + "obj"); - dictionary.mustContain("/Filter" + whitespace + "/Standard\\b"); dictionary.mustContain("/V" + whitespace + "(" + digits + ")") diff --git a/test/java/org/apache/fop/pdf/PDFFilterListTestCase.java b/test/java/org/apache/fop/pdf/PDFFilterListTestCase.java new file mode 100644 index 000000000..2504d871a --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFFilterListTestCase.java @@ -0,0 +1,33 @@ +/* + * 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 org.junit.Test; + +public class PDFFilterListTestCase { + + @Test + public void testFilterList() { + PDFFilterList filterList = new PDFFilterList(); + assertFalse(filterList.isInitialized()); + } +} diff --git a/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java b/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java index a0c4ea11f..c7a9dff89 100644 --- a/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java +++ b/test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java @@ -40,7 +40,9 @@ import org.junit.runners.Suite.SuiteClasses; PDFNullTestCase.class, PDFNumsArrayTestCase.class, PDFRectangleTestCase.class, - PDFReferenceTestCase.class + PDFReferenceTestCase.class, + VersionTestCase.class, + VersionControllerTestCase.class }) public class PDFLibraryTestSuite { } diff --git a/test/java/org/apache/fop/pdf/PDFObjectTestCase.java b/test/java/org/apache/fop/pdf/PDFObjectTestCase.java index ee9512d88..10ffa3b27 100644 --- a/test/java/org/apache/fop/pdf/PDFObjectTestCase.java +++ b/test/java/org/apache/fop/pdf/PDFObjectTestCase.java @@ -40,10 +40,6 @@ public class PDFObjectTestCase { 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 { @@ -195,8 +191,7 @@ public class PDFObjectTestCase { 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()); + assertEquals(expectedString.length(), object.output(outStream)); + assertEquals(expectedString, outStream.toString()); } } diff --git a/test/java/org/apache/fop/pdf/PDFStreamTestCase.java b/test/java/org/apache/fop/pdf/PDFStreamTestCase.java new file mode 100644 index 000000000..93dcea511 --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFStreamTestCase.java @@ -0,0 +1,126 @@ +/* + * 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.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.junit.Before; +import org.junit.Test; + +public class PDFStreamTestCase { + + private PDFStream stream; + + @Before + public void createStream() { + stream = new PDFStream(); + stream.setObjectNumber(1); + PDFDocument pdfDocument = new PDFDocument("Apache FOP"); + stream.setDocument(pdfDocument); + } + + @Test + public void testFilterSetup() { + testGetFilterList(); + testSetupFilterList(); + } + + private void testGetFilterList() { + PDFFilterList filterList = stream.getFilterList(); + assertFalse(filterList.isInitialized()); + assertEquals(0, filterList.getFilters().size()); + } + + private void testSetupFilterList() { + stream.setupFilterList(); + PDFFilterList filterList = stream.getFilterList(); + assertTrue(filterList.isInitialized()); + assertEquals(1, filterList.getFilters().size()); + PDFFilter filter = filterList.getFilters().get(0); + assertEquals("/FlateDecode", filter.getName()); + } + + @Test + public void customFilter() { + PDFFilterList filters = stream.getFilterList(); + filters.addFilter("null"); + assertTrue(filters.isInitialized()); + assertEquals(1, filters.getFilters().size()); + PDFFilter filter = filters.getFilters().get(0); + assertEquals("", filter.getName()); + } + + @Test + public void testStream() throws IOException { + PDFFilterList filters = stream.getFilterList(); + filters.addFilter("null"); + byte[] bytes = createSampleData(); + stream.setData(bytes); + ByteArrayOutputStream actual = new ByteArrayOutputStream(); + stream.outputRawStreamData(actual); + assertArrayEquals(bytes, actual.toByteArray()); + } + + @Test + public void testEncodeStream() throws IOException { + PDFFilterList filters = stream.getFilterList(); + filters.addFilter("null"); + byte[] bytes = createSampleData(); + stream.setData(bytes); + ByteArrayOutputStream actual = new ByteArrayOutputStream(); + StreamCache streamCache = stream.encodeStream(); + streamCache.outputContents(actual); + assertArrayEquals(bytes, actual.toByteArray()); + } + + @Test + public void testEncodeAndWriteStream() throws IOException { + PDFFilterList filters = stream.getFilterList(); + filters.addFilter("null"); + byte[] bytes = createSampleData(); + stream.setData(bytes); + ByteArrayOutputStream actual = new ByteArrayOutputStream(); + PDFNumber number = new PDFNumber(); + stream.encodeAndWriteStream(actual, number); + assertArrayEquals(createSampleStreamData(), actual.toByteArray()); + } + + private byte[] createSampleData() { + byte[] bytes = new byte[10]; + for (int i = 0; i < 10; i++) { + bytes[i] = (byte) i; + } + return bytes; + } + + private byte[] createSampleStreamData() throws IOException { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + stream.write("stream\n".getBytes("US-ASCII")); + stream.write(createSampleData()); + stream.write("\nendstream".getBytes("US-ASCII")); + return stream.toByteArray(); + } +} diff --git a/test/java/org/apache/fop/pdf/xref/CompressedObjectReferenceTestCase.java b/test/java/org/apache/fop/pdf/xref/CompressedObjectReferenceTestCase.java new file mode 100644 index 000000000..8b103d277 --- /dev/null +++ b/test/java/org/apache/fop/pdf/xref/CompressedObjectReferenceTestCase.java @@ -0,0 +1,50 @@ +/* + * 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.xref; + +import static org.junit.Assert.assertArrayEquals; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +public class CompressedObjectReferenceTestCase extends ObjectReferenceTest { + + @Test + public void testOutput() throws IOException { + runTest(Arrays.asList(0, 0, 0, 0, 0, 0, 0, 0), 0); + runTest(Arrays.asList(0, 0, 0, 0, 0, 0, 0, 0x1), 4); + runTest(Arrays.asList(0, 0, 0, 0, 0, 0, 0, 0xf3), 16); + runTest(Arrays.asList(0, 0, 0, 0, 0, 0, 0x5, 0xf7), 128); + runTest(Arrays.asList(0, 0, 0, 0, 0, 0x9, 0xfb, 0xd), 0xae); + runTest(Arrays.asList(0, 0, 0, 0, 0x11, 0xff, 0x15, 0xe9), 0xff); + } + + private void runTest(List expectedObjectStreamBytes, int index) throws IOException { + int objectStreamNumber = (int) computeNumberFromBytes(expectedObjectStreamBytes); + sut = new CompressedObjectReference(0, objectStreamNumber, index); + byte[] expected = createExpectedOutput((byte) 2, expectedObjectStreamBytes, index); + byte[] actual = getActualOutput(); + assertArrayEquals(expected, actual); + } + +} diff --git a/test/java/org/apache/fop/pdf/xref/CrossReferenceObjectTest.java b/test/java/org/apache/fop/pdf/xref/CrossReferenceObjectTest.java new file mode 100644 index 000000000..df1b86e53 --- /dev/null +++ b/test/java/org/apache/fop/pdf/xref/CrossReferenceObjectTest.java @@ -0,0 +1,106 @@ +/* + * 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.xref; + +import static org.junit.Assert.assertArrayEquals; + +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.junit.Before; + +import org.apache.fop.pdf.PDFDocument; +import org.apache.fop.pdf.PDFInfo; +import org.apache.fop.pdf.PDFPages; +import org.apache.fop.pdf.PDFRoot; + +public abstract class CrossReferenceObjectTest { + + protected static final int STARTXREF = 12345; + + protected PDFDocument pdfDocument; + + protected TrailerDictionary trailerDictionary; + + private CrossReferenceObject crossReferenceObject; + + @Before + public void setUp() throws UnsupportedEncodingException { + pdfDocument = new PDFDocument("Apache FOP"); + Map> filterMap = pdfDocument.getFilterMap(); + filterMap.put("default", Arrays.asList("null")); + PDFRoot root = new PDFRoot(1, new PDFPages(10)); + PDFInfo info = new PDFInfo(); + info.setObjectNumber(2); + byte[] fileID = + new byte[] {0x01, 0x23, 0x45, 0x67, (byte) 0x89, (byte) 0xab, (byte) 0xcd, (byte) 0xef}; + trailerDictionary = new TrailerDictionary(pdfDocument) + .setRoot(root) + .setInfo(info) + .setFileID(fileID, fileID); + } + + protected void runTest() throws IOException { + crossReferenceObject = createCrossReferenceObject(); + byte[] expected = createExpectedCrossReferenceData(); + byte[] actual = createActualCrossReferenceData(); + assertArrayEquals(expected, actual); + } + + protected abstract CrossReferenceObject createCrossReferenceObject(); + + protected abstract byte[] createExpectedCrossReferenceData() throws IOException; + + protected byte[] createActualCrossReferenceData() throws IOException { + ByteArrayOutputStream pdf = new ByteArrayOutputStream(); + crossReferenceObject.output(pdf); + pdf.close(); + return pdf.toByteArray(); + } + + protected byte[] getBytes(StringBuilder stringBuilder) { + return getBytes(stringBuilder.toString()); + } + + protected byte[] getBytes(String string) { + try { + return string.getBytes("US-ASCII"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + /** + * Outputs the given byte array to a file with the given name. Use for debugging + * purpose. + */ + protected void streamToFile(byte[] bytes, String filename) throws IOException { + OutputStream output = new FileOutputStream(filename); + output.write(bytes); + output.close(); + } + +} diff --git a/test/java/org/apache/fop/pdf/xref/CrossReferenceStreamTestCase.java b/test/java/org/apache/fop/pdf/xref/CrossReferenceStreamTestCase.java new file mode 100644 index 000000000..3e609635d --- /dev/null +++ b/test/java/org/apache/fop/pdf/xref/CrossReferenceStreamTestCase.java @@ -0,0 +1,142 @@ +/* + * 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.xref; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; + +public class CrossReferenceStreamTestCase extends CrossReferenceObjectTest { + + private List uncompressedObjectOffsets; + + private List compressedObjectReferences; + + @Test + public void testWithNoOffset() throws IOException { + List emptyList = Collections.emptyList(); + test(emptyList); + } + + @Test + public void testWithOffsets() throws IOException { + test(new ArrayList(Arrays.asList(0L, 1L, 2L, 3L, 4L))); + } + + @Test + public void testWithBigOffsets() throws IOException { + test(new ArrayList(Arrays.asList(0xffL, 0xffffL, 0xffffffffL, 0xffffffffffffffffL))); + } + + @Test + public void testWithObjectStreams1() throws IOException { + List compressedObjectReferences = + Arrays.asList(new CompressedObjectReference(2, 1, 0)); + test(Arrays.asList(0L, null), compressedObjectReferences); + } + + @Test + public void testWithObjectStreams2() throws IOException { + int numIndirectObjects = 2; + int numCompressedObjects = 1; + List indirectObjectOffsets + = new ArrayList(numIndirectObjects + numCompressedObjects); + for (long i = 0; i < numIndirectObjects; i++) { + indirectObjectOffsets.add(i); + } + List compressedObjectReferences + = new ArrayList(); + for (int index = 0; index < numCompressedObjects; index++) { + indirectObjectOffsets.add(null); + int obNum = numIndirectObjects + index + 1; + compressedObjectReferences.add(new CompressedObjectReference(obNum, + numIndirectObjects, index)); + } + test(indirectObjectOffsets, compressedObjectReferences); + } + + private void test(List indirectObjectOffsets) throws IOException { + List compressedObjectReferences = Collections.emptyList(); + test(indirectObjectOffsets, compressedObjectReferences); + } + + private void test(List indirectObjectOffsets, + List compressedObjectReferences) throws IOException { + this.uncompressedObjectOffsets = indirectObjectOffsets; + this.compressedObjectReferences = compressedObjectReferences; + runTest(); + } + + @Override + protected CrossReferenceObject createCrossReferenceObject() { + return new CrossReferenceStream(pdfDocument, + uncompressedObjectOffsets.size() + 1, + trailerDictionary, + STARTXREF, + uncompressedObjectOffsets, + compressedObjectReferences); + } + + @Override + protected byte[] createExpectedCrossReferenceData() throws IOException { + List objectReferences + = new ArrayList(uncompressedObjectOffsets.size()); + for (Long offset : uncompressedObjectOffsets) { + objectReferences.add(offset == null ? null : new UncompressedObjectReference(offset)); + } + for (CompressedObjectReference ref : compressedObjectReferences) { + objectReferences.set(ref.getObjectNumber() - 1, ref); + } + int maxObjectNumber = objectReferences.size() + 1; + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + StringBuilder expected = new StringBuilder(256); + expected.append(maxObjectNumber + " 0 obj\n") + .append("<<\n") + .append(" /Root 1 0 R\n") + .append(" /Info 2 0 R\n") + .append(" /ID [<0123456789ABCDEF> <0123456789ABCDEF>]\n") + .append(" /Type /XRef\n") + .append(" /Size ").append(Integer.toString(maxObjectNumber + 1)).append('\n') + .append(" /W [1 8 2]\n") + .append(" /Length ").append(Integer.toString((maxObjectNumber + 1) * 11 + 1)).append('\n') + .append(">>\n") + .append("stream\n"); + stream.write(getBytes(expected)); + DataOutputStream data = new DataOutputStream(stream); + data.write(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xff, (byte) 0xff}); + for (ObjectReference objectReference : objectReferences) { + objectReference.output(data); + } + data.write(1); + data.writeLong(STARTXREF); + data.write(0); + data.write(0); + data.close(); + stream.write(getBytes("\nendstream\nendobj\n")); + return stream.toByteArray(); + } + +} diff --git a/test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java b/test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java new file mode 100644 index 000000000..ceff96a91 --- /dev/null +++ b/test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java @@ -0,0 +1,80 @@ +/* + * 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.xref; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; + +public class CrossReferenceTableTestCase extends CrossReferenceObjectTest { + + private List offsets; + + @Test + public void testWithNoOffset() throws IOException { + List emptyList = Collections.emptyList(); + runTest(emptyList); + } + + @Test + public void testWithOffsets() throws IOException { + runTest(Arrays.asList(0L, 1L, 2L, 3L, 4L)); + } + + @Test + public void testWithBigOffsets() throws IOException { + runTest(Arrays.asList(0xffL, 0xffffL, 0x7fffffffL)); + } + + private void runTest(List offsets) throws IOException { + this.offsets = offsets; + runTest(); + } + + @Override + protected CrossReferenceObject createCrossReferenceObject() { + return new CrossReferenceTable(trailerDictionary, STARTXREF, offsets); + } + + @Override + protected byte[] createExpectedCrossReferenceData() throws IOException { + StringBuilder expected = new StringBuilder(256); + expected.append("xref\n0 ") + .append(offsets.size() + 1) + .append("\n0000000000 65535 f \n"); + for (Long objectReference : offsets) { + final String padding = "0000000000"; + String s = String.valueOf(objectReference).toString(); + String loc = padding.substring(s.length()) + s; + expected.append(loc).append(" 00000 n \n"); + } + expected.append("trailer\n<<\n") + .append(" /Root 1 0 R\n") + .append(" /Info 2 0 R\n") + .append(" /ID [<0123456789ABCDEF> <0123456789ABCDEF>]\n") + .append(" /Size ").append(Integer.toString(offsets.size() + 1)).append('\n') + .append(">>\n"); + return getBytes(expected); + } + +} diff --git a/test/java/org/apache/fop/pdf/xref/ObjectReferenceTest.java b/test/java/org/apache/fop/pdf/xref/ObjectReferenceTest.java new file mode 100644 index 000000000..fada2794c --- /dev/null +++ b/test/java/org/apache/fop/pdf/xref/ObjectReferenceTest.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.xref; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.List; + +abstract class ObjectReferenceTest { + + protected ObjectReference sut; + + protected long computeNumberFromBytes(List expectedOffsetBytes) { + assert expectedOffsetBytes.size() <= 8; + long offset = 0; + for (int b : expectedOffsetBytes) { + offset = offset << 8 | b; + } + return offset; + } + + protected byte[] createExpectedOutput(byte field1, List field2, int field3) { + assert field2.size() == 8; + assert (field3 & 0xffff) == field3; + byte[] expected = new byte[11]; + int index = 0; + expected[index++] = field1; + for (Integer b : field2) { + expected[index++] = b.byteValue(); + } + expected[index++] = (byte) ((field3 & 0xff00) >> 8); + expected[index++] = (byte) (field3 & 0xff); + return expected; + } + + protected byte[] getActualOutput() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DataOutputStream dataOutputStream = new DataOutputStream(out); + sut.output(dataOutputStream); + dataOutputStream.close(); + return out.toByteArray(); + } + +} diff --git a/test/java/org/apache/fop/pdf/xref/UncompressedObjectReferenceTestCase.java b/test/java/org/apache/fop/pdf/xref/UncompressedObjectReferenceTestCase.java new file mode 100644 index 000000000..b147084e8 --- /dev/null +++ b/test/java/org/apache/fop/pdf/xref/UncompressedObjectReferenceTestCase.java @@ -0,0 +1,90 @@ +/* + * 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.xref; + +import static org.junit.Assert.assertArrayEquals; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +public class UncompressedObjectReferenceTestCase extends ObjectReferenceTest { + + @Test + public void test1ByteOffsets() throws IOException { + run1ByteOffsetTest(0x0); + run1ByteOffsetTest(0xf); + run1ByteOffsetTest(0x10); + run1ByteOffsetTest(0xff); + } + + private void run1ByteOffsetTest(int offset) throws IOException { + runIntegerOffsetTest(Arrays.asList(0, 0, 0, offset)); + } + + @Test + public void test2ByteOffsets() throws IOException { + runIntegerOffsetTest(Arrays.asList(0, 0, 1, 0xff)); + runIntegerOffsetTest(Arrays.asList(0, 0, 0xa0, 0xff)); + } + + @Test + public void test3ByteOffsets() throws IOException { + runIntegerOffsetTest(Arrays.asList(0, 2, 0x12, 0x34)); + runIntegerOffsetTest(Arrays.asList(0, 0xee, 0x56, 0x78)); + } + + @Test + public void test4ByteOffsets() throws IOException { + runIntegerOffsetTest(Arrays.asList(0x6, 0x12, 0x34, 0x56)); + runIntegerOffsetTest(Arrays.asList(0xf1, 0x9a, 0xbc, 0xde)); + } + + @Test + public void test5ByteOffsets() throws IOException { + runTest(Arrays.asList(0, 0, 0, 0x7, 0x78, 0x9a, 0xbc, 0xde)); + runTest(Arrays.asList(0, 0, 0, 0xbf, 0xf0, 0, 0x1, 0x2)); + } + + @Test + public void test8ByteOffsets() throws IOException { + runTest(Arrays.asList(0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8)); + runTest(Arrays.asList(0xf9, 0xe8, 0xd7, 0xc6, 0xb5, 0xa4, 0x93, 0x82)); + } + + private void runIntegerOffsetTest(List expectedOffsetBytes) throws IOException { + List expectedLongOffset = new ArrayList(8); + expectedLongOffset.addAll(Arrays.asList(0, 0, 0, 0)); + expectedLongOffset.addAll(expectedOffsetBytes); + runTest(expectedLongOffset); + } + + private void runTest(List expectedOffsetBytes) throws IOException { + long offset = computeNumberFromBytes(expectedOffsetBytes); + sut = new UncompressedObjectReference(offset); + byte[] expected = createExpectedOutput((byte) 1, expectedOffsetBytes, (byte) 0); + byte[] actual = getActualOutput(); + assertArrayEquals(expected, actual); + } + +} diff --git a/test/pdf/1.5/fop.xconf b/test/pdf/1.5/fop.xconf new file mode 100644 index 000000000..ab8bc7cbf --- /dev/null +++ b/test/pdf/1.5/fop.xconf @@ -0,0 +1,24 @@ + + + true + 144 + false + ../../resources/fonts/ttf/ + + + 1.5 + + null + + + flate + ascii-85 + + + + + + + + + diff --git a/test/pdf/1.5/test.fo b/test/pdf/1.5/test.fo new file mode 100644 index 000000000..23e8405f3 --- /dev/null +++ b/test/pdf/1.5/test.fo @@ -0,0 +1,207 @@ + + + + + + + + + + + + + This is the page headerPage + + + + + + + (There’s another page sequence below.) + About Apache FOP + It is a print formatter driven by XSL formatting objects (XSL-FO) and an output + independent formatter1See the FOP + website for more + information. FOP has a nice logo: + + + + + + Header 1.1 + + + Header 1.2 + + + + + + + Cell 1.1 + + + Cell 1.2 + + + + + Cell 2.1 + + + Cell 2.2 + + + + + Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL + formatting objects (XSL-FO) and an output independent formatter. It is a Java application + that reads a formatting object (FO) tree and renders the resulting pages to a specified + output. + This fo:block element spans all the columns of the + document. This is intended to test the abilities of the text-to-speech program. + And now we are back to normal content flowing in two columns. Let’s start a numbered + list: + + + + 1. + + + + Line 1 of item 1 + Line 2 of item 1 + Line 3 of item 1 + + + + + + 2. + + + + Line 1 of item 2 + Line 2 of item 2 + Line 3 of item 2 + + + + + And now we are going to see how a second page sequence is handled. + + + + + This is the page headerPage + + + + + + + Apache FOP (Formatting Objects Processor) is a print formatter driven by + XSL formatting objects (XSL-FO) and an output independent formatter1See the + FOP + website for more + information. It is a Java application that + reads a formatting object (FO) tree and renders the resulting pages to a specified + output. + + + + + Header 1.1 + + + Header 1.2 + + + + + + + Cell 1.1 + + + Cell 1.2 + + + + + Cell 2.1 + + + Cell 2.2 + + + + + Apache FOP (Formatting Objects Processor) est une + application de mise en page de documents respectant le standard XSL-FO. À partir d’un + document au format XSL-FO, cette application écrite en Java effectue une mise en page et + renvoie un document prêt pour impression. + This fo:block element spans all the columns of the + document. This is intended to test the abilities of the text-to-speech program. + And now we are back to normal content flowing in two columns. Let’s start a numbered + list: + + + + 1. + + + + Line 1 of item 1 + Line 2 of item 1 + Line 3 of item 1 + + + + + + 2. + + + + Line 1 of item 2 + Line 2 of item 2 + Line 3 of item 2 + + + + + The end of the document has now been reached. + + + diff --git a/test/pdf/1.5/test.pdf b/test/pdf/1.5/test.pdf new file mode 100644 index 000000000..4c25c0847 Binary files /dev/null and b/test/pdf/1.5/test.pdf differ diff --git a/test/pdf/accessibility/README b/test/pdf/accessibility/README new file mode 100644 index 000000000..1d2e04361 --- /dev/null +++ b/test/pdf/accessibility/README @@ -0,0 +1,41 @@ +This directory contains sample FO files for testing the accessibility features +of FOP. + +Every FO file in this directory has a corresponding PDF file in the pdf/ +sub-directory. The fop.xconf configuration file was used. + +The PDF files have been checked with Adobe Acrobat Professional 9, using both +the full accessibility checker and the read-aloud feature. The checker reports +no error /and/ the entire document can be read aloud. + + +!! DO NOT MODIFY THOSE FILES, NEITHER THE FO NOR THE PDF !! + + +... Or at least, know what you are doing +If the FO files are modified, the resulting PDFs must be checked again, both +with the checker and the read-aloud feature. (Sometimes the checker reports no +problem yet part or all of the document cannot be read aloud.) + +The purpose of this infrastructure is to be able to quickly re-test the +accessibility processing chain when any change has been made to it. The +configuration file disables the compression of the PDF streams, so it is +possible to compare a re-generated PDF with the original one by using a simple +diff tool. The files will not be identical because of the different creation +dates (and the ID key in the trailer), but apart from that there should be no +difference. + +The rationale is that using a diff tool is much quicker and less tedious than +running Acrobat's accessibility checker and read-aloud feature every time. + + +To re-generate the PDF files: + ../../fop -c fop.xconf text_1.fo pdf/text_1.new.pdf + diff pdf/text_1_painter.pdf pdf/text_1.new.pdf +Or, going through the intermediate format: + ../../fop -c fop.xconf text_1.fo -if application/pdf text_1_if.xml + ../../fop -c fop.xconf -ifin text_1_if.xml pdf/text_1.new.pdf + diff pdf/text_1.pdf pdf/text_1.new.pdf + + +$Id$ diff --git a/test/pdf/accessibility/background-image_jpg_repeat.fo b/test/pdf/accessibility/background-image_jpg_repeat.fo new file mode 100644 index 000000000..12af7d72a --- /dev/null +++ b/test/pdf/accessibility/background-image_jpg_repeat.fo @@ -0,0 +1,34 @@ + + + + + + + + + + + + Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL + formatting objects (XSL-FO) and an output independent formatter. It is a Java application + that reads a formatting object (FO) tree and renders the resulting pages to a specified + output. + + + diff --git a/test/pdf/accessibility/background-image_jpg_single.fo b/test/pdf/accessibility/background-image_jpg_single.fo new file mode 100644 index 000000000..1efd10deb --- /dev/null +++ b/test/pdf/accessibility/background-image_jpg_single.fo @@ -0,0 +1,36 @@ + + + + + + + + + + + + Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL + formatting objects (XSL-FO) and an output independent formatter. It is a Java application + that reads a formatting object (FO) tree and renders the resulting pages to a specified + output. + + + diff --git a/test/pdf/accessibility/background-image_png_repeat.fo b/test/pdf/accessibility/background-image_png_repeat.fo new file mode 100644 index 000000000..9bbc3e47c --- /dev/null +++ b/test/pdf/accessibility/background-image_png_repeat.fo @@ -0,0 +1,34 @@ + + + + + + + + + + + + Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL + formatting objects (XSL-FO) and an output independent formatter. It is a Java application + that reads a formatting object (FO) tree and renders the resulting pages to a specified + output. + + + diff --git a/test/pdf/accessibility/background-image_png_single.fo b/test/pdf/accessibility/background-image_png_single.fo new file mode 100644 index 000000000..0cff427b6 --- /dev/null +++ b/test/pdf/accessibility/background-image_png_single.fo @@ -0,0 +1,36 @@ + + + + + + + + + + + + Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL + formatting objects (XSL-FO) and an output independent formatter. It is a Java application + that reads a formatting object (FO) tree and renders the resulting pages to a specified + output. + + + diff --git a/test/pdf/accessibility/background-image_svg_repeat.fo b/test/pdf/accessibility/background-image_svg_repeat.fo new file mode 100644 index 000000000..ba69947cc --- /dev/null +++ b/test/pdf/accessibility/background-image_svg_repeat.fo @@ -0,0 +1,34 @@ + + + + + + + + + + + + Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL + formatting objects (XSL-FO) and an output independent formatter. It is a Java application + that reads a formatting object (FO) tree and renders the resulting pages to a specified + output. + + + diff --git a/test/pdf/accessibility/background-image_svg_single.fo b/test/pdf/accessibility/background-image_svg_single.fo new file mode 100644 index 000000000..efe91c65b --- /dev/null +++ b/test/pdf/accessibility/background-image_svg_single.fo @@ -0,0 +1,36 @@ + + + + + + + + + + + + Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL + formatting objects (XSL-FO) and an output independent formatter. It is a Java application + that reads a formatting object (FO) tree and renders the resulting pages to a specified + output. + + + diff --git a/test/pdf/accessibility/complete.fo b/test/pdf/accessibility/complete.fo new file mode 100644 index 000000000..23e8405f3 --- /dev/null +++ b/test/pdf/accessibility/complete.fo @@ -0,0 +1,207 @@ + + + + + + + + + + + + + This is the page headerPage + + + + + + + (There’s another page sequence below.) + About Apache FOP + It is a print formatter driven by XSL formatting objects (XSL-FO) and an output + independent formatter1See the FOP + website for more + information. FOP has a nice logo: + + + + + + Header 1.1 + + + Header 1.2 + + + + + + + Cell 1.1 + + + Cell 1.2 + + + + + Cell 2.1 + + + Cell 2.2 + + + + + Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL + formatting objects (XSL-FO) and an output independent formatter. It is a Java application + that reads a formatting object (FO) tree and renders the resulting pages to a specified + output. + This fo:block element spans all the columns of the + document. This is intended to test the abilities of the text-to-speech program. + And now we are back to normal content flowing in two columns. Let’s start a numbered + list: + + + + 1. + + + + Line 1 of item 1 + Line 2 of item 1 + Line 3 of item 1 + + + + + + 2. + + + + Line 1 of item 2 + Line 2 of item 2 + Line 3 of item 2 + + + + + And now we are going to see how a second page sequence is handled. + + + + + This is the page headerPage + + + + + + + Apache FOP (Formatting Objects Processor) is a print formatter driven by + XSL formatting objects (XSL-FO) and an output independent formatter1See the + FOP + website for more + information. It is a Java application that + reads a formatting object (FO) tree and renders the resulting pages to a specified + output. + + + + + Header 1.1 + + + Header 1.2 + + + + + + + Cell 1.1 + + + Cell 1.2 + + + + + Cell 2.1 + + + Cell 2.2 + + + + + Apache FOP (Formatting Objects Processor) est une + application de mise en page de documents respectant le standard XSL-FO. À partir d’un + document au format XSL-FO, cette application écrite en Java effectue une mise en page et + renvoie un document prêt pour impression. + This fo:block element spans all the columns of the + document. This is intended to test the abilities of the text-to-speech program. + And now we are back to normal content flowing in two columns. Let’s start a numbered + list: + + + + 1. + + + + Line 1 of item 1 + Line 2 of item 1 + Line 3 of item 1 + + + + + + 2. + + + + Line 1 of item 2 + Line 2 of item 2 + Line 3 of item 2 + + + + + The end of the document has now been reached. + + + diff --git a/test/pdf/accessibility/fop.xconf b/test/pdf/accessibility/fop.xconf new file mode 100644 index 000000000..adfccd2cc --- /dev/null +++ b/test/pdf/accessibility/fop.xconf @@ -0,0 +1,23 @@ + + + true + 144 + false + ../../resources/fonts/ttf/ + + + + null + + + flate + ascii-85 + + + + + + + + + diff --git a/test/pdf/accessibility/image_jpg.fo b/test/pdf/accessibility/image_jpg.fo new file mode 100644 index 000000000..2a2b60076 --- /dev/null +++ b/test/pdf/accessibility/image_jpg.fo @@ -0,0 +1,35 @@ + + + + + + + + + + + + This document contains an image in the JPEG format: . Here is the end of the text. + + + diff --git a/test/pdf/accessibility/image_png.fo b/test/pdf/accessibility/image_png.fo new file mode 100644 index 000000000..52ee80ac5 --- /dev/null +++ b/test/pdf/accessibility/image_png.fo @@ -0,0 +1,35 @@ + + + + + + + + + + + + This document contains an image in the PNG format: . Here is the end of the text. + + + diff --git a/test/pdf/accessibility/image_svg.fo b/test/pdf/accessibility/image_svg.fo new file mode 100644 index 000000000..96cfedee3 --- /dev/null +++ b/test/pdf/accessibility/image_svg.fo @@ -0,0 +1,45 @@ + + + + + + + + + + + + This document contains an image in the SVG format: . And here is the same image as an instream-foreign-object: + + + + + + + + + . + + + diff --git a/test/pdf/accessibility/image_wmf.fo b/test/pdf/accessibility/image_wmf.fo new file mode 100644 index 000000000..43112dba1 --- /dev/null +++ b/test/pdf/accessibility/image_wmf.fo @@ -0,0 +1,35 @@ + + + + + + + + + + + + This document contains an image in the WMF format: Here is the end of the text. + + + diff --git a/test/pdf/accessibility/leader.fo b/test/pdf/accessibility/leader.fo new file mode 100644 index 000000000..4b395cc69 --- /dev/null +++ b/test/pdf/accessibility/leader.fo @@ -0,0 +1,38 @@ + + + + + + + + + + + + This is a text followed by a leader with leader-pattern=​"use-content", the + content being text: • + 1 + This is a text followed by a leader with + leader-pattern=​"use-content", the content being images:1 + + + diff --git a/test/pdf/accessibility/links.fo b/test/pdf/accessibility/links.fo new file mode 100644 index 000000000..36250e332 --- /dev/null +++ b/test/pdf/accessibility/links.fo @@ -0,0 +1,41 @@ + + + + + + + + + + + + This is a link to the next + paragraph. + Apache FOP (Formatting Objects Processor) is a print formatter driven by + XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java + application that reads a formatting object (FO) tree and renders the resulting pages to a + specified output. + For more information, see the FOP + website. + + + diff --git a/test/pdf/accessibility/pdf/background-image_jpg_repeat.pdf b/test/pdf/accessibility/pdf/background-image_jpg_repeat.pdf new file mode 100644 index 000000000..9b4a7fc20 Binary files /dev/null and b/test/pdf/accessibility/pdf/background-image_jpg_repeat.pdf differ diff --git a/test/pdf/accessibility/pdf/background-image_jpg_single.pdf b/test/pdf/accessibility/pdf/background-image_jpg_single.pdf new file mode 100644 index 000000000..d1d0ecb1b Binary files /dev/null and b/test/pdf/accessibility/pdf/background-image_jpg_single.pdf differ diff --git a/test/pdf/accessibility/pdf/background-image_png_repeat.pdf b/test/pdf/accessibility/pdf/background-image_png_repeat.pdf new file mode 100644 index 000000000..ccb2cf089 Binary files /dev/null and b/test/pdf/accessibility/pdf/background-image_png_repeat.pdf differ diff --git a/test/pdf/accessibility/pdf/background-image_png_single.pdf b/test/pdf/accessibility/pdf/background-image_png_single.pdf new file mode 100644 index 000000000..902520b95 Binary files /dev/null and b/test/pdf/accessibility/pdf/background-image_png_single.pdf differ diff --git a/test/pdf/accessibility/pdf/background-image_svg_repeat.pdf b/test/pdf/accessibility/pdf/background-image_svg_repeat.pdf new file mode 100644 index 000000000..a720a5b9a Binary files /dev/null and b/test/pdf/accessibility/pdf/background-image_svg_repeat.pdf differ diff --git a/test/pdf/accessibility/pdf/background-image_svg_single.pdf b/test/pdf/accessibility/pdf/background-image_svg_single.pdf new file mode 100644 index 000000000..7e6e3e9d4 Binary files /dev/null and b/test/pdf/accessibility/pdf/background-image_svg_single.pdf differ diff --git a/test/pdf/accessibility/pdf/complete.pdf b/test/pdf/accessibility/pdf/complete.pdf new file mode 100644 index 000000000..f1dc10559 Binary files /dev/null and b/test/pdf/accessibility/pdf/complete.pdf differ diff --git a/test/pdf/accessibility/pdf/image_jpg.pdf b/test/pdf/accessibility/pdf/image_jpg.pdf new file mode 100644 index 000000000..e943c83e5 Binary files /dev/null and b/test/pdf/accessibility/pdf/image_jpg.pdf differ diff --git a/test/pdf/accessibility/pdf/image_png.pdf b/test/pdf/accessibility/pdf/image_png.pdf new file mode 100644 index 000000000..5ea44673b Binary files /dev/null and b/test/pdf/accessibility/pdf/image_png.pdf differ diff --git a/test/pdf/accessibility/pdf/image_svg.pdf b/test/pdf/accessibility/pdf/image_svg.pdf new file mode 100644 index 000000000..db1e061cf Binary files /dev/null and b/test/pdf/accessibility/pdf/image_svg.pdf differ diff --git a/test/pdf/accessibility/pdf/image_wmf.pdf b/test/pdf/accessibility/pdf/image_wmf.pdf new file mode 100644 index 000000000..65c46d12a Binary files /dev/null and b/test/pdf/accessibility/pdf/image_wmf.pdf differ diff --git a/test/pdf/accessibility/pdf/leader.pdf b/test/pdf/accessibility/pdf/leader.pdf new file mode 100644 index 000000000..d270c25a1 Binary files /dev/null and b/test/pdf/accessibility/pdf/leader.pdf differ diff --git a/test/pdf/accessibility/pdf/links.pdf b/test/pdf/accessibility/pdf/links.pdf new file mode 100644 index 000000000..b2a5a4209 Binary files /dev/null and b/test/pdf/accessibility/pdf/links.pdf differ diff --git a/test/pdf/accessibility/pdf/role.pdf b/test/pdf/accessibility/pdf/role.pdf new file mode 100644 index 000000000..329925bcd Binary files /dev/null and b/test/pdf/accessibility/pdf/role.pdf differ diff --git a/test/pdf/accessibility/pdf/role_non-standard.pdf b/test/pdf/accessibility/pdf/role_non-standard.pdf new file mode 100644 index 000000000..84daea788 Binary files /dev/null and b/test/pdf/accessibility/pdf/role_non-standard.pdf differ diff --git a/test/pdf/accessibility/pdf/text_1.pdf b/test/pdf/accessibility/pdf/text_1.pdf new file mode 100644 index 000000000..13f01711d Binary files /dev/null and b/test/pdf/accessibility/pdf/text_1.pdf differ diff --git a/test/pdf/accessibility/pdf/text_2.pdf b/test/pdf/accessibility/pdf/text_2.pdf new file mode 100644 index 000000000..943263994 Binary files /dev/null and b/test/pdf/accessibility/pdf/text_2.pdf differ diff --git a/test/pdf/accessibility/pdf/text_font-embedding.pdf b/test/pdf/accessibility/pdf/text_font-embedding.pdf new file mode 100644 index 000000000..7ffb40af8 Binary files /dev/null and b/test/pdf/accessibility/pdf/text_font-embedding.pdf differ diff --git a/test/pdf/accessibility/role.fo b/test/pdf/accessibility/role.fo new file mode 100644 index 000000000..ced8a4d44 --- /dev/null +++ b/test/pdf/accessibility/role.fo @@ -0,0 +1,125 @@ + + + + + + + + + + + + Title 1: To Start With + Title 2: A Sub-Title + Apache FOP (Formatting Objects Processor) is a print formatter + driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java + application that reads a formatting object (FO) tree and renders the resulting pages to a + specified output. + Title 2: Another Sub-Title + Apache FOP (Formatting Objects Processor) is a print formatter + driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java + application that reads a formatting object (FO) tree and renders the resulting pages to a + specified output. + Title 1: Second Title + Title 2: A Sample Table + See data below: + + + + + Header 1 + + + Header 2 + + + + + + + Footer 1 + + + Footer 2 + + + + + + + Cell 1.1 + + + Cell 1.2 + + + + + Cell 2.1 + + + Cell 2.2 + + + + + That’s all folks. + + + diff --git a/test/pdf/accessibility/role_non-standard.fo b/test/pdf/accessibility/role_non-standard.fo new file mode 100644 index 000000000..d3e1a9852 --- /dev/null +++ b/test/pdf/accessibility/role_non-standard.fo @@ -0,0 +1,125 @@ + + + + + + + + + + + + Title 1: To Start With + A Sub-Title With a Non-Standard Role + Apache FOP (Formatting Objects Processor) is a print formatter + driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java + application that reads a formatting object (FO) tree and renders the resulting pages to a + specified output. + Title 2: Another Sub-Title + Apache FOP (Formatting Objects Processor) is a print formatter + driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java + application that reads a formatting object (FO) tree and renders the resulting pages to a + specified output. + Title 1: Second Title + Title 2: A Sample Table + See data below: + + + + + Header 1 + + + Header 2 + + + + + + + Footer 1 + + + Footer 2 + + + + + + + Cell 1.1 + + + Cell 1.2 + + + + + Cell 2.1 + + + Cell 2.2 + + + + + That’s all folks. + + + diff --git a/test/pdf/accessibility/text_1.fo b/test/pdf/accessibility/text_1.fo new file mode 100644 index 000000000..31ad31514 --- /dev/null +++ b/test/pdf/accessibility/text_1.fo @@ -0,0 +1,34 @@ + + + + + + + + + + + + Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL + formatting objects (XSL-FO) and an output independent formatter. It is a Java application + that reads a formatting object (FO) tree and renders the resulting pages to a specified + output. + + + diff --git a/test/pdf/accessibility/text_2.fo b/test/pdf/accessibility/text_2.fo new file mode 100644 index 000000000..f5693110e --- /dev/null +++ b/test/pdf/accessibility/text_2.fo @@ -0,0 +1,41 @@ + + + + + + + + + + + + Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL + formatting objects (XSL-FO) and an output independent formatter. It is a Java application + that reads a formatting object (FO) tree and renders the resulting pages to a specified + output. + Apache FOP (Formatting Objects Processor) est une + application de mise en page de documents respectant le standard XSL-FO. À partir d’un + document au format XSL-FO, cette application écrite en Java effectue une mise en page et + renvoie un document prêt pour impression. + Back to English and let’s say it again: Apache FOP (Formatting Objects Processor) is + a print formatter driven by XSL formatting objects (XSL-FO) and an output independent + formatter. + + + diff --git a/test/pdf/accessibility/text_font-embedding.fo b/test/pdf/accessibility/text_font-embedding.fo new file mode 100644 index 000000000..10c1c99d9 --- /dev/null +++ b/test/pdf/accessibility/text_font-embedding.fo @@ -0,0 +1,34 @@ + + + + + + + + + + + + Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL + formatting objects (XSL-FO) and an output independent formatter. It is a Java application + that reads a formatting object (FO) tree and renders the resulting pages to a specified + output. + + +