git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1305467 13f79535-47bb-0310-9956-ffa450edef68tags/fop-1_1rc1old
<module name="UpperEll"/> | <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"> | <module name="WhitespaceAfter"> | ||||
<property name="tokens" value="COMMA,SEMI"/> <!-- adding TYPECAST produces 2203 new errors --> | <property name="tokens" value="COMMA,SEMI"/> <!-- adding TYPECAST produces 2203 new errors --> |
/** | /** | ||||
* This is an abstract base class for PDF streams. | * 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 */ | /** The filters that should be applied */ | ||||
private PDFFilterList filters; | 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); | |||||
} | } | ||||
/** | /** | ||||
* {@inheritDoc} | * {@inheritDoc} | ||||
*/ | */ | ||||
@Override | @Override | ||||
protected int output(OutputStream stream) throws IOException { | |||||
public int output(OutputStream stream) throws IOException { | |||||
setupFilterList(); | setupFilterList(); | ||||
CountingOutputStream cout = new CountingOutputStream(stream); | CountingOutputStream cout = new CountingOutputStream(stream); | ||||
StringBuilder textBuffer = new StringBuilder(64); | StringBuilder textBuffer = new StringBuilder(64); | ||||
textBuffer.append(getObjectID()); | |||||
StreamCache encodedStream = null; | StreamCache encodedStream = null; | ||||
PDFNumber refLength = null; | PDFNumber refLength = null; | ||||
final Object lengthEntry; | final Object lengthEntry; | ||||
if (isEncodingOnTheFly()) { | |||||
if (encodeOnTheFly) { | |||||
refLength = new PDFNumber(); | refLength = new PDFNumber(); | ||||
getDocumentSafely().registerObject(refLength); | getDocumentSafely().registerObject(refLength); | ||||
lengthEntry = refLength; | lengthEntry = refLength; | ||||
} | } | ||||
populateStreamDict(lengthEntry); | populateStreamDict(lengthEntry); | ||||
writeDictionary(cout, textBuffer); | |||||
dictionary.writeDictionary(cout, textBuffer); | |||||
//Send encoded stream to target OutputStream | //Send encoded stream to target OutputStream | ||||
PDFDocument.flushTextBuffer(textBuffer, cout); | PDFDocument.flushTextBuffer(textBuffer, cout); | ||||
encodedStream.clear(); //Encoded stream can now be discarded | encodedStream.clear(); //Encoded stream can now be discarded | ||||
} | } | ||||
textBuffer.append("\nendobj\n"); | |||||
PDFDocument.flushTextBuffer(textBuffer, cout); | PDFDocument.flushTextBuffer(textBuffer, cout); | ||||
return cout.getCount(); | 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); | |||||
} | } | ||||
/** | /** | ||||
protected void populateStreamDict(Object lengthEntry) { | protected void populateStreamDict(Object lengthEntry) { | ||||
put("Length", lengthEntry); | put("Length", lengthEntry); | ||||
if (!getFilterList().isDisableAllFilters()) { | if (!getFilterList().isDisableAllFilters()) { | ||||
getFilterList().putFilterDictEntries(this); | |||||
getFilterList().putFilterDictEntries(dictionary); | |||||
} | } | ||||
} | } | ||||
/* | |||||
* 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; | |||||
} |
/* | |||||
* 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); | |||||
} | |||||
} |
/* | |||||
* 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; | |||||
} | |||||
} |
*/ | */ | ||||
public String toPDFString() { | public String toPDFString() { | ||||
StringBuffer p = new StringBuffer(128); | StringBuffer p = new StringBuffer(128); | ||||
p.append(getObjectID()); | |||||
p.append("[\n"); | p.append("[\n"); | ||||
for (int i = 0; i < getCount(); i++) { | for (int i = 0; i < getCount(); i++) { | ||||
p.append(((PDFObject)links.get(i)).referencePDF()); | p.append(((PDFObject)links.get(i)).referencePDF()); | ||||
p.append("\n"); | p.append("\n"); | ||||
} | } | ||||
p.append("]\nendobj\n"); | |||||
p.append("]"); | |||||
return p.toString(); | return p.toString(); | ||||
} | } | ||||
/* | /* | ||||
* example | * example | ||||
* 20 0 obj | |||||
* [ | * [ | ||||
* 19 0 R | * 19 0 R | ||||
* ] | * ] | ||||
* endobj | |||||
*/ | */ | ||||
} | } |
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.OutputStream; | import java.io.OutputStream; | ||||
import java.util.Collection; | |||||
import java.util.List; | import java.util.List; | ||||
import org.apache.commons.io.output.CountingOutputStream; | import org.apache.commons.io.output.CountingOutputStream; | ||||
* Create a new, empty array object with no parent. | * Create a new, empty array object with no parent. | ||||
*/ | */ | ||||
public PDFArray() { | public PDFArray() { | ||||
this(null); | |||||
this((PDFObject) null); | |||||
} | } | ||||
/** | /** | ||||
* @param parent the array's parent if any | * @param parent the array's parent if any | ||||
* @param values the actual values wrapped by this object | * @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 */ | /* generic creation of PDF object */ | ||||
super(parent); | super(parent); | ||||
this.values.addAll(values); | 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 | * Create the array object | ||||
* @param parent the array's parent if any | * @param parent the array's parent if any | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
@Override | @Override | ||||
protected int output(OutputStream stream) throws IOException { | |||||
public int output(OutputStream stream) throws IOException { | |||||
CountingOutputStream cout = new CountingOutputStream(stream); | CountingOutputStream cout = new CountingOutputStream(stream); | ||||
StringBuilder textBuffer = new StringBuilder(64); | StringBuilder textBuffer = new StringBuilder(64); | ||||
if (hasObjectNumber()) { | |||||
textBuffer.append(getObjectID()); | |||||
} | |||||
textBuffer.append('['); | textBuffer.append('['); | ||||
for (int i = 0; i < values.size(); i++) { | for (int i = 0; i < values.size(); i++) { | ||||
if (i > 0) { | if (i > 0) { | ||||
formatObject(obj, cout, textBuffer); | formatObject(obj, cout, textBuffer); | ||||
} | } | ||||
textBuffer.append(']'); | textBuffer.append(']'); | ||||
if (hasObjectNumber()) { | |||||
textBuffer.append("\nendobj\n"); | |||||
} | |||||
PDFDocument.flushTextBuffer(textBuffer, cout); | PDFDocument.flushTextBuffer(textBuffer, cout); | ||||
return cout.getCount(); | return cout.getCount(); | ||||
} | } |
*/ | */ | ||||
public String toPDFString() { | public String toPDFString() { | ||||
StringBuffer p = new StringBuffer(128); | StringBuffer p = new StringBuffer(128); | ||||
p.append(getObjectID()); | |||||
p.append("<< /Type /Font"); | p.append("<< /Type /Font"); | ||||
p.append("\n/BaseFont /"); | p.append("\n/BaseFont /"); | ||||
p.append(this.basefont); | p.append(this.basefont); | ||||
p.append("\n/DW2 ["); // always two values, see p 211 | p.append("\n/DW2 ["); // always two values, see p 211 | ||||
p.append(this.dw2[0]); | p.append(this.dw2[0]); | ||||
p.append(this.dw2[1]); | p.append(this.dw2[1]); | ||||
p.append("] \n>>\nendobj\n"); | |||||
p.append("]"); | |||||
} | } | ||||
if (w2 != null) { | if (w2 != null) { | ||||
p.append("\n/W2 "); | p.append("\n/W2 "); | ||||
p.append(w2.toPDFString()); | p.append(w2.toPDFString()); | ||||
p.append(" \n>>\nendobj\n"); | |||||
} | } | ||||
p.append(" \n>>\nendobj\n"); | |||||
p.append("\n>>"); | |||||
return p.toString(); | return p.toString(); | ||||
} | } | ||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
protected int output(OutputStream stream) throws IOException { | |||||
public int output(OutputStream stream) throws IOException { | |||||
CMapBuilder builder = createCMapBuilder(getBufferWriter()); | CMapBuilder builder = createCMapBuilder(getBufferWriter()); | ||||
builder.writeCMap(); | builder.writeCMap(); | ||||
return super.output(stream); | return super.output(stream); |
this.idRef = idRef; | this.idRef = idRef; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
@Override | @Override | ||||
protected int output(OutputStream stream) throws IOException { | |||||
public int output(OutputStream stream) throws IOException { | |||||
CountingOutputStream cout = new CountingOutputStream(stream); | CountingOutputStream cout = new CountingOutputStream(stream); | ||||
StringBuilder textBuffer = new StringBuilder(64); | StringBuilder textBuffer = new StringBuilder(64); | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
@Override | @Override | ||||
protected int output(OutputStream stream) throws IOException { | |||||
public int output(OutputStream stream) throws IOException { | |||||
CountingOutputStream cout = new CountingOutputStream(stream); | CountingOutputStream cout = new CountingOutputStream(stream); | ||||
StringBuilder textBuffer = new StringBuilder(64); | StringBuilder textBuffer = new StringBuilder(64); | ||||
if (hasObjectNumber()) { | |||||
textBuffer.append(getObjectID()); | |||||
} | |||||
writeDictionary(cout, textBuffer); | writeDictionary(cout, textBuffer); | ||||
if (hasObjectNumber()) { | |||||
textBuffer.append("\nendobj\n"); | |||||
} | |||||
PDFDocument.flushTextBuffer(textBuffer, cout); | PDFDocument.flushTextBuffer(textBuffer, cout); | ||||
return cout.getCount(); | return cout.getCount(); | ||||
} | } |
import java.io.UnsupportedEncodingException; | import java.io.UnsupportedEncodingException; | ||||
import java.security.NoSuchAlgorithmException; | import java.security.NoSuchAlgorithmException; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Collection; | |||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||
import org.apache.commons.logging.LogFactory; | 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 */ | /* image support modified from work of BoBoGi */ | ||||
/* font support based on work by Takayuki Takeuchi */ | /* font support based on work by Takayuki Takeuchi */ | ||||
*/ | */ | ||||
public class PDFDocument { | public class PDFDocument { | ||||
private static final Long LOCATION_PLACEHOLDER = new Long(0); | |||||
/** the encoding to use when converting strings to PDF commands */ | /** the encoding to use when converting strings to PDF commands */ | ||||
public static final String ENCODING = "ISO-8859-1"; | public static final String ENCODING = "ISO-8859-1"; | ||||
/** the counter for object numbering */ | /** the counter for object numbering */ | ||||
protected int objectcount = 0; | |||||
protected int objectcount; | |||||
/** the logger instance */ | /** the logger instance */ | ||||
private Log log = LogFactory.getLog("org.apache.fop.pdf"); | private Log log = LogFactory.getLog("org.apache.fop.pdf"); | ||||
/** the current character position */ | /** 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 */ | /** 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 */ | /** List of objects to write in the trailer */ | ||||
private List trailerObjects = new ArrayList(); | |||||
private List<PDFObject> trailerObjects = new ArrayList<PDFObject>(); | |||||
/** the objects themselves */ | /** the objects themselves */ | ||||
private List objects = new LinkedList(); | |||||
private List<PDFObject> objects = new LinkedList<PDFObject>(); | |||||
/** Controls the PDF version of this document */ | /** Controls the PDF version of this document */ | ||||
private VersionController versionController; | private VersionController versionController; | ||||
private PDFRoot root; | private PDFRoot root; | ||||
/** The root outline object */ | /** The root outline object */ | ||||
private PDFOutline outlineRoot = null; | |||||
private PDFOutline outlineRoot; | |||||
/** The /Pages object (mark-fop@inomial.com) */ | /** The /Pages object (mark-fop@inomial.com) */ | ||||
private PDFPages pages; | private PDFPages pages; | ||||
= new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); | = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); | ||||
/** the counter for Pattern name numbering (e.g. 'Pattern1') */ | /** the counter for Pattern name numbering (e.g. 'Pattern1') */ | ||||
private int patternCount = 0; | |||||
private int patternCount; | |||||
/** the counter for Shading name numbering */ | /** the counter for Shading name numbering */ | ||||
private int shadingCount = 0; | |||||
private int shadingCount; | |||||
/** the counter for XObject numbering */ | /** 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) */ | /* 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 PDFFactory factory; | ||||
private boolean encodingOnTheFly = true; | |||||
private FileIDGenerator fileIDGenerator; | private FileIDGenerator fileIDGenerator; | ||||
private boolean accessibilityEnabled; | |||||
/** | /** | ||||
* Creates an empty PDF document. | * Creates an empty PDF document. | ||||
* | * | ||||
return this.factory; | 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. | * Converts text to a byte array for writing to a PDF file. | ||||
* | * | ||||
* | * | ||||
* @param map the map of filter lists for each stream type | * @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; | this.filterMap = map; | ||||
} | } | ||||
* | * | ||||
* @return the map of filters being used | * @return the map of filters being used | ||||
*/ | */ | ||||
public Map getFilterMap() { | |||||
public Map<String, List<String>> getFilterMap() { | |||||
return this.filterMap; | return this.filterMap; | ||||
} | } | ||||
return this.root; | 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. | * Get the {@link PDFInfo} object for this document. | ||||
* | * | ||||
//Add object to special lists where necessary | //Add object to special lists where necessary | ||||
if (obj instanceof PDFFunction) { | if (obj instanceof PDFFunction) { | ||||
this.functions.add(obj); | |||||
this.functions.add((PDFFunction) obj); | |||||
} | } | ||||
if (obj instanceof PDFShading) { | if (obj instanceof PDFShading) { | ||||
final String shadingName = "Sh" + (++this.shadingCount); | final String shadingName = "Sh" + (++this.shadingCount); | ||||
((PDFShading)obj).setName(shadingName); | ((PDFShading)obj).setName(shadingName); | ||||
this.shadings.add(obj); | |||||
this.shadings.add((PDFShading) obj); | |||||
} | } | ||||
if (obj instanceof PDFPattern) { | if (obj instanceof PDFPattern) { | ||||
final String patternName = "Pa" + (++this.patternCount); | final String patternName = "Pa" + (++this.patternCount); | ||||
((PDFPattern)obj).setName(patternName); | ((PDFPattern)obj).setName(patternName); | ||||
this.patterns.add(obj); | |||||
this.patterns.add((PDFPattern) obj); | |||||
} | } | ||||
if (obj instanceof PDFFont) { | if (obj instanceof PDFFont) { | ||||
final PDFFont font = (PDFFont)obj; | final PDFFont font = (PDFFont)obj; | ||||
this.fontMap.put(font.getName(), font); | this.fontMap.put(font.getName(), font); | ||||
} | } | ||||
if (obj instanceof PDFGState) { | if (obj instanceof PDFGState) { | ||||
this.gstates.add(obj); | |||||
this.gstates.add((PDFGState) obj); | |||||
} | } | ||||
if (obj instanceof PDFPage) { | if (obj instanceof PDFPage) { | ||||
this.pages.notifyKidRegistered((PDFPage)obj); | this.pages.notifyKidRegistered((PDFPage)obj); | ||||
} | } | ||||
if (obj instanceof PDFLaunch) { | if (obj instanceof PDFLaunch) { | ||||
this.launches.add(obj); | |||||
this.launches.add((PDFLaunch) obj); | |||||
} | } | ||||
if (obj instanceof PDFLink) { | if (obj instanceof PDFLink) { | ||||
this.links.add(obj); | |||||
this.links.add((PDFLink) obj); | |||||
} | } | ||||
if (obj instanceof PDFFileSpec) { | if (obj instanceof PDFFileSpec) { | ||||
this.filespecs.add(obj); | |||||
this.filespecs.add((PDFFileSpec) obj); | |||||
} | } | ||||
if (obj instanceof PDFGoToRemote) { | if (obj instanceof PDFGoToRemote) { | ||||
this.gotoremotes.add(obj); | |||||
this.gotoremotes.add((PDFGoToRemote) obj); | |||||
} | } | ||||
} | } | ||||
this.trailerObjects.add(obj); | this.trailerObjects.add(obj); | ||||
if (obj instanceof PDFGoTo) { | if (obj instanceof PDFGoTo) { | ||||
this.gotos.add(obj); | |||||
this.gotos.add((PDFGoTo) obj); | |||||
} | } | ||||
} | } | ||||
return this.encryption; | 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)) { | if (compare.contentEquals(obj)) { | ||||
return obj; | return obj; | ||||
} | } | ||||
* @return PDFFont the requested font, null if it wasn't found | * @return PDFFont the requested font, null if it wasn't found | ||||
*/ | */ | ||||
protected PDFFont findFont(String fontname) { | protected PDFFont findFont(String fontname) { | ||||
return (PDFFont)this.fontMap.get(fontname); | |||||
return this.fontMap.get(fontname); | |||||
} | } | ||||
/** | /** | ||||
protected PDFDestination findDestination(PDFDestination compare) { | protected PDFDestination findDestination(PDFDestination compare) { | ||||
int index = getDestinationList().indexOf(compare); | int index = getDestinationList().indexOf(compare); | ||||
if (index >= 0) { | if (index >= 0) { | ||||
return (PDFDestination)getDestinationList().get(index); | |||||
return getDestinationList().get(index); | |||||
} else { | } else { | ||||
return null; | return null; | ||||
} | } | ||||
*/ | */ | ||||
protected PDFGState findGState(PDFGState wanted, PDFGState current) { | protected PDFGState findGState(PDFGState wanted, PDFGState current) { | ||||
PDFGState poss; | PDFGState poss; | ||||
Iterator iter = this.gstates.iterator(); | |||||
Iterator<PDFGState> iter = this.gstates.iterator(); | |||||
while (iter.hasNext()) { | while (iter.hasNext()) { | ||||
PDFGState avail = (PDFGState)iter.next(); | |||||
PDFGState avail = iter.next(); | |||||
poss = new PDFGState(); | poss = new PDFGState(); | ||||
poss.addValues(current); | poss.addValues(current); | ||||
poss.addValues(avail); | poss.addValues(avail); | ||||
* | * | ||||
* @return the map of fonts used in this document | * @return the map of fonts used in this document | ||||
*/ | */ | ||||
public Map getFontMap() { | |||||
public Map<String, PDFFont> getFontMap() { | |||||
return this.fontMap; | return this.fontMap; | ||||
} | } | ||||
* @return the PDFXObject for the key if found | * @return the PDFXObject for the key if found | ||||
*/ | */ | ||||
public PDFXObject getXObject(String key) { | 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); | |||||
} | } | ||||
/** | /** | ||||
*/ | */ | ||||
public void addDestination(PDFDestination destination) { | public void addDestination(PDFDestination destination) { | ||||
if (this.destinations == null) { | if (this.destinations == null) { | ||||
this.destinations = new ArrayList(); | |||||
this.destinations = new ArrayList<PDFDestination>(); | |||||
} | } | ||||
this.destinations.add(destination); | this.destinations.add(destination); | ||||
} | } | ||||
* | * | ||||
* @return the list of named destinations. | * @return the list of named destinations. | ||||
*/ | */ | ||||
public List getDestinationList() { | |||||
public List<PDFDestination> getDestinationList() { | |||||
if (hasDestinations()) { | if (hasDestinations()) { | ||||
return this.destinations; | return this.destinations; | ||||
} else { | } else { | ||||
return Collections.EMPTY_LIST; | |||||
return Collections.emptyList(); | |||||
} | } | ||||
} | } | ||||
return this.resources; | 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; | |||||
} | } | ||||
/** | /** | ||||
//LinkedList) allows for output() methods to create and register objects | //LinkedList) allows for output() methods to create and register objects | ||||
//on the fly even during serialization. | //on the fly even during serialization. | ||||
while (this.objects.size() > 0) { | 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; | |||||
} | } | ||||
/** | /** | ||||
* @throws IOException if there is an exception writing to the output stream | * @throws IOException if there is an exception writing to the output stream | ||||
*/ | */ | ||||
public void outputTrailer(OutputStream stream) throws IOException { | public void outputTrailer(OutputStream stream) throws IOException { | ||||
createDestinations(); | |||||
output(stream); | |||||
outputTrailerObjectsAndXref(stream); | |||||
} | |||||
private void createDestinations() { | |||||
if (hasDestinations()) { | if (hasDestinations()) { | ||||
Collections.sort(this.destinations, new DestinationComparator()); | Collections.sort(this.destinations, new DestinationComparator()); | ||||
this.dests = getFactory().makeDests(this.destinations); | |||||
PDFDests dests = getFactory().makeDests(this.destinations); | |||||
if (this.root.getNames() == null) { | if (this.root.getNames() == null) { | ||||
this.root.setNames(getFactory().makeNames()); | this.root.setNames(getFactory().makeNames()); | ||||
} | } | ||||
this.root.getNames().setDests(dests); | 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() { | long getCurrentFileSize() { |
byte[] encrypt(byte[] data, PDFObject refObj); | 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(); | String getTrailerEntry(); | ||||
} | } |
? new Rev2Engine(encryptionSettings) | ? new Rev2Engine(encryptionSettings) | ||||
: new Rev3Engine(encryptionSettings); | : new Rev3Engine(encryptionSettings); | ||||
initializationEngine.run(); | initializationEngine.run(); | ||||
encryptionDictionary = createEncryptionDictionary(getObjectID(), permissions, | |||||
initializationEngine.oValue, initializationEngine.uValue); | |||||
encryptionDictionary = createEncryptionDictionary(permissions, | |||||
initializationEngine.oValue, | |||||
initializationEngine.uValue); | |||||
} | } | ||||
private void determineEncryptionAlgorithm() { | private void determineEncryptionAlgorithm() { | ||||
&& encryptionParams.isAllowPrintHq(); | && 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" | + "/V " + version + "\n" | ||||
+ "/R " + revision + "\n" | + "/R " + revision + "\n" | ||||
+ "/Length " + encryptionLength + "\n" | + "/Length " + encryptionLength + "\n" | ||||
+ "/P " + permissions + "\n" | + "/P " + permissions + "\n" | ||||
+ "/O " + PDFText.toHex(oValue) + "\n" | + "/O " + PDFText.toHex(oValue) + "\n" | ||||
+ "/U " + PDFText.toHex(uValue) + "\n" | + "/U " + PDFText.toHex(uValue) + "\n" | ||||
+ ">>\n" | |||||
+ "endobj\n"; | |||||
+ ">>"; | |||||
} | } | ||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public String getTrailerEntry() { | 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) { | private static byte[] encryptWithKey(byte[] key, byte[] data) { |
return pageLabels; | 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). | * Make a the head object of the name dictionary (the /Dests object). | ||||
* | * |
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.OutputStream; | import java.io.OutputStream; | ||||
import java.util.Collections; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
/** Key for the filter used for metadata */ | /** Key for the filter used for metadata */ | ||||
public static final String METADATA_FILTER = "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; | private boolean ignoreASCIIFilters = false; | ||||
} | } | ||||
} | } | ||||
List<PDFFilter> getFilters() { | |||||
return Collections.unmodifiableList(filters); | |||||
} | |||||
/** | /** | ||||
* Apply the filters to the data | * Apply the filters to the data | ||||
* in the order given and return the /Filter and /DecodeParms | * in the order given and return the /Filter and /DecodeParms | ||||
* @return a String representing the filter list | * @return a String representing the filter list | ||||
*/ | */ | ||||
protected String buildFilterDictEntries() { | protected String buildFilterDictEntries() { | ||||
if (filters != null && filters.size() > 0) { | |||||
if (filters.size() > 0) { | |||||
List names = new java.util.ArrayList(); | List names = new java.util.ArrayList(); | ||||
List parms = new java.util.ArrayList(); | List parms = new java.util.ArrayList(); | ||||
* @param dict the PDFDictionary to set the entries on | * @param dict the PDFDictionary to set the entries on | ||||
*/ | */ | ||||
protected void putFilterDictEntries(PDFDictionary dict) { | protected void putFilterDictEntries(PDFDictionary dict) { | ||||
if (filters != null && filters.size() > 0) { | |||||
if (filters.size() > 0) { | |||||
List names = new java.util.ArrayList(); | List names = new java.util.ArrayList(); | ||||
List parms = new java.util.ArrayList(); | List parms = new java.util.ArrayList(); | ||||
*/ | */ | ||||
public OutputStream applyFilters(OutputStream stream) throws IOException { | public OutputStream applyFilters(OutputStream stream) throws IOException { | ||||
OutputStream out = stream; | OutputStream out = stream; | ||||
if (filters != null && !isDisableAllFilters()) { | |||||
if (!isDisableAllFilters()) { | |||||
for (int count = filters.size() - 1; count >= 0; count--) { | for (int count = filters.size() - 1; count >= 0; count--) { | ||||
PDFFilter filter = (PDFFilter)filters.get(count); | PDFFilter filter = (PDFFilter)filters.get(count); | ||||
out = filter.applyFilter(out); | out = filter.applyFilter(out); |
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
protected int output(OutputStream stream) throws IOException { | |||||
public int output(OutputStream stream) throws IOException { | |||||
validate(); | validate(); | ||||
return super.output(stream); | return super.output(stream); | ||||
} | } |
* @param resources the resource PDF reference | * @param resources the resource PDF reference | ||||
*/ | */ | ||||
public PDFFormXObject(int xnumber, PDFStream contents, PDFReference resources) { | public PDFFormXObject(int xnumber, PDFStream contents, PDFReference resources) { | ||||
super(); | |||||
super(contents.getDictionary()); | |||||
put("Name", new PDFName("Form" + xnumber)); | put("Name", new PDFName("Form" + xnumber)); | ||||
this.contents = contents; | this.contents = contents; | ||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
protected int output(OutputStream stream) throws IOException { | |||||
public int output(OutputStream stream) throws IOException { | |||||
final int len = super.output(stream); | final int len = super.output(stream); | ||||
//Now that the data has been written, it can be discarded. | //Now that the data has been written, it can be discarded. |
int numberOfFunctions = 0; | int numberOfFunctions = 0; | ||||
int tempInt = 0; | int tempInt = 0; | ||||
StringBuffer p = new StringBuffer(256); | StringBuffer p = new StringBuffer(256); | ||||
p.append(getObjectID() | |||||
+ "<< \n/FunctionType " + this.functionType + " \n"); | |||||
p.append("<< \n/FunctionType " + this.functionType + " \n"); | |||||
// FunctionType 0 | // FunctionType 0 | ||||
if (this.functionType == 0) { | if (this.functionType == 0) { | ||||
p.append("] \n"); | p.append("] \n"); | ||||
} | } | ||||
} | } | ||||
p.append(">> \n"); | |||||
p.append(">>"); | |||||
// stream representing the function | // stream representing the function | ||||
if (this.functionDataStream != null) { | 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 | // end of if FunctionType 0 | ||||
} else if (this.functionType == 2) { | } else if (this.functionType == 2) { | ||||
+ PDFNumber.doubleOut(new Double(this.interpolationExponentN)) | + PDFNumber.doubleOut(new Double(this.interpolationExponentN)) | ||||
+ " \n"); | + " \n"); | ||||
p.append(">> \nendobj\n"); | |||||
p.append(">>"); | |||||
} else if (this.functionType | } else if (this.functionType | ||||
== 3) { // fix this up when my eyes uncross | == 3) { // fix this up when my eyes uncross | ||||
} | } | ||||
} | } | ||||
p.append("] \n"); | |||||
p.append(">> \nendobj\n"); | |||||
p.append("]\n>>"); | |||||
} else if (this.functionType | } else if (this.functionType | ||||
== 4) { // fix this up when my eyes uncross | == 4) { // fix this up when my eyes uncross | ||||
// DOMAIN | // DOMAIN | ||||
+ " \n"); | + " \n"); | ||||
} | } | ||||
p.append(">> \n"); | |||||
p.append(">>"); | |||||
// stream representing the function | // stream representing the function | ||||
if (this.functionDataStream != null) { | if (this.functionDataStream != null) { | ||||
p.append("stream\n{ " + this.functionDataStream | |||||
+ " } \nendstream\n"); | |||||
p.append("\nstream\n{ " + this.functionDataStream | |||||
+ " }\nendstream"); | |||||
} | } | ||||
p.append("endobj\n"); | |||||
} | } | ||||
*/ | */ | ||||
public String toPDFString() { | public String toPDFString() { | ||||
StringBuffer sb = new StringBuffer(64); | StringBuffer sb = new StringBuffer(64); | ||||
sb.append(getObjectID()); | |||||
sb.append("<<\n/Type /ExtGState\n"); | sb.append("<<\n/Type /ExtGState\n"); | ||||
appendVal(sb, GSTATE_ALPHA_NONSTROKE); | appendVal(sb, GSTATE_ALPHA_NONSTROKE); | ||||
appendVal(sb, GSTATE_ALPHA_STROKE); | appendVal(sb, GSTATE_ALPHA_STROKE); | ||||
sb.append(">>\nendobj\n"); | |||||
sb.append(">>"); | |||||
return sb.toString(); | return sb.toString(); | ||||
} | } | ||||
} else { | } else { | ||||
dest = "/D [" + this.pageReference + " " + destination + "]\n"; | 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 + ">>"; | |||||
} | } | ||||
/* | /* |
*/ | */ | ||||
public String toPDFString() { | public String toPDFString() { | ||||
StringBuffer sb = new StringBuffer(64); | StringBuffer sb = new StringBuffer(64); | ||||
sb.append(getObjectID()); | |||||
sb.append("<<\n/S /GoToR\n/F "); | sb.append("<<\n/S /GoToR\n/F "); | ||||
sb.append(pdfFileSpec.toString()); | sb.append(pdfFileSpec.toString()); | ||||
sb.append("\n"); | sb.append("\n"); | ||||
sb.append("/NewWindow true"); | sb.append("/NewWindow true"); | ||||
} | } | ||||
sb.append(" \n>>\nendobj\n"); | |||||
sb.append("\n>>"); | |||||
return sb.toString(); | return sb.toString(); | ||||
} | } |
@Override | @Override | ||||
protected String toPDFString() { | protected String toPDFString() { | ||||
StringBuffer sb = new StringBuffer(64); | StringBuffer sb = new StringBuffer(64); | ||||
sb.append(getObjectID()); | |||||
sb.append("[/ICCBased ").append(getICCStream().referencePDF()).append("]"); | sb.append("[/ICCBased ").append(getICCStream().referencePDF()).append("]"); | ||||
sb.append("\nendobj\n"); | |||||
return sb.toString(); | return sb.toString(); | ||||
} | } | ||||
* {@inheritDoc} | * {@inheritDoc} | ||||
*/ | */ | ||||
@Override | @Override | ||||
protected int output(java.io.OutputStream stream) | |||||
public int output(java.io.OutputStream stream) | |||||
throws java.io.IOException { | throws java.io.IOException { | ||||
int length = super.output(stream); | int length = super.output(stream); | ||||
this.cp = null; //Free ICC stream when it's not used anymore | this.cp = null; //Free ICC stream when it's not used anymore |
* @throws IOException if there is an error writing the data | * @throws IOException if there is an error writing the data | ||||
* @return the length of the data written | * @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); | int length = super.output(stream); | ||||
// let it gc | // let it gc | ||||
put("SMask", ref); | put("SMask", ref); | ||||
} | } | ||||
//Important: do this at the end so previous values can be overwritten. | //Important: do this at the end so previous values can be overwritten. | ||||
pdfimage.populateXObjectDictionary(this); | |||||
pdfimage.populateXObjectDictionary(getDictionary()); | |||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ |
PDFProfile profile = getDocumentSafely().getProfile(); | PDFProfile profile = getDocumentSafely().getProfile(); | ||||
ByteArrayOutputStream bout = new ByteArrayOutputStream(128); | ByteArrayOutputStream bout = new ByteArrayOutputStream(128); | ||||
try { | try { | ||||
bout.write(encode(getObjectID())); | |||||
bout.write(encode("<<\n")); | bout.write(encode("<<\n")); | ||||
if (title != null && title.length() > 0) { | if (title != null && title.length() > 0) { | ||||
bout.write(encode("/Title ")); | bout.write(encode("/Title ")); | ||||
bout.write(encode("/Trapped /False\n")); | bout.write(encode("/Trapped /False\n")); | ||||
} | } | ||||
bout.write(encode(">>\nendobj\n")); | |||||
bout.write(encode(">>")); | |||||
} catch (IOException ioe) { | } catch (IOException ioe) { | ||||
log.error("Ignored I/O exception", ioe); | log.error("Ignored I/O exception", ioe); | ||||
} | } |
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public String toPDFString() { | public String toPDFString() { | ||||
StringBuffer sb = new StringBuffer(64); | StringBuffer sb = new StringBuffer(64); | ||||
sb.append(getObjectID()); | |||||
sb.append("<<\n/S /Launch\n/F "); | sb.append("<<\n/S /Launch\n/F "); | ||||
sb.append(externalFileSpec.toString()); | sb.append(externalFileSpec.toString()); | ||||
sb.append(" \n>>\nendobj\n"); | |||||
sb.append("\n>>"); | |||||
return sb.toString(); | return sb.toString(); | ||||
} | } |
f |= 1 << (5 - 1); //NoRotate, bit 5 | f |= 1 << (5 - 1); //NoRotate, bit 5 | ||||
fFlag = "/F " + f; | fFlag = "/F " + f; | ||||
} | } | ||||
String s = getObjectID() | |||||
+ "<< /Type /Annot\n" + "/Subtype /Link\n" + "/Rect [ " | |||||
String s = "<< /Type /Annot\n" + "/Subtype /Link\n" + "/Rect [ " | |||||
+ (ulx) + " " + (uly) + " " | + (ulx) + " " + (uly) + " " | ||||
+ (brx) + " " + (bry) + " ]\n" + "/C [ " | + (brx) + " " + (bry) + " ]\n" + "/C [ " | ||||
+ this.color + " ]\n" + "/Border [ 0 0 0 ]\n" + "/A " | + this.color + " ]\n" + "/Border [ 0 0 0 ]\n" + "/A " | ||||
+ this.action.getAction() + "\n" + "/H /I\n" | + this.action.getAction() + "\n" + "/H /I\n" | ||||
+ (this.structParent != null | + (this.structParent != null | ||||
? "/StructParent " + this.structParent.toString() + "\n" : "") | ? "/StructParent " + this.structParent.toString() + "\n" : "") | ||||
+ fFlag + "\n>>\nendobj\n"; | |||||
+ fFlag + "\n>>"; | |||||
return s; | return s; | ||||
} | } | ||||
* byte arrays around so much | * byte arrays around so much | ||||
* {@inheritDoc} | * {@inheritDoc} | ||||
*/ | */ | ||||
protected int output(java.io.OutputStream stream) | |||||
public int output(java.io.OutputStream stream) | |||||
throws java.io.IOException { | throws java.io.IOException { | ||||
int length = super.output(stream); | int length = super.output(stream); | ||||
this.xmpMetadata = null; //Release DOM when it's not used anymore | this.xmpMetadata = null; //Release DOM when it's not used anymore |
return name.hashCode(); | return name.hashCode(); | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
@Override | @Override | ||||
protected int output(OutputStream stream) throws IOException { | |||||
public int output(OutputStream stream) throws IOException { | |||||
CountingOutputStream cout = new CountingOutputStream(stream); | CountingOutputStream cout = new CountingOutputStream(stream); | ||||
StringBuilder textBuffer = new StringBuilder(64); | StringBuilder textBuffer = new StringBuilder(64); | ||||
if (hasObjectNumber()) { | |||||
textBuffer.append(getObjectID()); | |||||
} | |||||
textBuffer.append(toString()); | textBuffer.append(toString()); | ||||
if (hasObjectNumber()) { | |||||
textBuffer.append("\nendobj\n"); | |||||
} | |||||
PDFDocument.flushTextBuffer(textBuffer, cout); | PDFDocument.flushTextBuffer(textBuffer, cout); | ||||
return cout.getCount(); | return cout.getCount(); | ||||
} | } |
"The number of this PDFNumber must not be empty"); | "The number of this PDFNumber must not be empty"); | ||||
} | } | ||||
StringBuffer sb = new StringBuffer(64); | StringBuffer sb = new StringBuffer(64); | ||||
if (hasObjectNumber()) { | |||||
sb.append(getObjectID()); | |||||
} | |||||
sb.append(doubleOut(getNumber().doubleValue(), 10)); | sb.append(doubleOut(getNumber().doubleValue(), 10)); | ||||
if (hasObjectNumber()) { | |||||
sb.append("\nendobj\n"); | |||||
} | |||||
return sb.toString(); | return sb.toString(); | ||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
@Override | @Override | ||||
protected int output(OutputStream stream) throws IOException { | |||||
public int output(OutputStream stream) throws IOException { | |||||
CountingOutputStream cout = new CountingOutputStream(stream); | CountingOutputStream cout = new CountingOutputStream(stream); | ||||
StringBuilder textBuffer = new StringBuilder(64); | StringBuilder textBuffer = new StringBuilder(64); | ||||
if (hasObjectNumber()) { | |||||
textBuffer.append(getObjectID()); | |||||
} | |||||
textBuffer.append('['); | textBuffer.append('['); | ||||
boolean first = true; | boolean first = true; | ||||
for (Map.Entry<Integer, Object> entry : this.map.entrySet()) { | for (Map.Entry<Integer, Object> entry : this.map.entrySet()) { | ||||
formatObject(entry.getValue(), cout, textBuffer); | formatObject(entry.getValue(), cout, textBuffer); | ||||
} | } | ||||
textBuffer.append(']'); | textBuffer.append(']'); | ||||
if (hasObjectNumber()) { | |||||
textBuffer.append("\nendobj\n"); | |||||
} | |||||
PDFDocument.flushTextBuffer(textBuffer, cout); | PDFDocument.flushTextBuffer(textBuffer, cout); | ||||
return cout.getCount(); | return cout.getCount(); | ||||
} | } |
* @throws IOException if there is an error writing to the stream | * @throws IOException if there is an error writing to the stream | ||||
* @return the number of bytes written | * @return the number of bytes written | ||||
*/ | */ | ||||
protected int output(OutputStream stream) throws IOException { | |||||
public int output(OutputStream stream) throws IOException { | |||||
byte[] pdf = this.toPDF(); | byte[] pdf = this.toPDF(); | ||||
stream.write(pdf); | stream.write(pdf); | ||||
return pdf.length; | return pdf.length; |
protected byte[] toPDF() { | protected byte[] toPDF() { | ||||
ByteArrayOutputStream bout = new ByteArrayOutputStream(128); | ByteArrayOutputStream bout = new ByteArrayOutputStream(128); | ||||
try { | try { | ||||
bout.write(encode(getObjectID())); | |||||
bout.write(encode("<<")); | bout.write(encode("<<")); | ||||
if (parent == null) { | if (parent == null) { | ||||
// root Outlines object | // root Outlines object | ||||
bout.write(encode(" /A " + actionRef + "\n")); | bout.write(encode(" /A " + actionRef + "\n")); | ||||
} | } | ||||
} | } | ||||
bout.write(encode(">> endobj\n")); | |||||
bout.write(encode(">>")); | |||||
} catch (IOException ioe) { | } catch (IOException ioe) { | ||||
log.error("Ignored I/O exception", ioe); | log.error("Ignored I/O exception", ioe); | ||||
} | } |
public byte[] toPDF() { | public byte[] toPDF() { | ||||
ByteArrayOutputStream bout = new ByteArrayOutputStream(128); | ByteArrayOutputStream bout = new ByteArrayOutputStream(128); | ||||
try { | try { | ||||
bout.write(encode(getObjectID())); | |||||
bout.write(encode("<<\n")); | bout.write(encode("<<\n")); | ||||
bout.write(encode("/Type /OutputIntent\n")); | bout.write(encode("/Type /OutputIntent\n")); | ||||
bout.write(encode("/DestOutputProfile " + destOutputProfile.referencePDF() + "\n")); | bout.write(encode("/DestOutputProfile " + destOutputProfile.referencePDF() + "\n")); | ||||
} | } | ||||
bout.write(encode(">>\nendobj\n")); | |||||
bout.write(encode(">>")); | |||||
} catch (IOException ioe) { | } catch (IOException ioe) { | ||||
log.error("Ignored I/O exception", ioe); | log.error("Ignored I/O exception", ioe); | ||||
} | } |
*/ | */ | ||||
public String toPDFString() { | public String toPDFString() { | ||||
StringBuffer sb = new StringBuffer(64); | 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++) { | for (int i = 0; i < kids.size(); i++) { | ||||
Object kid = kids.get(i); | Object kid = kids.get(i); | ||||
if (kid == null) { | if (kid == null) { | ||||
} | } | ||||
sb.append(kid).append(" "); | sb.append(kid).append(" "); | ||||
} | } | ||||
sb.append("] >>\nendobj\n"); | |||||
sb.append("] >>"); | |||||
return sb.toString(); | return sb.toString(); | ||||
} | } | ||||
* @throws IOException if there is an error writing to the stream | * @throws IOException if there is an error writing to the stream | ||||
* @return the PDF string. | * @return the PDF string. | ||||
*/ | */ | ||||
protected int output(OutputStream stream) throws IOException { | |||||
public int output(OutputStream stream) throws IOException { | |||||
int vectorSize = 0; | int vectorSize = 0; | ||||
int tempInt = 0; | int tempInt = 0; | ||||
byte[] buffer; | byte[] buffer; | ||||
StringBuffer p = new StringBuffer(64); | StringBuffer p = new StringBuffer(64); | ||||
p.append(getObjectID()); | |||||
p.append("<< \n/Type /Pattern \n"); | p.append("<< \n/Type /Pattern \n"); | ||||
if (this.resources != null) { | if (this.resources != null) { | ||||
length += pdfStream.outputStreamData(encodedStream, stream); | length += pdfStream.outputStreamData(encodedStream, stream); | ||||
} | } | ||||
buffer = encode("\nendobj\n"); | |||||
stream.write(buffer); | |||||
length += buffer.length; | |||||
return length; | return length; | ||||
} | } | ||||
return cs; | return cs; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
protected int output(OutputStream stream) throws IOException { | |||||
@Override | |||||
public int output(OutputStream stream) throws IOException { | |||||
populateDictionary(); | populateDictionary(); | ||||
return super.output(stream); | return super.output(stream); | ||||
} | } |
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
protected int output(OutputStream stream) throws IOException { | |||||
public int output(OutputStream stream) throws IOException { | |||||
getDocument().getProfile().verifyTaggedPDF(); | getDocument().getProfile().verifyTaggedPDF(); | ||||
return super.output(stream); | return super.output(stream); | ||||
} | } |
int vectorSize; | int vectorSize; | ||||
int tempInt; | int tempInt; | ||||
StringBuffer p = new StringBuffer(128); | StringBuffer p = new StringBuffer(128); | ||||
p.append(getObjectID() | |||||
+ "<< \n/ShadingType " + this.shadingType + " \n"); | |||||
p.append("<<\n/ShadingType " + this.shadingType + " \n"); | |||||
if (this.colorSpace != null) { | if (this.colorSpace != null) { | ||||
p.append("/ColorSpace /" | p.append("/ColorSpace /" | ||||
+ this.colorSpace.getName() + " \n"); | + this.colorSpace.getName() + " \n"); | ||||
} | } | ||||
p.append(">> \nendobj\n"); | |||||
p.append(">>"); | |||||
return (p.toString()); | return (p.toString()); | ||||
} | } |
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.OutputStream; | import java.io.OutputStream; | ||||
import java.io.OutputStreamWriter; | |||||
import java.io.Writer; | import java.io.Writer; | ||||
/** | /** | ||||
* Create an empty stream object | * Create an empty stream object | ||||
*/ | */ | ||||
public PDFStream() { | 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 { | try { | ||||
data = StreamCacheFactory.getInstance().createStreamCache(); | data = StreamCacheFactory.getInstance().createStreamCache(); | ||||
this.streamWriter = new java.io.OutputStreamWriter( | |||||
this.streamWriter = new OutputStreamWriter( | |||||
getBufferOutputStream(), PDFDocument.ENCODING); | getBufferOutputStream(), PDFDocument.ENCODING); | ||||
//Buffer to minimize calls to the converter | //Buffer to minimize calls to the converter | ||||
this.streamWriter = new java.io.BufferedWriter(this.streamWriter); | 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); | |||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* {@inheritDoc} | * {@inheritDoc} | ||||
*/ | */ | ||||
protected int output(OutputStream stream) throws IOException { | |||||
public int output(OutputStream stream) throws IOException { | |||||
final int len = super.output(stream); | final int len = super.output(stream); | ||||
//Now that the data has been written, it can be discarded. | //Now that the data has been written, it can be discarded. |
/** | /** | ||||
* Class representing a PDF Structure Element. | * Class representing a PDF Structure Element. | ||||
*/ | */ | ||||
public class PDFStructElem extends PDFDictionary implements StructureTreeElement { | |||||
public class PDFStructElem extends PDFDictionary implements StructureTreeElement, CompressedObject { | |||||
private PDFStructElem parentElement; | private PDFStructElem parentElement; | ||||
* byte arrays around so much | * byte arrays around so much | ||||
* {@inheritDoc} | * {@inheritDoc} | ||||
*/ | */ | ||||
protected int output(java.io.OutputStream stream) | |||||
public int output(java.io.OutputStream stream) | |||||
throws java.io.IOException { | throws java.io.IOException { | ||||
if (pfb == null) { | if (pfb == null) { | ||||
throw new IllegalStateException("pfb must not be null at this point"); | throw new IllegalStateException("pfb must not be null at this point"); |
* byte arrays around so much | * byte arrays around so much | ||||
* {@inheritDoc} | * {@inheritDoc} | ||||
*/ | */ | ||||
protected int output(java.io.OutputStream stream) | |||||
public int output(java.io.OutputStream stream) | |||||
throws java.io.IOException { | throws java.io.IOException { | ||||
if (log.isDebugEnabled()) { | if (log.isDebugEnabled()) { | ||||
log.debug("Writing " + origLength + " bytes of TTF font data"); | log.debug("Writing " + origLength + " bytes of TTF font data"); |
"The text of this PDFText must not be empty"); | "The text of this PDFText must not be empty"); | ||||
} | } | ||||
StringBuffer sb = new StringBuffer(64); | StringBuffer sb = new StringBuffer(64); | ||||
sb.append(getObjectID()); | |||||
sb.append("("); | sb.append("("); | ||||
sb.append(escapeText(getText())); | sb.append(escapeText(getText())); | ||||
sb.append(")"); | sb.append(")"); | ||||
sb.append("\nendobj\n"); | |||||
return sb.toString(); | return sb.toString(); | ||||
} | } | ||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public String toPDFString() { | public String toPDFString() { | ||||
//TODO Convert this class into a dictionary | //TODO Convert this class into a dictionary | ||||
return getObjectID() + getDictString() + "\nendobj\n"; | |||||
//throw new UnsupportedOperationException("This method should not be called"); | |||||
return getDictString(); | |||||
} | } | ||||
} | } |
super(); | super(); | ||||
} | } | ||||
protected PDFXObject(PDFDictionary dictionary) { | |||||
super(dictionary); | |||||
} | |||||
/** | /** | ||||
* Returns the XObject's name. | * Returns the XObject's name. | ||||
* @return the name of the XObject | * @return the name of the XObject |
} | } | ||||
/** | /** | ||||
* 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 | * @param version a version number | ||||
* @return the corresponding Version instance | * @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) { | public static Version getValueOf(String version) { | ||||
for (Version pdfVersion : Version.values()) { | for (Version pdfVersion : Version.values()) { |
/* | |||||
* 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; | |||||
} | |||||
} |
/* | |||||
* 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; | |||||
} |
/* | |||||
* 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}); | |||||
} | |||||
} |
/* | |||||
* 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); | |||||
} | |||||
} |
/* | |||||
* 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; | |||||
} |
/* | |||||
* 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; | |||||
} | |||||
} |
/* | |||||
* 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); | |||||
} | |||||
} |
*/ | */ | ||||
PDFLogicalStructureHandler(PDFDocument pdfDoc) { | PDFLogicalStructureHandler(PDFDocument pdfDoc) { | ||||
this.pdfDoc = 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); | FOToPDFRoleMap.mapFormattingObject("root", structTreeRoot), structTreeRoot); | ||||
structTreeRoot.addKid(rootStructureElement); | structTreeRoot.addKid(rootStructureElement); | ||||
} | } | ||||
PDFStructElem createPageSequence(Locale language) { | PDFStructElem createPageSequence(Locale language) { | ||||
PDFStructElem structElemPart = pdfDoc.getFactory().makeStructureElement( | |||||
PDFStructElem structElemPart = pdfDoc.makeStructureElement( | |||||
FOToPDFRoleMap.mapFormattingObject("page-sequence", rootStructureElement), | FOToPDFRoleMap.mapFormattingObject("page-sequence", rootStructureElement), | ||||
rootStructureElement); | rootStructureElement); | ||||
rootStructureElement.addKid(structElemPart); | rootStructureElement.addKid(structElemPart); |
if (maxPDFVersion == null) { | if (maxPDFVersion == null) { | ||||
this.pdfDoc = new PDFDocument(producer); | this.pdfDoc = new PDFDocument(producer); | ||||
} else { | } else { | ||||
VersionController controller = | |||||
VersionController.getFixedVersionController(maxPDFVersion); | |||||
VersionController controller | |||||
= VersionController.getFixedVersionController(maxPDFVersion); | |||||
this.pdfDoc = new PDFDocument(producer, controller); | this.pdfDoc = new PDFDocument(producer, controller); | ||||
} | } | ||||
updateInfo(); | updateInfo(); | ||||
log.debug("PDF/A is active. Conformance Level: " + pdfAMode); | log.debug("PDF/A is active. Conformance Level: " + pdfAMode); | ||||
addPDFA1OutputIntent(); | addPDFA1OutputIntent(); | ||||
} | } | ||||
this.pdfDoc.enableAccessibility(userAgent.isAccessibilityEnabled()); | |||||
return this.pdfDoc; | return this.pdfDoc; | ||||
} | } | ||||
PDFStructElem parent = ancestors.getFirst(); | PDFStructElem parent = ancestors.getFirst(); | ||||
String role = attributes.getValue("role"); | String role = attributes.getValue("role"); | ||||
PDFStructElem created; | PDFStructElem created; | ||||
created = pdfFactory.makeStructureElement( | |||||
created = pdfFactory.getDocument().makeStructureElement( | |||||
FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster), parent); | FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster), parent); | ||||
parent.addKid(created); | parent.addKid(created); | ||||
ancestors.addFirst(created); | ancestors.addFirst(created); | ||||
PDFStructElem parent = ancestors.getFirst(); | PDFStructElem parent = ancestors.getFirst(); | ||||
String role = attributes.getValue("role"); | String role = attributes.getValue("role"); | ||||
PDFStructElem created; | PDFStructElem created; | ||||
created = pdfFactory.makeStructureElement( | |||||
created = pdfFactory.getDocument().makeStructureElement( | |||||
FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster), parent); | FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster), parent); | ||||
parent.addKid(created); | parent.addKid(created); | ||||
String altTextNode = attributes.getValue(ExtensionElementMapping.URI, "alt-text"); | String altTextNode = attributes.getValue(ExtensionElementMapping.URI, "alt-text"); | ||||
if ("#PCDATA".equals(name)) { | if ("#PCDATA".equals(name)) { | ||||
created = new PDFStructElem.Placeholder(parent, name); | created = new PDFStructElem.Placeholder(parent, name); | ||||
} else { | } else { | ||||
created = pdfFactory.makeStructureElement( | |||||
created = pdfFactory.getDocument().makeStructureElement( | |||||
FOToPDFRoleMap.mapFormattingObject(name, role, parent, | FOToPDFRoleMap.mapFormattingObject(name, role, parent, | ||||
eventBroadcaster), parent); | eventBroadcaster), parent); | ||||
} | } |
documents. Example: the fix of marks layering will be such a case when it's done. | documents. Example: the fix of marks layering will be such a case when it's done. | ||||
--> | --> | ||||
<release version="FOP Trunk" date="TBD"> | <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"> | <action context="Code" dev="VH" type="add" fixes-bug="46962" due-to="Alexios Giotis"> | ||||
Fixed deadlock in PropertyCache. | Fixed deadlock in PropertyCache. | ||||
</action> | </action> |
encodedBytes[i++] = (byte) (in & 0xff); | 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 | @Before | ||||
public void setUp() { | public void setUp() { |
/* | |||||
* 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); | |||||
} | |||||
} |
/* | |||||
* 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; | |||||
} | |||||
} | |||||
} |
final String digits = "\\d+"; | final String digits = "\\d+"; | ||||
final String hexDigits = "\\p{XDigit}+"; | final String hexDigits = "\\p{XDigit}+"; | ||||
dictionary.mustContain("1" + whitespace + "0" + whitespace + "obj"); | |||||
dictionary.mustContain("/Filter" + whitespace + "/Standard\\b"); | dictionary.mustContain("/Filter" + whitespace + "/Standard\\b"); | ||||
dictionary.mustContain("/V" + whitespace + "(" + digits + ")") | dictionary.mustContain("/V" + whitespace + "(" + digits + ")") |
/* | |||||
* 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()); | |||||
} | |||||
} |
PDFNullTestCase.class, | PDFNullTestCase.class, | ||||
PDFNumsArrayTestCase.class, | PDFNumsArrayTestCase.class, | ||||
PDFRectangleTestCase.class, | PDFRectangleTestCase.class, | ||||
PDFReferenceTestCase.class | |||||
PDFReferenceTestCase.class, | |||||
VersionTestCase.class, | |||||
VersionControllerTestCase.class | |||||
}) | }) | ||||
public class PDFLibraryTestSuite { | public class PDFLibraryTestSuite { | ||||
} | } |
protected final PDFObject parent = new DummyPDFObject(); | protected final PDFObject parent = new DummyPDFObject(); | ||||
/** The test subject */ | /** The test subject */ | ||||
protected PDFObject pdfObjectUnderTest; | 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 { | private static class DummyPDFObject extends PDFObject { | ||||
outStream.reset(); | outStream.reset(); | ||||
object.setObjectNumber(1); | object.setObjectNumber(1); | ||||
// Test the length of the output string is returned correctly. | // 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()); | |||||
} | } | ||||
} | } |
/* | |||||
* 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(); | |||||
} | |||||
} |
/* | |||||
* 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); | |||||
} | |||||
} |
/* | |||||
* 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(); | |||||
} | |||||
} |
/* | |||||
* 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(); | |||||
} | |||||
} |
/* | |||||
* 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); | |||||
} | |||||
} |
/* | |||||
* 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(); | |||||
} | |||||
} |
/* | |||||
* 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); | |||||
} | |||||
} |
<?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> |
<?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> |
<fo:layout-master-set> | <fo:layout-master-set> | ||||
<fo:simple-page-master master-name="page" | <fo:simple-page-master master-name="page" | ||||
page-height="220pt" page-width="320pt" margin="10pt"> | 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:simple-page-master> | ||||
</fo:layout-master-set> | </fo:layout-master-set> | ||||
<fo:page-sequence master-reference="page"> | <fo:page-sequence master-reference="page"> |
<fo:layout-master-set> | <fo:layout-master-set> | ||||
<fo:simple-page-master master-name="page" | <fo:simple-page-master master-name="page" | ||||
page-height="220pt" page-width="320pt" margin="10pt"> | 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-repeat="no-repeat" background-position-horizontal="50%" | ||||
background-position-vertical="50%"/> | background-position-vertical="50%"/> | ||||
</fo:simple-page-master> | </fo:simple-page-master> |
<fo:layout-master-set> | <fo:layout-master-set> | ||||
<fo:simple-page-master master-name="page" | <fo:simple-page-master master-name="page" | ||||
page-height="220pt" page-width="320pt" margin="10pt"> | 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:simple-page-master> | ||||
</fo:layout-master-set> | </fo:layout-master-set> | ||||
<fo:page-sequence master-reference="page"> | <fo:page-sequence master-reference="page"> |
<fo:layout-master-set> | <fo:layout-master-set> | ||||
<fo:simple-page-master master-name="page" | <fo:simple-page-master master-name="page" | ||||
page-height="220pt" page-width="320pt" margin="10pt"> | 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-repeat="no-repeat" background-position-horizontal="50%" | ||||
background-position-vertical="50%"/> | background-position-vertical="50%"/> | ||||
</fo:simple-page-master> | </fo:simple-page-master> |
<fo:layout-master-set> | <fo:layout-master-set> | ||||
<fo:simple-page-master master-name="page" | <fo:simple-page-master master-name="page" | ||||
page-height="220pt" page-width="320pt" margin="10pt"> | 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:simple-page-master> | ||||
</fo:layout-master-set> | </fo:layout-master-set> | ||||
<fo:page-sequence master-reference="page"> | <fo:page-sequence master-reference="page"> |
<fo:layout-master-set> | <fo:layout-master-set> | ||||
<fo:simple-page-master master-name="page" | <fo:simple-page-master master-name="page" | ||||
page-height="220pt" page-width="320pt" margin="10pt"> | 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-repeat="no-repeat" background-position-horizontal="50%" | ||||
background-position-vertical="50%"/> | background-position-vertical="50%"/> | ||||
</fo:simple-page-master> | </fo:simple-page-master> |
external-destination="http://xmlgraphics.apache.org/fop/">FOP | external-destination="http://xmlgraphics.apache.org/fop/">FOP | ||||
website</fo:basic-link></fo:wrapper> for more | website</fo:basic-link></fo:wrapper> for more | ||||
information</fo:block></fo:footnote-body></fo:footnote>. FOP has a nice logo: | 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" | inline-progression-dimension.maximum="100%" content-width="scale-to-fit" | ||||
fox:alt-text="FOP Logo"/></fo:block> | fox:alt-text="FOP Logo"/></fo:block> | ||||
<fo:table space-before="10pt" space-after="10pt" width="100%" table-layout="fixed"> | <fo:table space-before="10pt" space-after="10pt" width="100%" table-layout="fixed"> |
<accessibility>true</accessibility> | <accessibility>true</accessibility> | ||||
<source-resolution>144</source-resolution> | <source-resolution>144</source-resolution> | ||||
<use-cache>false</use-cache> | <use-cache>false</use-cache> | ||||
<font-base>../resources/fonts/</font-base> | |||||
<font-base>../../resources/fonts/ttf/</font-base> | |||||
<renderers> | <renderers> | ||||
<renderer mime="application/pdf"> | <renderer mime="application/pdf"> | ||||
<filterList> | <filterList> |
<fo:page-sequence master-reference="page"> | <fo:page-sequence master-reference="page"> | ||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | <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 | <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" | inline-progression-dimension.maximum="100%" content-width="scale-to-fit" | ||||
fox:alt-text="CMYK colours"/>. Here is the end of the text.</fo:block> | fox:alt-text="CMYK colours"/>. Here is the end of the text.</fo:block> | ||||
</fo:flow> | </fo:flow> |
<fo:page-sequence master-reference="page"> | <fo:page-sequence master-reference="page"> | ||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | <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 | <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" | inline-progression-dimension.maximum="100%" content-width="scale-to-fit" | ||||
fox:alt-text="FOP Logo"/>. Here is the end of the text.</fo:block> | fox:alt-text="FOP Logo"/>. Here is the end of the text.</fo:block> | ||||
</fo:flow> | </fo:flow> |
<fo:page-sequence master-reference="page"> | <fo:page-sequence master-reference="page"> | ||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | <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 | <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" | 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: | 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" | <fo:instream-foreign-object inline-progression-dimension.maximum="75pt" |
<fo:page-sequence master-reference="page"> | <fo:page-sequence master-reference="page"> | ||||
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> | <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 | <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" | 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> | fox:alt-text="Metafile Companion Test Chart"/> Here is the end of the text.</fo:block> | ||||
</fo:flow> | </fo:flow> |
<fo:block space-before="10pt">This is a text followed by a leader with | <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", the content being images:<fo:leader | ||||
leader-pattern="use-content"><fo:external-graphic | 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:flow> | ||||
</fo:page-sequence> | </fo:page-sequence> | ||||
</fo:root> | </fo:root> |