diff options
author | Vincent Hennebert <vhennebert@apache.org> | 2012-03-26 17:39:20 +0000 |
---|---|---|
committer | Vincent Hennebert <vhennebert@apache.org> | 2012-03-26 17:39:20 +0000 |
commit | 4f9ccbd0b79cdb8eebbbdea994355f2df24e595b (patch) | |
tree | 19396231b63ee296159a2b846675c9e379be817c | |
parent | 2874695d34037474b121b41b178b70cbfb229490 (diff) | |
parent | b9fbfa6b71b5e0d67806c066ea422819de08f3b2 (diff) | |
download | xmlgraphics-fop-4f9ccbd0b79cdb8eebbbdea994355f2df24e595b.tar.gz xmlgraphics-fop-4f9ccbd0b79cdb8eebbbdea994355f2df24e595b.zip |
Merged branch Temp_PDF_ObjectStreams
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1305467 13f79535-47bb-0310-9956-ffa450edef68
118 files changed, 2242 insertions, 433 deletions
diff --git a/checkstyle-5.5.xml b/checkstyle-5.5.xml index 45ac11b1f..8194b78ca 100644 --- a/checkstyle-5.5.xml +++ b/checkstyle-5.5.xml @@ -256,14 +256,6 @@ <!-- ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... --> <!-- ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... --> - <module name="VisibilityModifier"> - <property name="packageAllowed" value="false"/> - <property name="protectedAllowed" value="true"/> - <property name="publicMemberPattern" value="^serialVersionUID"/> - </module> - <!-- ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... --> - - <!-- ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... --> <module name="WhitespaceAfter"> <property name="tokens" value="COMMA,SEMI"/> <!-- adding TYPECAST produces 2203 new errors --> </module> 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<CompressedObject> objects = new ArrayList<CompressedObject>(); + + 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<CompressedObjectReference> compressedObjectReferences; + + private int numObjectsInStream; + + private ObjectStream currentObjectStream; + + ObjectStreamManager(PDFDocument pdfDocument) { + this.pdfDocument = pdfDocument; + createObjectStream(); + compressedObjectReferences = new ArrayList<CompressedObjectReference>(); + } + + 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<CompressedObjectReference> 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,7 +83,7 @@ public class PDFArray extends PDFObject { * @param parent the array's parent if any * @param values the actual values wrapped by this object */ - public PDFArray(PDFObject parent, Collection<Object> values) { + public PDFArray(PDFObject parent, List<?> values) { /* generic creation of PDF object */ super(parent); @@ -92,6 +91,15 @@ public class PDFArray extends PDFObject { } /** + * 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 * @param values the actual array wrapped by this object @@ -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<Long> location = new ArrayList<Long>(); + private List<Long> indirectObjectOffsets = new ArrayList<Long>(); + + private Collection<PDFStructElem> structureTreeElements; /** List of objects to write in the trailer */ - private List trailerObjects = new ArrayList(); + private List<PDFObject> trailerObjects = new ArrayList<PDFObject>(); /** the objects themselves */ - private List objects = new LinkedList(); + private List<PDFObject> objects = new LinkedList<PDFObject>(); /** 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<String, PDFXObject> xObjectsMap = new HashMap<String, PDFXObject>(); - /** The {@link PDFFilter} map */ - private Map filterMap = new HashMap(); + private Map<String, PDFFont> fontMap = new HashMap<String, PDFFont>(); - /** List of {@link PDFGState}s. */ - private List gstates = new ArrayList(); + private Map<String, List<String>> filterMap = new HashMap<String, List<String>>(); - /** List of {@link PDFFunction}s. */ - private List functions = new ArrayList(); + private List<PDFGState> gstates = new ArrayList<PDFGState>(); - /** List of {@link PDFShading}s. */ - private List shadings = new ArrayList(); + private List<PDFFunction> functions = new ArrayList<PDFFunction>(); - /** List of {@link PDFPattern}s. */ - private List patterns = new ArrayList(); + private List<PDFShading> shadings = new ArrayList<PDFShading>(); - /** List of {@link PDFLink}s. */ - private List links = new ArrayList(); + private List<PDFPattern> patterns = new ArrayList<PDFPattern>(); - /** List of {@link PDFDestination}s. */ - private List destinations; + private List<PDFLink> links = new ArrayList<PDFLink>(); - /** List of {@link PDFFileSpec}s. */ - private List filespecs = new ArrayList(); + private List<PDFDestination> destinations; - /** List of {@link PDFGoToRemote}s. */ - private List gotoremotes = new ArrayList(); + private List<PDFFileSpec> filespecs = new ArrayList<PDFFileSpec>(); - /** List of {@link PDFGoTo}s. */ - private List gotos = new ArrayList(); + private List<PDFGoToRemote> gotoremotes = new ArrayList<PDFGoToRemote>(); - /** List of {@link PDFLaunch}es. */ - private List launches = new ArrayList(); + private List<PDFGoTo> gotos = new ArrayList<PDFGoTo>(); - /** - * The PDFDests object for the name dictionary. - * Note: This object is not a list. - */ - private PDFDests dests; + private List<PDFLaunch> launches = new ArrayList<PDFLaunch>(); private PDFFactory factory; - private boolean encodingOnTheFly = true; - private FileIDGenerator fileIDGenerator; + private boolean accessibilityEnabled; + /** * Creates an empty PDF document. * @@ -266,17 +249,6 @@ public class PDFDocument { } /** - * Indicates whether stream encoding on-the-fly is enabled. If enabled - * stream can be serialized without the need for a buffer to merely - * calculate the stream length. - * - * @return <code>true</code> if on-the-fly encoding is enabled - */ - public boolean isEncodingOnTheFly() { - return this.encodingOnTheFly; - } - - /** * Converts text to a byte array for writing to a PDF file. * * @param text text to convert/encode @@ -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<String, List<String>> map) { this.filterMap = map; } @@ -345,7 +317,7 @@ public class PDFDocument { * * @return the map of filters being used */ - public Map getFilterMap() { + public Map<String, List<String>> getFilterMap() { return this.filterMap; } @@ -368,6 +340,37 @@ public class PDFDocument { } /** + * Creates and returns a StructTreeRoot object. + * + * @param parentTree the value of the ParenTree entry + * @return the structure tree root + */ + public PDFStructTreeRoot makeStructTreeRoot(PDFParentTree parentTree) { + PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(parentTree); + assignObjectNumber(structTreeRoot); + addTrailerObject(structTreeRoot); + root.setStructTreeRoot(structTreeRoot); + structureTreeElements = new ArrayList<PDFStructElem>(); + return structTreeRoot; + } + + /** + * Creates and returns a structure element. + * + * @param structureType the structure type of the new element (value for the + * S entry) + * @param parent the parent of the new structure element in the structure + * hierarchy + * @return a dictionary of type StructElem + */ + public PDFStructElem makeStructureElement(PDFName structureType, PDFObject parent) { + PDFStructElem structElem = new PDFStructElem(parent, structureType); + assignObjectNumber(structElem); + structureTreeElements.add(structElem); + return structElem; + } + + /** * Get the {@link PDFInfo} object for this document. * * @return the {@link PDFInfo} object @@ -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<? extends PDFObject> 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<PDFGState> iter = this.gstates.iterator(); while (iter.hasNext()) { - PDFGState avail = (PDFGState)iter.next(); + PDFGState avail = iter.next(); poss = new PDFGState(); poss.addValues(current); poss.addValues(avail); @@ -712,7 +714,7 @@ public class PDFDocument { * * @return the map of fonts used in this document */ - public Map getFontMap() { + public Map<String, PDFFont> getFontMap() { return this.fontMap; } @@ -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<PDFDestination>(); } this.destinations.add(destination); } @@ -781,11 +774,11 @@ public class PDFDocument { * * @return the list of named destinations. */ - public List getDestinationList() { + public List<PDFDestination> getDestinationList() { if (hasDestinations()) { return this.destinations; } else { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } } @@ -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<? extends PDFObject> objects, OutputStream stream) + throws IOException { + for (PDFObject o : objects) { + streamIndirectObject(o, stream); + } + } + + private void recordObjectOffset(PDFObject object) { + int index = object.getObjectNumber() - 1; + while (indirectObjectOffsets.size() <= index) { + indirectObjectOffsets.add(null); + } + indirectObjectOffsets.set(index, position); + } + + /** + * 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 @@ -913,35 +913,6 @@ public class PDFFactory { } /** - * Creates and returns a StructTreeRoot object. Used for accessibility. - * @param parentTree the value of the ParenTree entry - * @return structure Tree Root element - */ - public PDFStructTreeRoot makeStructTreeRoot(PDFParentTree parentTree) { - PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(parentTree); - getDocument().assignObjectNumber(structTreeRoot); - getDocument().addTrailerObject(structTreeRoot); - getDocument().getRoot().setStructTreeRoot(structTreeRoot); - return structTreeRoot; - } - - /** - * Creates and returns a StructElem object. - * - * @param structureType the structure type of the new element (value for the - * S entry) - * @param parent the parent of the new structure element in the structure - * hierarchy - * @return the newly created element - */ - public PDFStructElem makeStructureElement(PDFName structureType, PDFObject parent) { - PDFStructElem structElem = new PDFStructElem(parent, structureType); - getDocument().assignObjectNumber(structElem); - getDocument().addTrailerObject(structElem); - return structElem; - } - - /** * Make a the head object of the name dictionary (the /Dests object). * * @param destinationList a list of PDFDestination instances 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<PDFFilter> filters = new java.util.ArrayList<PDFFilter>(); private boolean ignoreASCIIFilters = false; @@ -197,6 +198,10 @@ public class PDFFilterList { } } + List<PDFFilter> getFilters() { + return Collections.unmodifiableList(filters); + } + /** * Apply the filters to the data * in the order given and return the /Filter and /DecodeParms @@ -206,7 +211,7 @@ public class PDFFilterList { * @return a String representing the filter list */ protected String buildFilterDictEntries() { - if (filters != null && filters.size() > 0) { + if (filters.size() > 0) { List names = new java.util.ArrayList(); List parms = new java.util.ArrayList(); @@ -229,7 +234,7 @@ public class PDFFilterList { * @param dict the PDFDictionary to set the entries on */ protected void putFilterDictEntries(PDFDictionary dict) { - if (filters != null && filters.size() > 0) { + if (filters.size() > 0) { List names = new java.util.ArrayList(); List parms = new java.util.ArrayList(); @@ -358,7 +363,7 @@ public class PDFFilterList { */ public OutputStream applyFilters(OutputStream stream) throws IOException { OutputStream out = stream; - if (filters != null && !isDisableAllFilters()) { + if (!isDisableAllFilters()) { for (int count = filters.size() - 1; count >= 0; count--) { PDFFilter filter = (PDFFilter)filters.get(count); out = filter.applyFilter(out); 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<Integer, Object> 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<ObjectReference> objectReferences; + + public CrossReferenceStream(PDFDocument document, + int objectNumber, + TrailerDictionary trailerDictionary, + long startxref, + List<Long> uncompressedObjectReferences, + List<CompressedObjectReference> compressedObjectReferences) { + super(trailerDictionary, startxref); + this.document = document; + this.objectNumber = objectNumber; + this.objectReferences = new ArrayList<ObjectReference>(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<Long> objectReferences; + + private final StringBuilder pdf = new StringBuilder(256); + + public CrossReferenceTable(TrailerDictionary trailerDictionary, long startxref, + List<Long> 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/status.xml b/status.xml index 64c2d2ec3..b36097928 100644 --- a/status.xml +++ b/status.xml @@ -62,6 +62,11 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> <release version="FOP Trunk" date="TBD"> + <action context="Code" dev="PH,VH" type="add"> + 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. + </action> <action context="Code" dev="VH" type="add" fixes-bug="46962" due-to="Alexios Giotis"> Fixed deadlock in PropertyCache. </action> diff --git a/test/accessibility/pdf/background-image_png_single.pdf b/test/accessibility/pdf/background-image_png_single.pdf Binary files differdeleted file mode 100644 index 356e4885c..000000000 --- a/test/accessibility/pdf/background-image_png_single.pdf +++ /dev/null diff --git a/test/accessibility/pdf/complete.pdf b/test/accessibility/pdf/complete.pdf Binary files differdeleted file mode 100644 index cffb9e2cb..000000000 --- a/test/accessibility/pdf/complete.pdf +++ /dev/null diff --git a/test/accessibility/pdf/image_png.pdf b/test/accessibility/pdf/image_png.pdf Binary files differdeleted file mode 100644 index c3289374f..000000000 --- a/test/accessibility/pdf/image_png.pdf +++ /dev/null diff --git a/test/accessibility/pdf/image_wmf.pdf b/test/accessibility/pdf/image_wmf.pdf Binary files differdeleted file mode 100644 index b9ec8c55a..000000000 --- a/test/accessibility/pdf/image_wmf.pdf +++ /dev/null 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<CompressedObjectReference> 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<MockCompressedObject> 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 <code>"1 0 obj\n"</code> */ - protected final String beginObj = "1 0 obj\n"; - /** The string to end describing the object <code>"\nendobj\n"</code> */ - protected final String endObj = "\nendobj\n"; private static class DummyPDFObject extends PDFObject { @@ -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<Integer> 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<String, List<String>> 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<Long> uncompressedObjectOffsets; + + private List<CompressedObjectReference> compressedObjectReferences; + + @Test + public void testWithNoOffset() throws IOException { + List<Long> emptyList = Collections.emptyList(); + test(emptyList); + } + + @Test + public void testWithOffsets() throws IOException { + test(new ArrayList<Long>(Arrays.asList(0L, 1L, 2L, 3L, 4L))); + } + + @Test + public void testWithBigOffsets() throws IOException { + test(new ArrayList<Long>(Arrays.asList(0xffL, 0xffffL, 0xffffffffL, 0xffffffffffffffffL))); + } + + @Test + public void testWithObjectStreams1() throws IOException { + List<CompressedObjectReference> 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<Long> indirectObjectOffsets + = new ArrayList<Long>(numIndirectObjects + numCompressedObjects); + for (long i = 0; i < numIndirectObjects; i++) { + indirectObjectOffsets.add(i); + } + List<CompressedObjectReference> compressedObjectReferences + = new ArrayList<CompressedObjectReference>(); + 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<Long> indirectObjectOffsets) throws IOException { + List<CompressedObjectReference> compressedObjectReferences = Collections.emptyList(); + test(indirectObjectOffsets, compressedObjectReferences); + } + + private void test(List<Long> indirectObjectOffsets, + List<CompressedObjectReference> 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<ObjectReference> objectReferences + = new ArrayList<ObjectReference>(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<Long> offsets; + + @Test + public void testWithNoOffset() throws IOException { + List<Long> 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<Long> 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<Integer> expectedOffsetBytes) { + assert expectedOffsetBytes.size() <= 8; + long offset = 0; + for (int b : expectedOffsetBytes) { + offset = offset << 8 | b; + } + return offset; + } + + protected byte[] createExpectedOutput(byte field1, List<Integer> 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<Integer> expectedOffsetBytes) throws IOException { + List<Integer> expectedLongOffset = new ArrayList<Integer>(8); + expectedLongOffset.addAll(Arrays.asList(0, 0, 0, 0)); + expectedLongOffset.addAll(expectedOffsetBytes); + runTest(expectedLongOffset); + } + + private void runTest(List<Integer> 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<fop version="1.0"> + <accessibility>true</accessibility> + <source-resolution>144</source-resolution> + <use-cache>false</use-cache> + <font-base>../../resources/fonts/ttf/</font-base> + <renderers> + <renderer mime="application/pdf"> + <version>1.5</version> + <filterList> + <value>null</value> + </filterList> + <filterList type="image"> + <value>flate</value> + <value>ascii-85</value> + </filterList> + <fonts> + <font embed-url="DejaVuLGCSerif.ttf"> + <font-triplet name="DejaVu" style="normal" weight="normal"/> + </font> + </fonts> + </renderer> + </renderers> +</fop> 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 @@ +<?xml version="1.0" standalone="no"?> +<!-- + 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$ --> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" + xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" language="en" country="GB"> + <fo:layout-master-set> + <fo:simple-page-master master-name="page" + page-height="220pt" page-width="320pt" margin="10pt"> + <fo:region-body column-count="2" margin-top="15pt"/> + <fo:region-before extent="12pt"/> + </fo:simple-page-master> + </fo:layout-master-set> + <fo:page-sequence master-reference="page"> + <fo:static-content flow-name="xsl-region-before"> + <fo:block font-size="8pt" text-align-last="justify">This is the page header<fo:leader/>Page + <fo:page-number/></fo:block> + </fo:static-content> + <fo:static-content flow-name="xsl-footnote-separator"> + <fo:block><fo:leader leader-length="100pt" leader-pattern="rule"/></fo:block> + </fo:static-content> + <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> + <fo:block>(There’s another page sequence <fo:wrapper color="blue"><fo:basic-link + internal-destination="second">below</fo:basic-link></fo:wrapper>.)</fo:block> + <fo:block font-family="sans-serif" font-weight="bold" space-before="1em" space-after="0.2em" + role="H1"><fo:block>About Apache FOP</fo:block></fo:block> + <fo:block>It is a print formatter driven by XSL formatting objects (XSL-FO) and an output + independent formatter<fo:footnote><fo:inline baseline-shift="super" + font-size="70%">1</fo:inline><fo:footnote-body><fo:block>See the <fo:wrapper + color="blue"><fo:basic-link + external-destination="http://xmlgraphics.apache.org/fop/">FOP + website</fo:basic-link></fo:wrapper> for more + information</fo:block></fo:footnote-body></fo:footnote>. FOP has a nice logo: + <fo:external-graphic src="../../resources/images/fop-logo-color-24bit.png" + inline-progression-dimension.maximum="100%" content-width="scale-to-fit" + fox:alt-text="FOP Logo"/></fo:block> + <fo:table space-before="10pt" space-after="10pt" width="100%" table-layout="fixed"> + <fo:table-header> + <fo:table-row> + <fo:table-cell border="2pt solid black" padding="2pt 2pt 0"> + <fo:block>Header 1.1</fo:block> + </fo:table-cell> + <fo:table-cell border="2pt solid black" padding="2pt 2pt 0"> + <fo:block>Header 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-header> + <fo:table-body> + <fo:table-row> + <fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> + <fo:block>Cell 1.1</fo:block> + </fo:table-cell> + <fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> + <fo:block>Cell 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + <fo:table-row> + <fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> + <fo:block>Cell 2.1</fo:block> + </fo:table-cell> + <fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> + <fo:block>Cell 2.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + <fo:block>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.</fo:block> + <fo:block span="all" border-top="1pt solid black" border-bottom="1pt solid black" + padding-before="2pt" padding-after="2pt">This fo:block element spans all the columns of the + document. This is intended to test the abilities of the text-to-speech program.</fo:block> + <fo:block>And now we are back to normal content flowing in two columns. Let’s start a numbered + list:</fo:block> + <fo:list-block provisional-distance-between-starts="15pt" provisional-label-separation="0mm" + keep-with-previous="auto"> + <fo:list-item keep-with-previous="always"> + <fo:list-item-label end-indent="label-end()"> + <fo:block>1.</fo:block> + </fo:list-item-label> + <fo:list-item-body start-indent="body-start()"> + <fo:block> + <fo:block>Line 1 of item 1</fo:block> + <fo:block>Line 2 of item 1</fo:block> + <fo:block>Line 3 of item 1</fo:block> + </fo:block> + </fo:list-item-body> + </fo:list-item> + <fo:list-item keep-with-previous="always"> + <fo:list-item-label end-indent="label-end()"> + <fo:block>2.</fo:block> + </fo:list-item-label> + <fo:list-item-body start-indent="body-start()"> + <fo:block> + <fo:block>Line 1 of item 2</fo:block> + <fo:block>Line 2 of item 2</fo:block> + <fo:block>Line 3 of item 2</fo:block> + </fo:block> + </fo:list-item-body> + </fo:list-item> + </fo:list-block> + <fo:block>And now we are going to see how a second page sequence is handled.</fo:block> + </fo:flow> + </fo:page-sequence> + <fo:page-sequence master-reference="page"> + <fo:static-content flow-name="xsl-region-before"> + <fo:block font-size="8pt" text-align-last="justify">This is the page header<fo:leader/>Page + <fo:page-number/></fo:block> + </fo:static-content> + <fo:static-content flow-name="xsl-footnote-separator"> + <fo:block><fo:leader leader-length="100pt" leader-pattern="rule"/></fo:block> + </fo:static-content> + <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> + <fo:block id="second">Apache FOP (Formatting Objects Processor) is a print formatter driven by + XSL formatting objects (XSL-FO) and an output independent formatter<fo:footnote><fo:inline + baseline-shift="super" font-size="70%">1</fo:inline><fo:footnote-body><fo:block>See the + <fo:wrapper color="blue"><fo:basic-link + external-destination="http://xmlgraphics.apache.org/fop/">FOP + website</fo:basic-link></fo:wrapper> for more + information</fo:block></fo:footnote-body></fo:footnote>. It is a Java application that + reads a formatting object (FO) tree and renders the resulting pages to a specified + output.</fo:block> + <fo:table space-before="10pt" space-after="10pt" width="100%" table-layout="fixed"> + <fo:table-header> + <fo:table-row> + <fo:table-cell border="2pt solid black" padding="2pt 2pt 0"> + <fo:block>Header 1.1</fo:block> + </fo:table-cell> + <fo:table-cell border="2pt solid black" padding="2pt 2pt 0"> + <fo:block>Header 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-header> + <fo:table-body> + <fo:table-row> + <fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> + <fo:block>Cell 1.1</fo:block> + </fo:table-cell> + <fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> + <fo:block>Cell 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + <fo:table-row> + <fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> + <fo:block>Cell 2.1</fo:block> + </fo:table-cell> + <fo:table-cell border="1pt solid black" padding="2pt 2pt 0"> + <fo:block>Cell 2.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + <fo:block language="fr" country="FR">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.</fo:block> + <fo:block span="all" border-top="1pt solid black" border-bottom="1pt solid black" + padding-before="2pt" padding-after="2pt">This fo:block element spans all the columns of the + document. This is intended to test the abilities of the text-to-speech program.</fo:block> + <fo:block>And now we are back to normal content flowing in two columns. Let’s start a numbered + list:</fo:block> + <fo:list-block provisional-distance-between-starts="15pt" provisional-label-separation="0mm" + keep-with-previous="auto"> + <fo:list-item keep-with-previous="always"> + <fo:list-item-label end-indent="label-end()"> + <fo:block>1.</fo:block> + </fo:list-item-label> + <fo:list-item-body start-indent="body-start()"> + <fo:block> + <fo:block>Line 1 of item 1</fo:block> + <fo:block>Line 2 of item 1</fo:block> + <fo:block>Line 3 of item 1</fo:block> + </fo:block> + </fo:list-item-body> + </fo:list-item> + <fo:list-item keep-with-previous="always"> + <fo:list-item-label end-indent="label-end()"> + <fo:block>2.</fo:block> + </fo:list-item-label> + <fo:list-item-body start-indent="body-start()"> + <fo:block> + <fo:block>Line 1 of item 2</fo:block> + <fo:block>Line 2 of item 2</fo:block> + <fo:block>Line 3 of item 2</fo:block> + </fo:block> + </fo:list-item-body> + </fo:list-item> + </fo:list-block> + <fo:block>The end of the document has now been reached.</fo:block> + </fo:flow> + </fo:page-sequence> +</fo:root> diff --git a/test/pdf/1.5/test.pdf b/test/pdf/1.5/test.pdf Binary files differnew file mode 100644 index 000000000..4c25c0847 --- /dev/null +++ b/test/pdf/1.5/test.pdf diff --git a/test/accessibility/README b/test/pdf/accessibility/README index 1d2e04361..1d2e04361 100644 --- a/test/accessibility/README +++ b/test/pdf/accessibility/README diff --git a/test/accessibility/background-image_jpg_repeat.fo b/test/pdf/accessibility/background-image_jpg_repeat.fo index 727162e49..12af7d72a 100644 --- a/test/accessibility/background-image_jpg_repeat.fo +++ b/test/pdf/accessibility/background-image_jpg_repeat.fo @@ -20,7 +20,7 @@ <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> - <fo:region-body background-image="../resources/images/bgimg72dpi.jpg"/> + <fo:region-body background-image="../../resources/images/bgimg72dpi.jpg"/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="page"> diff --git a/test/accessibility/background-image_jpg_single.fo b/test/pdf/accessibility/background-image_jpg_single.fo index 335353e03..1efd10deb 100644 --- a/test/accessibility/background-image_jpg_single.fo +++ b/test/pdf/accessibility/background-image_jpg_single.fo @@ -20,7 +20,7 @@ <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> - <fo:region-body background-image="../resources/images/bgimg72dpi.jpg" + <fo:region-body background-image="../../resources/images/bgimg72dpi.jpg" background-repeat="no-repeat" background-position-horizontal="50%" background-position-vertical="50%"/> </fo:simple-page-master> diff --git a/test/accessibility/background-image_png_repeat.fo b/test/pdf/accessibility/background-image_png_repeat.fo index 5e4a8ba0d..9bbc3e47c 100644 --- a/test/accessibility/background-image_png_repeat.fo +++ b/test/pdf/accessibility/background-image_png_repeat.fo @@ -20,7 +20,7 @@ <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> - <fo:region-body background-image="../resources/images/bgimg72dpi.png"/> + <fo:region-body background-image="../../resources/images/bgimg72dpi.png"/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="page"> diff --git a/test/accessibility/background-image_png_single.fo b/test/pdf/accessibility/background-image_png_single.fo index 90067ec53..0cff427b6 100644 --- a/test/accessibility/background-image_png_single.fo +++ b/test/pdf/accessibility/background-image_png_single.fo @@ -20,7 +20,7 @@ <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> - <fo:region-body background-image="../resources/images/fop-logo-color-24bit.png" + <fo:region-body background-image="../../resources/images/fop-logo-color-24bit.png" background-repeat="no-repeat" background-position-horizontal="50%" background-position-vertical="50%"/> </fo:simple-page-master> diff --git a/test/accessibility/background-image_svg_repeat.fo b/test/pdf/accessibility/background-image_svg_repeat.fo index 02520b6cf..ba69947cc 100644 --- a/test/accessibility/background-image_svg_repeat.fo +++ b/test/pdf/accessibility/background-image_svg_repeat.fo @@ -20,7 +20,7 @@ <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> - <fo:region-body background-image="../resources/images/rgb-circles.svg"/> + <fo:region-body background-image="../../resources/images/rgb-circles.svg"/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="page"> diff --git a/test/accessibility/background-image_svg_single.fo b/test/pdf/accessibility/background-image_svg_single.fo index 3029f32ec..efe91c65b 100644 --- a/test/accessibility/background-image_svg_single.fo +++ b/test/pdf/accessibility/background-image_svg_single.fo @@ -20,7 +20,7 @@ <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> - <fo:region-body background-image="../resources/images/rgb-circles.svg" + <fo:region-body background-image="../../resources/images/rgb-circles.svg" background-repeat="no-repeat" background-position-horizontal="50%" background-position-vertical="50%"/> </fo:simple-page-master> diff --git a/test/accessibility/complete.fo b/test/pdf/accessibility/complete.fo index 75684750c..23e8405f3 100644 --- a/test/accessibility/complete.fo +++ b/test/pdf/accessibility/complete.fo @@ -45,7 +45,7 @@ external-destination="http://xmlgraphics.apache.org/fop/">FOP website</fo:basic-link></fo:wrapper> for more information</fo:block></fo:footnote-body></fo:footnote>. FOP has a nice logo: - <fo:external-graphic src="../resources/images/fop-logo-color-24bit.png" + <fo:external-graphic src="../../resources/images/fop-logo-color-24bit.png" inline-progression-dimension.maximum="100%" content-width="scale-to-fit" fox:alt-text="FOP Logo"/></fo:block> <fo:table space-before="10pt" space-after="10pt" width="100%" table-layout="fixed"> diff --git a/test/accessibility/fop.xconf b/test/pdf/accessibility/fop.xconf index 8c5dc2bd5..adfccd2cc 100644 --- a/test/accessibility/fop.xconf +++ b/test/pdf/accessibility/fop.xconf @@ -3,7 +3,7 @@ <accessibility>true</accessibility> <source-resolution>144</source-resolution> <use-cache>false</use-cache> - <font-base>../resources/fonts/</font-base> + <font-base>../../resources/fonts/ttf/</font-base> <renderers> <renderer mime="application/pdf"> <filterList> diff --git a/test/accessibility/image_jpg.fo b/test/pdf/accessibility/image_jpg.fo index 5fe36f61a..2a2b60076 100644 --- a/test/accessibility/image_jpg.fo +++ b/test/pdf/accessibility/image_jpg.fo @@ -27,7 +27,7 @@ <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>This document contains an image in the JPEG format: <fo:external-graphic - src="../resources/images/cmyk.jpg" + src="../../resources/images/cmyk.jpg" inline-progression-dimension.maximum="100%" content-width="scale-to-fit" fox:alt-text="CMYK colours"/>. Here is the end of the text.</fo:block> </fo:flow> diff --git a/test/accessibility/image_png.fo b/test/pdf/accessibility/image_png.fo index b529aa8c8..52ee80ac5 100644 --- a/test/accessibility/image_png.fo +++ b/test/pdf/accessibility/image_png.fo @@ -27,7 +27,7 @@ <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>This document contains an image in the PNG format: <fo:external-graphic - src="../resources/images/fop-logo-color-24bit.png" + src="../../resources/images/fop-logo-color-24bit.png" inline-progression-dimension.maximum="100%" content-width="scale-to-fit" fox:alt-text="FOP Logo"/>. Here is the end of the text.</fo:block> </fo:flow> diff --git a/test/accessibility/image_svg.fo b/test/pdf/accessibility/image_svg.fo index bbc77fe65..96cfedee3 100644 --- a/test/accessibility/image_svg.fo +++ b/test/pdf/accessibility/image_svg.fo @@ -27,7 +27,7 @@ <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>This document contains an image in the SVG format: <fo:external-graphic - src="../resources/images/circles.svg" + src="../../resources/images/circles.svg" inline-progression-dimension.maximum="75pt" content-width="scale-to-fit" fox:alt-text="Nice circles"/>. And here is the same image as an instream-foreign-object: <fo:instream-foreign-object inline-progression-dimension.maximum="75pt" diff --git a/test/accessibility/image_wmf.fo b/test/pdf/accessibility/image_wmf.fo index 1a4de777b..43112dba1 100644 --- a/test/accessibility/image_wmf.fo +++ b/test/pdf/accessibility/image_wmf.fo @@ -27,7 +27,7 @@ <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>This document contains an image in the WMF format: <fo:external-graphic - src="../resources/images/testChart.wmf" + src="../../resources/images/testChart.wmf" inline-progression-dimension.maximum="100%" content-width="scale-to-fit" fox:alt-text="Metafile Companion Test Chart"/> Here is the end of the text.</fo:block> </fo:flow> diff --git a/test/accessibility/leader.fo b/test/pdf/accessibility/leader.fo index ffd768021..4b395cc69 100644 --- a/test/accessibility/leader.fo +++ b/test/pdf/accessibility/leader.fo @@ -32,7 +32,7 @@ <fo:block space-before="10pt">This is a text followed by a leader with leader-pattern="use-content", the content being images:<fo:leader leader-pattern="use-content"><fo:external-graphic - src="../resources/images/list-item.png"/></fo:leader>1</fo:block> + src="../../resources/images/list-item.png"/></fo:leader>1</fo:block> </fo:flow> </fo:page-sequence> </fo:root> diff --git a/test/accessibility/links.fo b/test/pdf/accessibility/links.fo index 36250e332..36250e332 100644 --- a/test/accessibility/links.fo +++ b/test/pdf/accessibility/links.fo diff --git a/test/accessibility/pdf/background-image_jpg_repeat.pdf b/test/pdf/accessibility/pdf/background-image_jpg_repeat.pdf Binary files differindex 221761757..9b4a7fc20 100644 --- a/test/accessibility/pdf/background-image_jpg_repeat.pdf +++ b/test/pdf/accessibility/pdf/background-image_jpg_repeat.pdf diff --git a/test/accessibility/pdf/background-image_jpg_single.pdf b/test/pdf/accessibility/pdf/background-image_jpg_single.pdf Binary files differindex 232afdd05..d1d0ecb1b 100644 --- a/test/accessibility/pdf/background-image_jpg_single.pdf +++ b/test/pdf/accessibility/pdf/background-image_jpg_single.pdf diff --git a/test/accessibility/pdf/background-image_png_repeat.pdf b/test/pdf/accessibility/pdf/background-image_png_repeat.pdf Binary files differindex 52da4020a..ccb2cf089 100644 --- a/test/accessibility/pdf/background-image_png_repeat.pdf +++ b/test/pdf/accessibility/pdf/background-image_png_repeat.pdf diff --git a/test/pdf/accessibility/pdf/background-image_png_single.pdf b/test/pdf/accessibility/pdf/background-image_png_single.pdf Binary files differnew file mode 100644 index 000000000..902520b95 --- /dev/null +++ b/test/pdf/accessibility/pdf/background-image_png_single.pdf diff --git a/test/accessibility/pdf/background-image_svg_repeat.pdf b/test/pdf/accessibility/pdf/background-image_svg_repeat.pdf Binary files differindex 0bce2404d..a720a5b9a 100644 --- a/test/accessibility/pdf/background-image_svg_repeat.pdf +++ b/test/pdf/accessibility/pdf/background-image_svg_repeat.pdf diff --git a/test/accessibility/pdf/background-image_svg_single.pdf b/test/pdf/accessibility/pdf/background-image_svg_single.pdf Binary files differindex 3e4afcdcf..7e6e3e9d4 100644 --- a/test/accessibility/pdf/background-image_svg_single.pdf +++ b/test/pdf/accessibility/pdf/background-image_svg_single.pdf diff --git a/test/pdf/accessibility/pdf/complete.pdf b/test/pdf/accessibility/pdf/complete.pdf Binary files differnew file mode 100644 index 000000000..f1dc10559 --- /dev/null +++ b/test/pdf/accessibility/pdf/complete.pdf diff --git a/test/accessibility/pdf/image_jpg.pdf b/test/pdf/accessibility/pdf/image_jpg.pdf Binary files differindex cb004bbd2..e943c83e5 100644 --- a/test/accessibility/pdf/image_jpg.pdf +++ b/test/pdf/accessibility/pdf/image_jpg.pdf diff --git a/test/pdf/accessibility/pdf/image_png.pdf b/test/pdf/accessibility/pdf/image_png.pdf Binary files differnew file mode 100644 index 000000000..5ea44673b --- /dev/null +++ b/test/pdf/accessibility/pdf/image_png.pdf diff --git a/test/accessibility/pdf/image_svg.pdf b/test/pdf/accessibility/pdf/image_svg.pdf Binary files differindex c3fce5b0e..db1e061cf 100644 --- a/test/accessibility/pdf/image_svg.pdf +++ b/test/pdf/accessibility/pdf/image_svg.pdf diff --git a/test/pdf/accessibility/pdf/image_wmf.pdf b/test/pdf/accessibility/pdf/image_wmf.pdf Binary files differnew file mode 100644 index 000000000..65c46d12a --- /dev/null +++ b/test/pdf/accessibility/pdf/image_wmf.pdf diff --git a/test/accessibility/pdf/leader.pdf b/test/pdf/accessibility/pdf/leader.pdf Binary files differindex c7432e751..d270c25a1 100644 --- a/test/accessibility/pdf/leader.pdf +++ b/test/pdf/accessibility/pdf/leader.pdf diff --git a/test/accessibility/pdf/links.pdf b/test/pdf/accessibility/pdf/links.pdf Binary files differindex 91d7c2592..b2a5a4209 100644 --- a/test/accessibility/pdf/links.pdf +++ b/test/pdf/accessibility/pdf/links.pdf diff --git a/test/accessibility/pdf/role.pdf b/test/pdf/accessibility/pdf/role.pdf Binary files differindex acb435027..329925bcd 100644 --- a/test/accessibility/pdf/role.pdf +++ b/test/pdf/accessibility/pdf/role.pdf diff --git a/test/accessibility/pdf/role_non-standard.pdf b/test/pdf/accessibility/pdf/role_non-standard.pdf Binary files differindex fcf614ed1..84daea788 100644 --- a/test/accessibility/pdf/role_non-standard.pdf +++ b/test/pdf/accessibility/pdf/role_non-standard.pdf diff --git a/test/accessibility/pdf/text_1.pdf b/test/pdf/accessibility/pdf/text_1.pdf Binary files differindex 596419c9d..13f01711d 100644 --- a/test/accessibility/pdf/text_1.pdf +++ b/test/pdf/accessibility/pdf/text_1.pdf diff --git a/test/accessibility/pdf/text_2.pdf b/test/pdf/accessibility/pdf/text_2.pdf Binary files differindex 19fff21a4..943263994 100644 --- a/test/accessibility/pdf/text_2.pdf +++ b/test/pdf/accessibility/pdf/text_2.pdf diff --git a/test/accessibility/pdf/text_font-embedding.pdf b/test/pdf/accessibility/pdf/text_font-embedding.pdf Binary files differindex 0288449d5..7ffb40af8 100644 --- a/test/accessibility/pdf/text_font-embedding.pdf +++ b/test/pdf/accessibility/pdf/text_font-embedding.pdf diff --git a/test/accessibility/role.fo b/test/pdf/accessibility/role.fo index ced8a4d44..ced8a4d44 100644 --- a/test/accessibility/role.fo +++ b/test/pdf/accessibility/role.fo diff --git a/test/accessibility/role_non-standard.fo b/test/pdf/accessibility/role_non-standard.fo index d3e1a9852..d3e1a9852 100644 --- a/test/accessibility/role_non-standard.fo +++ b/test/pdf/accessibility/role_non-standard.fo diff --git a/test/accessibility/text_1.fo b/test/pdf/accessibility/text_1.fo index 31ad31514..31ad31514 100644 --- a/test/accessibility/text_1.fo +++ b/test/pdf/accessibility/text_1.fo diff --git a/test/accessibility/text_2.fo b/test/pdf/accessibility/text_2.fo index f5693110e..f5693110e 100644 --- a/test/accessibility/text_2.fo +++ b/test/pdf/accessibility/text_2.fo diff --git a/test/accessibility/text_font-embedding.fo b/test/pdf/accessibility/text_font-embedding.fo index 10c1c99d9..10c1c99d9 100644 --- a/test/accessibility/text_font-embedding.fo +++ b/test/pdf/accessibility/text_font-embedding.fo |