Browse Source

Merged branch Temp_PDF_ObjectStreams


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1305467 13f79535-47bb-0310-9956-ffa450edef68
tags/fop-1_1rc1old
Vincent Hennebert 12 years ago
parent
commit
4f9ccbd0b7
100 changed files with 2242 additions and 433 deletions
  1. 0
    8
      checkstyle-5.5.xml
  2. 44
    17
      src/java/org/apache/fop/pdf/AbstractPDFStream.java
  3. 49
    0
      src/java/org/apache/fop/pdf/CompressedObject.java
  4. 85
    0
      src/java/org/apache/fop/pdf/ObjectStream.java
  5. 69
    0
      src/java/org/apache/fop/pdf/ObjectStreamManager.java
  6. 1
    4
      src/java/org/apache/fop/pdf/PDFAnnotList.java
  7. 12
    13
      src/java/org/apache/fop/pdf/PDFArray.java
  8. 2
    4
      src/java/org/apache/fop/pdf/PDFCIDFont.java
  9. 1
    1
      src/java/org/apache/fop/pdf/PDFCMap.java
  10. 1
    2
      src/java/org/apache/fop/pdf/PDFDestination.java
  11. 1
    10
      src/java/org/apache/fop/pdf/PDFDictionary.java
  12. 207
    183
      src/java/org/apache/fop/pdf/PDFDocument.java
  13. 4
    2
      src/java/org/apache/fop/pdf/PDFEncryption.java
  14. 8
    16
      src/java/org/apache/fop/pdf/PDFEncryptionJCE.java
  15. 0
    29
      src/java/org/apache/fop/pdf/PDFFactory.java
  16. 9
    4
      src/java/org/apache/fop/pdf/PDFFilterList.java
  17. 1
    1
      src/java/org/apache/fop/pdf/PDFFont.java
  18. 2
    2
      src/java/org/apache/fop/pdf/PDFFormXObject.java
  19. 9
    15
      src/java/org/apache/fop/pdf/PDFFunction.java
  20. 1
    3
      src/java/org/apache/fop/pdf/PDFGState.java
  21. 1
    3
      src/java/org/apache/fop/pdf/PDFGoTo.java
  22. 1
    2
      src/java/org/apache/fop/pdf/PDFGoToRemote.java
  23. 0
    2
      src/java/org/apache/fop/pdf/PDFICCBasedColorSpace.java
  24. 1
    1
      src/java/org/apache/fop/pdf/PDFICCStream.java
  25. 2
    2
      src/java/org/apache/fop/pdf/PDFImageXObject.java
  26. 1
    2
      src/java/org/apache/fop/pdf/PDFInfo.java
  27. 1
    2
      src/java/org/apache/fop/pdf/PDFLaunch.java
  28. 2
    3
      src/java/org/apache/fop/pdf/PDFLink.java
  29. 1
    1
      src/java/org/apache/fop/pdf/PDFMetadata.java
  30. 1
    12
      src/java/org/apache/fop/pdf/PDFName.java
  31. 0
    6
      src/java/org/apache/fop/pdf/PDFNumber.java
  32. 1
    10
      src/java/org/apache/fop/pdf/PDFNumsArray.java
  33. 1
    1
      src/java/org/apache/fop/pdf/PDFObject.java
  34. 1
    2
      src/java/org/apache/fop/pdf/PDFOutline.java
  35. 1
    2
      src/java/org/apache/fop/pdf/PDFOutputIntent.java
  36. 4
    5
      src/java/org/apache/fop/pdf/PDFPages.java
  37. 1
    6
      src/java/org/apache/fop/pdf/PDFPattern.java
  38. 2
    2
      src/java/org/apache/fop/pdf/PDFResources.java
  39. 1
    1
      src/java/org/apache/fop/pdf/PDFRoot.java
  40. 2
    3
      src/java/org/apache/fop/pdf/PDFShading.java
  41. 24
    6
      src/java/org/apache/fop/pdf/PDFStream.java
  42. 1
    1
      src/java/org/apache/fop/pdf/PDFStructElem.java
  43. 1
    1
      src/java/org/apache/fop/pdf/PDFT1Stream.java
  44. 1
    1
      src/java/org/apache/fop/pdf/PDFTTFStream.java
  45. 0
    2
      src/java/org/apache/fop/pdf/PDFText.java
  46. 1
    2
      src/java/org/apache/fop/pdf/PDFUri.java
  47. 4
    0
      src/java/org/apache/fop/pdf/PDFXObject.java
  48. 4
    3
      src/java/org/apache/fop/pdf/Version.java
  49. 66
    0
      src/java/org/apache/fop/pdf/xref/CompressedObjectReference.java
  50. 46
    0
      src/java/org/apache/fop/pdf/xref/CrossReferenceObject.java
  51. 107
    0
      src/java/org/apache/fop/pdf/xref/CrossReferenceStream.java
  52. 73
    0
      src/java/org/apache/fop/pdf/xref/CrossReferenceTable.java
  53. 39
    0
      src/java/org/apache/fop/pdf/xref/ObjectReference.java
  54. 94
    0
      src/java/org/apache/fop/pdf/xref/TrailerDictionary.java
  55. 48
    0
      src/java/org/apache/fop/pdf/xref/UncompressedObjectReference.java
  56. 3
    3
      src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java
  57. 5
    2
      src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
  58. 3
    3
      src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java
  59. 5
    0
      status.xml
  60. BIN
      test/accessibility/pdf/background-image_png_single.pdf
  61. BIN
      test/accessibility/pdf/complete.pdf
  62. BIN
      test/accessibility/pdf/image_png.pdf
  63. BIN
      test/accessibility/pdf/image_wmf.pdf
  64. 4
    4
      test/java/org/apache/fop/pdf/AbstractPDFStreamTestCase.java
  65. 113
    0
      test/java/org/apache/fop/pdf/ObjectStreamManagerTestCase.java
  66. 131
    0
      test/java/org/apache/fop/pdf/ObjectStreamTestCase.java
  67. 0
    2
      test/java/org/apache/fop/pdf/PDFEncryptionJCETestCase.java
  68. 33
    0
      test/java/org/apache/fop/pdf/PDFFilterListTestCase.java
  69. 3
    1
      test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java
  70. 2
    7
      test/java/org/apache/fop/pdf/PDFObjectTestCase.java
  71. 126
    0
      test/java/org/apache/fop/pdf/PDFStreamTestCase.java
  72. 50
    0
      test/java/org/apache/fop/pdf/xref/CompressedObjectReferenceTestCase.java
  73. 106
    0
      test/java/org/apache/fop/pdf/xref/CrossReferenceObjectTest.java
  74. 142
    0
      test/java/org/apache/fop/pdf/xref/CrossReferenceStreamTestCase.java
  75. 80
    0
      test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java
  76. 62
    0
      test/java/org/apache/fop/pdf/xref/ObjectReferenceTest.java
  77. 90
    0
      test/java/org/apache/fop/pdf/xref/UncompressedObjectReferenceTestCase.java
  78. 24
    0
      test/pdf/1.5/fop.xconf
  79. 207
    0
      test/pdf/1.5/test.fo
  80. BIN
      test/pdf/1.5/test.pdf
  81. 0
    0
      test/pdf/accessibility/README
  82. 1
    1
      test/pdf/accessibility/background-image_jpg_repeat.fo
  83. 1
    1
      test/pdf/accessibility/background-image_jpg_single.fo
  84. 1
    1
      test/pdf/accessibility/background-image_png_repeat.fo
  85. 1
    1
      test/pdf/accessibility/background-image_png_single.fo
  86. 1
    1
      test/pdf/accessibility/background-image_svg_repeat.fo
  87. 1
    1
      test/pdf/accessibility/background-image_svg_single.fo
  88. 1
    1
      test/pdf/accessibility/complete.fo
  89. 1
    1
      test/pdf/accessibility/fop.xconf
  90. 1
    1
      test/pdf/accessibility/image_jpg.fo
  91. 1
    1
      test/pdf/accessibility/image_png.fo
  92. 1
    1
      test/pdf/accessibility/image_svg.fo
  93. 1
    1
      test/pdf/accessibility/image_wmf.fo
  94. 1
    1
      test/pdf/accessibility/leader.fo
  95. 0
    0
      test/pdf/accessibility/links.fo
  96. BIN
      test/pdf/accessibility/pdf/background-image_jpg_repeat.pdf
  97. BIN
      test/pdf/accessibility/pdf/background-image_jpg_single.pdf
  98. BIN
      test/pdf/accessibility/pdf/background-image_png_repeat.pdf
  99. BIN
      test/pdf/accessibility/pdf/background-image_png_single.pdf
  100. 0
    0
      test/pdf/accessibility/pdf/background-image_svg_repeat.pdf

+ 0
- 8
checkstyle-5.5.xml View File

@@ -255,14 +255,6 @@
<module name="UpperEll"/>
<!-- ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... -->

<!-- ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... -->
<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 -->

+ 44
- 17
src/java/org/apache/fop/pdf/AbstractPDFStream.java View File

@@ -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);
}
}


+ 49
- 0
src/java/org/apache/fop/pdf/CompressedObject.java View File

@@ -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;

}

+ 85
- 0
src/java/org/apache/fop/pdf/ObjectStream.java View File

@@ -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);
}
}

+ 69
- 0
src/java/org/apache/fop/pdf/ObjectStreamManager.java View File

@@ -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;
}
}

+ 1
- 4
src/java/org/apache/fop/pdf/PDFAnnotList.java View File

@@ -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
*/
}

+ 12
- 13
src/java/org/apache/fop/pdf/PDFArray.java View File

@@ -21,7 +21,6 @@ package org.apache.fop.pdf;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.List;

import org.apache.commons.io.output.CountingOutputStream;
@@ -48,7 +47,7 @@ public class PDFArray extends PDFObject {
* Create a new, empty array object with no parent.
*/
public PDFArray() {
this(null);
this((PDFObject) null);
}

/**
@@ -84,13 +83,22 @@ public class PDFArray extends PDFObject {
* @param parent the array's parent if any
* @param values the actual values wrapped by this object
*/
public PDFArray(PDFObject parent, Collection<Object> values) {
public PDFArray(PDFObject parent, List<?> values) {
/* generic creation of PDF object */
super(parent);

this.values.addAll(values);
}

/**
* Creates an array object made of the given elements.
*
* @param elements the array content
*/
public PDFArray(Object... elements) {
this(null, elements);
}

/**
* Create the array object
* @param parent the array's parent if any
@@ -180,13 +188,9 @@ public class PDFArray extends PDFObject {

/** {@inheritDoc} */
@Override
protected int output(OutputStream stream) throws IOException {
public int output(OutputStream stream) throws IOException {
CountingOutputStream cout = new CountingOutputStream(stream);
StringBuilder textBuffer = new StringBuilder(64);
if (hasObjectNumber()) {
textBuffer.append(getObjectID());
}

textBuffer.append('[');
for (int i = 0; i < values.size(); i++) {
if (i > 0) {
@@ -196,11 +200,6 @@ public class PDFArray extends PDFObject {
formatObject(obj, cout, textBuffer);
}
textBuffer.append(']');

if (hasObjectNumber()) {
textBuffer.append("\nendobj\n");
}

PDFDocument.flushTextBuffer(textBuffer, cout);
return cout.getCount();
}

+ 2
- 4
src/java/org/apache/fop/pdf/PDFCIDFont.java View File

@@ -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();
}


+ 1
- 1
src/java/org/apache/fop/pdf/PDFCMap.java View File

@@ -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);

+ 1
- 2
src/java/org/apache/fop/pdf/PDFDestination.java View File

@@ -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);


+ 1
- 10
src/java/org/apache/fop/pdf/PDFDictionary.java View File

@@ -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();
}

+ 207
- 183
src/java/org/apache/fop/pdf/PDFDocument.java View File

@@ -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.
*
@@ -265,17 +248,6 @@ public class PDFDocument {
return this.factory;
}

/**
* Indicates whether stream encoding on-the-fly is enabled. If enabled
* stream can be serialized without the need for a buffer to merely
* calculate the stream length.
*
* @return <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.
*
@@ -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;
}

@@ -367,6 +339,37 @@ public class PDFDocument {
return this.root;
}

/**
* Creates and returns a StructTreeRoot object.
*
* @param parentTree the value of the ParenTree entry
* @return the structure tree root
*/
public PDFStructTreeRoot makeStructTreeRoot(PDFParentTree parentTree) {
PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(parentTree);
assignObjectNumber(structTreeRoot);
addTrailerObject(structTreeRoot);
root.setStructTreeRoot(structTreeRoot);
structureTreeElements = new ArrayList<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.
*
@@ -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() {

+ 4
- 2
src/java/org/apache/fop/pdf/PDFEncryption.java View File

@@ -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();
}

+ 8
- 16
src/java/org/apache/fop/pdf/PDFEncryptionJCE.java View File

@@ -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) {

+ 0
- 29
src/java/org/apache/fop/pdf/PDFFactory.java View File

@@ -912,35 +912,6 @@ public class PDFFactory {
return pageLabels;
}

/**
* Creates and returns a StructTreeRoot object. Used for accessibility.
* @param parentTree the value of the ParenTree entry
* @return structure Tree Root element
*/
public PDFStructTreeRoot makeStructTreeRoot(PDFParentTree parentTree) {
PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(parentTree);
getDocument().assignObjectNumber(structTreeRoot);
getDocument().addTrailerObject(structTreeRoot);
getDocument().getRoot().setStructTreeRoot(structTreeRoot);
return structTreeRoot;
}

/**
* Creates and returns a StructElem object.
*
* @param structureType the structure type of the new element (value for the
* S entry)
* @param parent the parent of the new structure element in the structure
* hierarchy
* @return the newly created element
*/
public PDFStructElem makeStructureElement(PDFName structureType, PDFObject parent) {
PDFStructElem structElem = new PDFStructElem(parent, structureType);
getDocument().assignObjectNumber(structElem);
getDocument().addTrailerObject(structElem);
return structElem;
}

/**
* Make a the head object of the name dictionary (the /Dests object).
*

+ 9
- 4
src/java/org/apache/fop/pdf/PDFFilterList.java View File

@@ -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);

+ 1
- 1
src/java/org/apache/fop/pdf/PDFFont.java View File

@@ -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);
}

+ 2
- 2
src/java/org/apache/fop/pdf/PDFFormXObject.java View File

@@ -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.

+ 9
- 15
src/java/org/apache/fop/pdf/PDFFunction.java View File

@@ -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");

}


+ 1
- 3
src/java/org/apache/fop/pdf/PDFGState.java View File

@@ -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();
}


+ 1
- 3
src/java/org/apache/fop/pdf/PDFGoTo.java View File

@@ -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 + ">>";
}

/*

+ 1
- 2
src/java/org/apache/fop/pdf/PDFGoToRemote.java View File

@@ -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();
}

+ 0
- 2
src/java/org/apache/fop/pdf/PDFICCBasedColorSpace.java View File

@@ -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();
}


+ 1
- 1
src/java/org/apache/fop/pdf/PDFICCStream.java View File

@@ -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

+ 2
- 2
src/java/org/apache/fop/pdf/PDFImageXObject.java View File

@@ -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} */

+ 1
- 2
src/java/org/apache/fop/pdf/PDFInfo.java View File

@@ -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);
}

+ 1
- 2
src/java/org/apache/fop/pdf/PDFLaunch.java View File

@@ -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();
}

+ 2
- 3
src/java/org/apache/fop/pdf/PDFLink.java View File

@@ -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;
}


+ 1
- 1
src/java/org/apache/fop/pdf/PDFMetadata.java View File

@@ -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

+ 1
- 12
src/java/org/apache/fop/pdf/PDFName.java View File

@@ -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();
}

+ 0
- 6
src/java/org/apache/fop/pdf/PDFNumber.java View File

@@ -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();
}


+ 1
- 10
src/java/org/apache/fop/pdf/PDFNumsArray.java View File

@@ -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();
}

+ 1
- 1
src/java/org/apache/fop/pdf/PDFObject.java View File

@@ -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;

+ 1
- 2
src/java/org/apache/fop/pdf/PDFOutline.java View File

@@ -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);
}

+ 1
- 2
src/java/org/apache/fop/pdf/PDFOutputIntent.java View File

@@ -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);
}

+ 4
- 5
src/java/org/apache/fop/pdf/PDFPages.java View File

@@ -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();
}


+ 1
- 6
src/java/org/apache/fop/pdf/PDFPattern.java View File

@@ -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;
}


+ 2
- 2
src/java/org/apache/fop/pdf/PDFResources.java View File

@@ -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);
}

+ 1
- 1
src/java/org/apache/fop/pdf/PDFRoot.java View File

@@ -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);
}

+ 2
- 3
src/java/org/apache/fop/pdf/PDFShading.java View File

@@ -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());
}

+ 24
- 6
src/java/org/apache/fop/pdf/PDFStream.java View File

@@ -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.

+ 1
- 1
src/java/org/apache/fop/pdf/PDFStructElem.java View File

@@ -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;


+ 1
- 1
src/java/org/apache/fop/pdf/PDFT1Stream.java View File

@@ -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");

+ 1
- 1
src/java/org/apache/fop/pdf/PDFTTFStream.java View File

@@ -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");

+ 0
- 2
src/java/org/apache/fop/pdf/PDFText.java View File

@@ -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();
}


+ 1
- 2
src/java/org/apache/fop/pdf/PDFUri.java View File

@@ -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();
}

}

+ 4
- 0
src/java/org/apache/fop/pdf/PDFXObject.java View File

@@ -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

+ 4
- 3
src/java/org/apache/fop/pdf/Version.java View File

@@ -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()) {

+ 66
- 0
src/java/org/apache/fop/pdf/xref/CompressedObjectReference.java View File

@@ -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;
}

}

+ 46
- 0
src/java/org/apache/fop/pdf/xref/CrossReferenceObject.java View File

@@ -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;
}

+ 107
- 0
src/java/org/apache/fop/pdf/xref/CrossReferenceStream.java View File

@@ -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});
}

}

+ 73
- 0
src/java/org/apache/fop/pdf/xref/CrossReferenceTable.java View File

@@ -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);
}

}

+ 39
- 0
src/java/org/apache/fop/pdf/xref/ObjectReference.java View File

@@ -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;
}

+ 94
- 0
src/java/org/apache/fop/pdf/xref/TrailerDictionary.java View File

@@ -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;
}

}

+ 48
- 0
src/java/org/apache/fop/pdf/xref/UncompressedObjectReference.java View File

@@ -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);
}

}

+ 3
- 3
src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java View File

@@ -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);

+ 5
- 2
src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java View File

@@ -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;
}


+ 3
- 3
src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java View File

@@ -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);
}

+ 5
- 0
status.xml View File

@@ -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>

BIN
test/accessibility/pdf/background-image_png_single.pdf View File


BIN
test/accessibility/pdf/complete.pdf View File


BIN
test/accessibility/pdf/image_png.pdf View File


BIN
test/accessibility/pdf/image_wmf.pdf View File


+ 4
- 4
test/java/org/apache/fop/pdf/AbstractPDFStreamTestCase.java View File

@@ -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() {

+ 113
- 0
test/java/org/apache/fop/pdf/ObjectStreamManagerTestCase.java View File

@@ -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);
}
}

+ 131
- 0
test/java/org/apache/fop/pdf/ObjectStreamTestCase.java View File

@@ -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;
}
}

}

+ 0
- 2
test/java/org/apache/fop/pdf/PDFEncryptionJCETestCase.java View File

@@ -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 + ")")

+ 33
- 0
test/java/org/apache/fop/pdf/PDFFilterListTestCase.java View File

@@ -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());
}
}

+ 3
- 1
test/java/org/apache/fop/pdf/PDFLibraryTestSuite.java View File

@@ -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 {
}

+ 2
- 7
test/java/org/apache/fop/pdf/PDFObjectTestCase.java View File

@@ -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());
}
}

+ 126
- 0
test/java/org/apache/fop/pdf/PDFStreamTestCase.java View File

@@ -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();
}
}

+ 50
- 0
test/java/org/apache/fop/pdf/xref/CompressedObjectReferenceTestCase.java View File

@@ -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);
}

}

+ 106
- 0
test/java/org/apache/fop/pdf/xref/CrossReferenceObjectTest.java View File

@@ -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();
}

}

+ 142
- 0
test/java/org/apache/fop/pdf/xref/CrossReferenceStreamTestCase.java View File

@@ -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();
}

}

+ 80
- 0
test/java/org/apache/fop/pdf/xref/CrossReferenceTableTestCase.java View File

@@ -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);
}

}

+ 62
- 0
test/java/org/apache/fop/pdf/xref/ObjectReferenceTest.java View File

@@ -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();
}

}

+ 90
- 0
test/java/org/apache/fop/pdf/xref/UncompressedObjectReferenceTestCase.java View File

@@ -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);
}

}

+ 24
- 0
test/pdf/1.5/fop.xconf View File

@@ -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>

+ 207
- 0
test/pdf/1.5/test.fo View File

@@ -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>

BIN
test/pdf/1.5/test.pdf View File


test/accessibility/README → test/pdf/accessibility/README View File


test/accessibility/background-image_jpg_repeat.fo → test/pdf/accessibility/background-image_jpg_repeat.fo View File

@@ -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">

test/accessibility/background-image_jpg_single.fo → test/pdf/accessibility/background-image_jpg_single.fo View File

@@ -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>

test/accessibility/background-image_png_repeat.fo → test/pdf/accessibility/background-image_png_repeat.fo View File

@@ -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">

test/accessibility/background-image_png_single.fo → test/pdf/accessibility/background-image_png_single.fo View File

@@ -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>

test/accessibility/background-image_svg_repeat.fo → test/pdf/accessibility/background-image_svg_repeat.fo View File

@@ -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">

test/accessibility/background-image_svg_single.fo → test/pdf/accessibility/background-image_svg_single.fo View File

@@ -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>

test/accessibility/complete.fo → test/pdf/accessibility/complete.fo View File

@@ -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">

test/accessibility/fop.xconf → test/pdf/accessibility/fop.xconf View File

@@ -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>

test/accessibility/image_jpg.fo → test/pdf/accessibility/image_jpg.fo View File

@@ -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>

test/accessibility/image_png.fo → test/pdf/accessibility/image_png.fo View File

@@ -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>

test/accessibility/image_svg.fo → test/pdf/accessibility/image_svg.fo View File

@@ -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"

test/accessibility/image_wmf.fo → test/pdf/accessibility/image_wmf.fo View File

@@ -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>

test/accessibility/leader.fo → test/pdf/accessibility/leader.fo View File

@@ -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>

test/accessibility/links.fo → test/pdf/accessibility/links.fo View File


BIN
test/accessibility/pdf/background-image_jpg_repeat.pdf → test/pdf/accessibility/pdf/background-image_jpg_repeat.pdf View File


BIN
test/accessibility/pdf/background-image_jpg_single.pdf → test/pdf/accessibility/pdf/background-image_jpg_single.pdf View File


BIN
test/accessibility/pdf/background-image_png_repeat.pdf → test/pdf/accessibility/pdf/background-image_png_repeat.pdf View File


BIN
test/pdf/accessibility/pdf/background-image_png_single.pdf View File


test/accessibility/pdf/background-image_svg_repeat.pdf → test/pdf/accessibility/pdf/background-image_svg_repeat.pdf View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save