summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Hennebert <vhennebert@apache.org>2012-03-26 17:39:20 +0000
committerVincent Hennebert <vhennebert@apache.org>2012-03-26 17:39:20 +0000
commit4f9ccbd0b79cdb8eebbbdea994355f2df24e595b (patch)
tree19396231b63ee296159a2b846675c9e379be817c
parent2874695d34037474b121b41b178b70cbfb229490 (diff)
parentb9fbfa6b71b5e0d67806c066ea422819de08f3b2 (diff)
downloadxmlgraphics-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
-rw-r--r--checkstyle-5.5.xml8
-rw-r--r--src/java/org/apache/fop/pdf/AbstractPDFStream.java61
-rw-r--r--src/java/org/apache/fop/pdf/CompressedObject.java49
-rw-r--r--src/java/org/apache/fop/pdf/ObjectStream.java85
-rw-r--r--src/java/org/apache/fop/pdf/ObjectStreamManager.java69
-rw-r--r--src/java/org/apache/fop/pdf/PDFAnnotList.java5
-rw-r--r--src/java/org/apache/fop/pdf/PDFArray.java25
-rw-r--r--src/java/org/apache/fop/pdf/PDFCIDFont.java6
-rw-r--r--src/java/org/apache/fop/pdf/PDFCMap.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFDestination.java3
-rw-r--r--src/java/org/apache/fop/pdf/PDFDictionary.java11
-rw-r--r--src/java/org/apache/fop/pdf/PDFDocument.java390
-rw-r--r--src/java/org/apache/fop/pdf/PDFEncryption.java6
-rw-r--r--src/java/org/apache/fop/pdf/PDFEncryptionJCE.java24
-rw-r--r--src/java/org/apache/fop/pdf/PDFFactory.java29
-rw-r--r--src/java/org/apache/fop/pdf/PDFFilterList.java13
-rw-r--r--src/java/org/apache/fop/pdf/PDFFont.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFFormXObject.java4
-rw-r--r--src/java/org/apache/fop/pdf/PDFFunction.java24
-rw-r--r--src/java/org/apache/fop/pdf/PDFGState.java4
-rw-r--r--src/java/org/apache/fop/pdf/PDFGoTo.java4
-rw-r--r--src/java/org/apache/fop/pdf/PDFGoToRemote.java3
-rw-r--r--src/java/org/apache/fop/pdf/PDFICCBasedColorSpace.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFICCStream.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFImageXObject.java4
-rw-r--r--src/java/org/apache/fop/pdf/PDFInfo.java3
-rw-r--r--src/java/org/apache/fop/pdf/PDFLaunch.java3
-rw-r--r--src/java/org/apache/fop/pdf/PDFLink.java5
-rw-r--r--src/java/org/apache/fop/pdf/PDFMetadata.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFName.java13
-rw-r--r--src/java/org/apache/fop/pdf/PDFNumber.java6
-rw-r--r--src/java/org/apache/fop/pdf/PDFNumsArray.java11
-rw-r--r--src/java/org/apache/fop/pdf/PDFObject.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFOutline.java3
-rw-r--r--src/java/org/apache/fop/pdf/PDFOutputIntent.java3
-rw-r--r--src/java/org/apache/fop/pdf/PDFPages.java9
-rw-r--r--src/java/org/apache/fop/pdf/PDFPattern.java7
-rw-r--r--src/java/org/apache/fop/pdf/PDFResources.java4
-rw-r--r--src/java/org/apache/fop/pdf/PDFRoot.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFShading.java5
-rw-r--r--src/java/org/apache/fop/pdf/PDFStream.java30
-rw-r--r--src/java/org/apache/fop/pdf/PDFStructElem.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFT1Stream.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFTTFStream.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFText.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFUri.java3
-rw-r--r--src/java/org/apache/fop/pdf/PDFXObject.java4
-rw-r--r--src/java/org/apache/fop/pdf/Version.java7
-rw-r--r--src/java/org/apache/fop/pdf/xref/CompressedObjectReference.java66
-rw-r--r--src/java/org/apache/fop/pdf/xref/CrossReferenceObject.java46
-rw-r--r--src/java/org/apache/fop/pdf/xref/CrossReferenceStream.java107
-rw-r--r--src/java/org/apache/fop/pdf/xref/CrossReferenceTable.java73
-rw-r--r--src/java/org/apache/fop/pdf/xref/ObjectReference.java39
-rw-r--r--src/java/org/apache/fop/pdf/xref/TrailerDictionary.java94
-rw-r--r--src/java/org/apache/fop/pdf/xref/UncompressedObjectReference.java48
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java6
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java7
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java6
-rw-r--r--status.xml5
-rw-r--r--test/accessibility/pdf/background-image_png_single.pdfbin58309 -> 0 bytes
-rw-r--r--test/accessibility/pdf/complete.pdfbin96722 -> 0 bytes
-rw-r--r--test/accessibility/pdf/image_png.pdfbin57780 -> 0 bytes
-rw-r--r--test/accessibility/pdf/image_wmf.pdfbin304960 -> 0 bytes
-rw-r--r--test/java/org/apache/fop/pdf/AbstractPDFStreamTestCase.java8
-rw-r--r--test/java/org/apache/fop/pdf/ObjectStreamManagerTestCase.java113
-rw-r--r--test/java/org/apache/fop/pdf/ObjectStreamTestCase.java131
-rw-r--r--test/java/org/apache/fop/pdf/PDFEncryptionJCETestCase.java2
-rw-r--r--test/java/org/apache/fop/pdf/PDFFilterListTestCase.java33
-rw-r--r--test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java4
-rw-r--r--test/java/org/apache/fop/pdf/PDFObjectTestCase.java9
-rw-r--r--test/java/org/apache/fop/pdf/PDFStreamTestCase.java126
-rw-r--r--test/java/org/apache/fop/pdf/xref/CompressedObjectReferenceTestCase.java50
-rw-r--r--test/java/org/apache/fop/pdf/xref/CrossReferenceObjectTest.java106
-rw-r--r--test/java/org/apache/fop/pdf/xref/CrossReferenceStreamTestCase.java142
-rw-r--r--test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java80
-rw-r--r--test/java/org/apache/fop/pdf/xref/ObjectReferenceTest.java62
-rw-r--r--test/java/org/apache/fop/pdf/xref/UncompressedObjectReferenceTestCase.java90
-rw-r--r--test/pdf/1.5/fop.xconf24
-rw-r--r--test/pdf/1.5/test.fo207
-rw-r--r--test/pdf/1.5/test.pdfbin0 -> 94918 bytes
-rw-r--r--test/pdf/accessibility/README (renamed from test/accessibility/README)0
-rw-r--r--test/pdf/accessibility/background-image_jpg_repeat.fo (renamed from test/accessibility/background-image_jpg_repeat.fo)2
-rw-r--r--test/pdf/accessibility/background-image_jpg_single.fo (renamed from test/accessibility/background-image_jpg_single.fo)2
-rw-r--r--test/pdf/accessibility/background-image_png_repeat.fo (renamed from test/accessibility/background-image_png_repeat.fo)2
-rw-r--r--test/pdf/accessibility/background-image_png_single.fo (renamed from test/accessibility/background-image_png_single.fo)2
-rw-r--r--test/pdf/accessibility/background-image_svg_repeat.fo (renamed from test/accessibility/background-image_svg_repeat.fo)2
-rw-r--r--test/pdf/accessibility/background-image_svg_single.fo (renamed from test/accessibility/background-image_svg_single.fo)2
-rw-r--r--test/pdf/accessibility/complete.fo (renamed from test/accessibility/complete.fo)2
-rw-r--r--test/pdf/accessibility/fop.xconf (renamed from test/accessibility/fop.xconf)2
-rw-r--r--test/pdf/accessibility/image_jpg.fo (renamed from test/accessibility/image_jpg.fo)2
-rw-r--r--test/pdf/accessibility/image_png.fo (renamed from test/accessibility/image_png.fo)2
-rw-r--r--test/pdf/accessibility/image_svg.fo (renamed from test/accessibility/image_svg.fo)2
-rw-r--r--test/pdf/accessibility/image_wmf.fo (renamed from test/accessibility/image_wmf.fo)2
-rw-r--r--test/pdf/accessibility/leader.fo (renamed from test/accessibility/leader.fo)2
-rw-r--r--test/pdf/accessibility/links.fo (renamed from test/accessibility/links.fo)0
-rw-r--r--test/pdf/accessibility/pdf/background-image_jpg_repeat.pdf (renamed from test/accessibility/pdf/background-image_jpg_repeat.pdf)bin12806 -> 12814 bytes
-rw-r--r--test/pdf/accessibility/pdf/background-image_jpg_single.pdf (renamed from test/accessibility/pdf/background-image_jpg_single.pdf)bin12645 -> 12653 bytes
-rw-r--r--test/pdf/accessibility/pdf/background-image_png_repeat.pdf (renamed from test/accessibility/pdf/background-image_png_repeat.pdf)bin35429 -> 35437 bytes
-rw-r--r--test/pdf/accessibility/pdf/background-image_png_single.pdfbin0 -> 58648 bytes
-rw-r--r--test/pdf/accessibility/pdf/background-image_svg_repeat.pdf (renamed from test/accessibility/pdf/background-image_svg_repeat.pdf)bin17096 -> 17056 bytes
-rw-r--r--test/pdf/accessibility/pdf/background-image_svg_single.pdf (renamed from test/accessibility/pdf/background-image_svg_single.pdf)bin9935 -> 9931 bytes
-rw-r--r--test/pdf/accessibility/pdf/complete.pdfbin0 -> 97061 bytes
-rw-r--r--test/pdf/accessibility/pdf/image_jpg.pdf (renamed from test/accessibility/pdf/image_jpg.pdf)bin9070 -> 9078 bytes
-rw-r--r--test/pdf/accessibility/pdf/image_png.pdfbin0 -> 58119 bytes
-rw-r--r--test/pdf/accessibility/pdf/image_svg.pdf (renamed from test/accessibility/pdf/image_svg.pdf)bin14284 -> 14260 bytes
-rw-r--r--test/pdf/accessibility/pdf/image_wmf.pdfbin0 -> 248229 bytes
-rw-r--r--test/pdf/accessibility/pdf/leader.pdf (renamed from test/accessibility/pdf/leader.pdf)bin9371 -> 9379 bytes
-rw-r--r--test/pdf/accessibility/pdf/links.pdf (renamed from test/accessibility/pdf/links.pdf)bin9704 -> 9712 bytes
-rw-r--r--test/pdf/accessibility/pdf/role.pdf (renamed from test/accessibility/pdf/role.pdf)bin18968 -> 18976 bytes
-rw-r--r--test/pdf/accessibility/pdf/role_non-standard.pdf (renamed from test/accessibility/pdf/role_non-standard.pdf)bin18982 -> 18990 bytes
-rw-r--r--test/pdf/accessibility/pdf/text_1.pdf (renamed from test/accessibility/pdf/text_1.pdf)bin7496 -> 7504 bytes
-rw-r--r--test/pdf/accessibility/pdf/text_2.pdf (renamed from test/accessibility/pdf/text_2.pdf)bin10338 -> 10346 bytes
-rw-r--r--test/pdf/accessibility/pdf/text_font-embedding.pdf (renamed from test/accessibility/pdf/text_font-embedding.pdf)bin19777 -> 19741 bytes
-rw-r--r--test/pdf/accessibility/role.fo (renamed from test/accessibility/role.fo)0
-rw-r--r--test/pdf/accessibility/role_non-standard.fo (renamed from test/accessibility/role_non-standard.fo)0
-rw-r--r--test/pdf/accessibility/text_1.fo (renamed from test/accessibility/text_1.fo)0
-rw-r--r--test/pdf/accessibility/text_2.fo (renamed from test/accessibility/text_2.fo)0
-rw-r--r--test/pdf/accessibility/text_font-embedding.fo (renamed from test/accessibility/text_font-embedding.fo)0
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
deleted file mode 100644
index 356e4885c..000000000
--- a/test/accessibility/pdf/background-image_png_single.pdf
+++ /dev/null
Binary files differ
diff --git a/test/accessibility/pdf/complete.pdf b/test/accessibility/pdf/complete.pdf
deleted file mode 100644
index cffb9e2cb..000000000
--- a/test/accessibility/pdf/complete.pdf
+++ /dev/null
Binary files differ
diff --git a/test/accessibility/pdf/image_png.pdf b/test/accessibility/pdf/image_png.pdf
deleted file mode 100644
index c3289374f..000000000
--- a/test/accessibility/pdf/image_png.pdf
+++ /dev/null
Binary files differ
diff --git a/test/accessibility/pdf/image_wmf.pdf b/test/accessibility/pdf/image_wmf.pdf
deleted file mode 100644
index b9ec8c55a..000000000
--- a/test/accessibility/pdf/image_wmf.pdf
+++ /dev/null
Binary files differ
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
new file mode 100644
index 000000000..4c25c0847
--- /dev/null
+++ b/test/pdf/1.5/test.pdf
Binary files differ
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
index 221761757..9b4a7fc20 100644
--- a/test/accessibility/pdf/background-image_jpg_repeat.pdf
+++ b/test/pdf/accessibility/pdf/background-image_jpg_repeat.pdf
Binary files differ
diff --git a/test/accessibility/pdf/background-image_jpg_single.pdf b/test/pdf/accessibility/pdf/background-image_jpg_single.pdf
index 232afdd05..d1d0ecb1b 100644
--- a/test/accessibility/pdf/background-image_jpg_single.pdf
+++ b/test/pdf/accessibility/pdf/background-image_jpg_single.pdf
Binary files differ
diff --git a/test/accessibility/pdf/background-image_png_repeat.pdf b/test/pdf/accessibility/pdf/background-image_png_repeat.pdf
index 52da4020a..ccb2cf089 100644
--- a/test/accessibility/pdf/background-image_png_repeat.pdf
+++ b/test/pdf/accessibility/pdf/background-image_png_repeat.pdf
Binary files differ
diff --git a/test/pdf/accessibility/pdf/background-image_png_single.pdf b/test/pdf/accessibility/pdf/background-image_png_single.pdf
new file mode 100644
index 000000000..902520b95
--- /dev/null
+++ b/test/pdf/accessibility/pdf/background-image_png_single.pdf
Binary files differ
diff --git a/test/accessibility/pdf/background-image_svg_repeat.pdf b/test/pdf/accessibility/pdf/background-image_svg_repeat.pdf
index 0bce2404d..a720a5b9a 100644
--- a/test/accessibility/pdf/background-image_svg_repeat.pdf
+++ b/test/pdf/accessibility/pdf/background-image_svg_repeat.pdf
Binary files differ
diff --git a/test/accessibility/pdf/background-image_svg_single.pdf b/test/pdf/accessibility/pdf/background-image_svg_single.pdf
index 3e4afcdcf..7e6e3e9d4 100644
--- a/test/accessibility/pdf/background-image_svg_single.pdf
+++ b/test/pdf/accessibility/pdf/background-image_svg_single.pdf
Binary files differ
diff --git a/test/pdf/accessibility/pdf/complete.pdf b/test/pdf/accessibility/pdf/complete.pdf
new file mode 100644
index 000000000..f1dc10559
--- /dev/null
+++ b/test/pdf/accessibility/pdf/complete.pdf
Binary files differ
diff --git a/test/accessibility/pdf/image_jpg.pdf b/test/pdf/accessibility/pdf/image_jpg.pdf
index cb004bbd2..e943c83e5 100644
--- a/test/accessibility/pdf/image_jpg.pdf
+++ b/test/pdf/accessibility/pdf/image_jpg.pdf
Binary files differ
diff --git a/test/pdf/accessibility/pdf/image_png.pdf b/test/pdf/accessibility/pdf/image_png.pdf
new file mode 100644
index 000000000..5ea44673b
--- /dev/null
+++ b/test/pdf/accessibility/pdf/image_png.pdf
Binary files differ
diff --git a/test/accessibility/pdf/image_svg.pdf b/test/pdf/accessibility/pdf/image_svg.pdf
index c3fce5b0e..db1e061cf 100644
--- a/test/accessibility/pdf/image_svg.pdf
+++ b/test/pdf/accessibility/pdf/image_svg.pdf
Binary files differ
diff --git a/test/pdf/accessibility/pdf/image_wmf.pdf b/test/pdf/accessibility/pdf/image_wmf.pdf
new file mode 100644
index 000000000..65c46d12a
--- /dev/null
+++ b/test/pdf/accessibility/pdf/image_wmf.pdf
Binary files differ
diff --git a/test/accessibility/pdf/leader.pdf b/test/pdf/accessibility/pdf/leader.pdf
index c7432e751..d270c25a1 100644
--- a/test/accessibility/pdf/leader.pdf
+++ b/test/pdf/accessibility/pdf/leader.pdf
Binary files differ
diff --git a/test/accessibility/pdf/links.pdf b/test/pdf/accessibility/pdf/links.pdf
index 91d7c2592..b2a5a4209 100644
--- a/test/accessibility/pdf/links.pdf
+++ b/test/pdf/accessibility/pdf/links.pdf
Binary files differ
diff --git a/test/accessibility/pdf/role.pdf b/test/pdf/accessibility/pdf/role.pdf
index acb435027..329925bcd 100644
--- a/test/accessibility/pdf/role.pdf
+++ b/test/pdf/accessibility/pdf/role.pdf
Binary files differ
diff --git a/test/accessibility/pdf/role_non-standard.pdf b/test/pdf/accessibility/pdf/role_non-standard.pdf
index fcf614ed1..84daea788 100644
--- a/test/accessibility/pdf/role_non-standard.pdf
+++ b/test/pdf/accessibility/pdf/role_non-standard.pdf
Binary files differ
diff --git a/test/accessibility/pdf/text_1.pdf b/test/pdf/accessibility/pdf/text_1.pdf
index 596419c9d..13f01711d 100644
--- a/test/accessibility/pdf/text_1.pdf
+++ b/test/pdf/accessibility/pdf/text_1.pdf
Binary files differ
diff --git a/test/accessibility/pdf/text_2.pdf b/test/pdf/accessibility/pdf/text_2.pdf
index 19fff21a4..943263994 100644
--- a/test/accessibility/pdf/text_2.pdf
+++ b/test/pdf/accessibility/pdf/text_2.pdf
Binary files differ
diff --git a/test/accessibility/pdf/text_font-embedding.pdf b/test/pdf/accessibility/pdf/text_font-embedding.pdf
index 0288449d5..7ffb40af8 100644
--- a/test/accessibility/pdf/text_font-embedding.pdf
+++ b/test/pdf/accessibility/pdf/text_font-embedding.pdf
Binary files differ
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