import java.io.IOException;
import java.io.OutputStream;
+import java.util.Set;
import org.apache.commons.io.output.CountingOutputStream;
/** The filters that should be applied */
private PDFFilterList filters;
- private final boolean encodeOnTheFly;
+ private boolean encodeOnTheFly;
+
+ private PDFNumber refLength = new PDFNumber();
protected AbstractPDFStream() {
this(true);
StringBuilder textBuffer = new StringBuilder(64);
StreamCache encodedStream = null;
- PDFNumber refLength = null;
final Object lengthEntry;
if (encodeOnTheFly) {
- refLength = new PDFNumber();
- getDocumentSafely().registerObject(refLength);
+ if (!refLength.hasObjectNumber()) {
+ registerChildren();
+ }
lengthEntry = refLength;
} else {
encodedStream = encodeStream();
protected boolean multipleFiltersAllowed() {
return true;
}
+
+ @Override
+ public void getChildren(Set<PDFObject> children) {
+ dictionary.getChildren(children);
+ if (encodeOnTheFly) {
+ children.add(refLength);
+ }
+ }
+
+ public void registerChildren() {
+ if (encodeOnTheFly) {
+ getDocument().registerObject(refLength);
+ }
+ }
}
* @param mask the transparency mask reference if any
*/
public BitmapImage(String k, int width, int height, byte[] data,
- String mask) {
+ PDFReference mask) {
this.key = k;
this.height = height;
this.width = width;
this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
this.bitmaps = data;
if (mask != null) {
- maskRef = new PDFReference(mask);
+ maskRef = mask;
}
}
*
* @return the object number.
*/
- int getObjectNumber();
+ PDFObjectNumber getObjectNumber();
/**
* Outputs this object's content into the given stream.
// Java
import java.util.List;
+import java.util.Set;
/**
* class representing an object which is a list of annotations.
* 19 0 R
* ]
*/
+
+ @Override
+ public void getChildren(Set<PDFObject> children) {
+ PDFDictionary.getChildren(links, children);
+ }
}
import java.io.IOException;
import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import org.apache.commons.io.output.CountingOutputStream;
return cout.getCount();
}
+ @Override
+ public void getChildren(Set<PDFObject> children) {
+ List<Object> contents = new ArrayList<Object>();
+ for (Object c : values) {
+ if (!(c instanceof PDFReference)) {
+ contents.add(c);
+ }
+ }
+ PDFDictionary.getChildren(contents, children);
+ }
}
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.util.Set;
import org.apache.fop.fonts.CIDFontType;
return bout.toByteArray();
}
+ @Override
+ public void getChildren(Set<PDFObject> children) {
+ super.getChildren(children);
+ if (cidMap != null) {
+ children.add(cidMap);
+ cidMap.getChildren(children);
+ }
+ children.add(descriptor);
+ descriptor.getChildren(children);
+ if (cmap != null) {
+ children.add(cmap);
+ cmap.getChildren(children);
+ }
+ }
+
}
import java.io.IOException;
import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.apache.commons.io.output.CountingOutputStream;
* Class representing a PDF dictionary object
*/
public class PDFDictionary extends PDFObject {
-
+ private boolean visited;
/**
* the entry map
*/
textBuffer.append(">>");
}
+ @Override
+ public void getChildren(Set<PDFObject> children) {
+ if (!visited) {
+ visited = true;
+ Map<String, Object> childrenMap = new HashMap<String, Object>(entries);
+ childrenMap.remove("Parent");
+ getChildren(childrenMap.values(), children);
+ visited = false;
+ }
+ }
+
+ public static void getChildren(Collection<Object> values, Set<PDFObject> children) {
+ for (Object x : values) {
+ if (x instanceof PDFReference) {
+ x = ((PDFReference) x).getObject();
+ }
+ if (x instanceof PDFObject) {
+ if (((PDFObject) x).hasObjectNumber()) {
+ children.add((PDFObject) x);
+ }
+ ((PDFObject) x).getChildren(children);
+ }
+ }
+ }
}
private Log log = LogFactory.getLog("org.apache.fop.pdf");
/** the current character position */
- private long position;
+ protected long position;
/** the character position of each object */
- private List<Long> indirectObjectOffsets = new ArrayList<Long>();
+ protected List<Long> indirectObjectOffsets = new ArrayList<Long>();
- private Collection<PDFStructElem> structureTreeElements;
+ protected List<PDFStructElem> structureTreeElements;
/** List of objects to write in the trailer */
- private List<PDFObject> trailerObjects = new ArrayList<PDFObject>();
+ protected List<PDFObject> trailerObjects = new ArrayList<PDFObject>();
/** the objects themselves */
- private List<PDFObject> objects = new LinkedList<PDFObject>();
+ protected List<PDFObject> objects = new LinkedList<PDFObject>();
/** Controls the PDF version of this document */
private VersionController versionController;
/** the counter for XObject numbering */
private int xObjectCount;
+ protected int gStateObjectCount;
+
/* TODO: Should be modified (works only for image subtype) */
private Map<String, PDFXObject> xObjectsMap = new HashMap<String, PDFXObject>();
private List<PDFLaunch> launches = new ArrayList<PDFLaunch>();
+ protected List<PDFPage> pageObjs = new ArrayList<PDFPage>();
+
private List<PDFLayer> layers;
private List<PDFNavigator> navigators;
private boolean mergeFontsEnabled;
+ private boolean linearizationEnabled;
+
+ protected boolean outputStarted;
+
/**
* Creates an empty PDF document.
*
return this.root;
}
+ /**
+ * Get the Structural Tree Collection for this document
+ * @return
+ */
+ public List<PDFStructElem> getStructureTreeElements() {
+ return structureTreeElements;
+ }
+
/**
* Creates and returns a StructTreeRoot object.
*
public PDFObject registerObject(PDFObject obj) {
assignObjectNumber(obj);
addObject(obj);
+ if (obj instanceof AbstractPDFStream) {
+ ((AbstractPDFStream) obj).registerChildren();
+ }
return obj;
}
* @param obj {@link PDFObject} to assign a number to
*/
public void assignObjectNumber(PDFObject obj) {
+ if (outputStarted && isLinearizationEnabled()) {
+ throw new IllegalStateException("Can't assign number after start of output");
+ }
if (obj == null) {
throw new NullPointerException("obj must not be null");
}
+ "PDFObject already has a parent PDFDocument");
}
- obj.setObjectNumber(++this.objectcount);
+ obj.setObjectNumber(this);
if (currentParent == null) {
obj.setDocument(this);
}
if (obj instanceof PDFPage) {
this.pages.notifyKidRegistered((PDFPage)obj);
+ pageObjs.add((PDFPage) obj);
}
if (obj instanceof PDFLaunch) {
this.launches.add((PDFLaunch) obj);
public void setEncryption(PDFEncryptionParams params) {
getProfile().verifyEncryptionAllowed();
fileIDGenerator = FileIDGenerator.getRandomFileIDGenerator();
- this.encryption = PDFEncryptionManager.newInstance(++this.objectcount, params, this);
+ this.encryption = PDFEncryptionManager.newInstance(params, this);
if (this.encryption != null) {
PDFObject pdfObject = (PDFObject)this.encryption;
addTrailerObject(pdfObject);
* @throws IOException if there is an exception writing to the output stream
*/
public void output(OutputStream stream) throws IOException {
+ outputStarted = true;
//Write out objects until the list is empty. This approach (used with a
//LinkedList) allows for output() methods to create and register objects
//on the fly even during serialization.
}
}
- private void streamIndirectObject(PDFObject o, OutputStream stream) throws IOException {
+ protected void writeTrailer(OutputStream stream, int first, int last, int size, long mainOffset, long startxref)
+ throws IOException {
+ TrailerOutputHelper trailerOutputHelper = mayCompressStructureTreeElements()
+ ? new CompressedTrailerOutputHelper()
+ : new UncompressedTrailerOutputHelper();
+ if (structureTreeElements != null) {
+ trailerOutputHelper.outputStructureTreeElements(stream);
+ }
+ TrailerDictionary trailerDictionary = createTrailerDictionary(mainOffset != 0);
+ if (mainOffset != 0) {
+ trailerDictionary.getDictionary().put("Prev", mainOffset);
+ }
+ trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary, first, last, size);
+ String trailer = "\nstartxref\n" + startxref + "\n%%EOF\n";
+ stream.write(encode(trailer));
+ }
+
+ protected int streamIndirectObject(PDFObject o, OutputStream stream) throws IOException {
+ outputStarted = true;
recordObjectOffset(o);
- this.position += outputIndirectObject(o, stream);
+ int len = outputIndirectObject(o, stream);
+ this.position += len;
+ return len;
}
private void streamIndirectObjects(Collection<? extends PDFObject> objects, OutputStream stream)
}
private void recordObjectOffset(PDFObject object) {
- int index = object.getObjectNumber() - 1;
+ int index = object.getObjectNumber().getNumber() - 1;
while (indirectObjectOffsets.size() <= index) {
indirectObjectOffsets.add(null);
}
trailerOutputHelper.outputStructureTreeElements(stream);
}
streamIndirectObjects(trailerObjects, stream);
- TrailerDictionary trailerDictionary = createTrailerDictionary();
- long startxref = trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary);
+ TrailerDictionary trailerDictionary = createTrailerDictionary(true);
+ long startxref = trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary, 0,
+ indirectObjectOffsets.size(), indirectObjectOffsets.size());
String trailer = "\nstartxref\n" + startxref + "\n%%EOF\n";
stream.write(encode(trailer));
}
private boolean mayCompressStructureTreeElements() {
return accessibilityEnabled
- && versionController.getPDFVersion().compareTo(Version.V1_5) >= 0;
+ && versionController.getPDFVersion().compareTo(Version.V1_5) >= 0
+ && !isLinearizationEnabled();
}
- private TrailerDictionary createTrailerDictionary() {
+ private TrailerDictionary createTrailerDictionary(boolean addRoot) {
FileIDGenerator gen = getFileIDGenerator();
- TrailerDictionary trailerDictionary = new TrailerDictionary(this)
- .setRoot(root)
- .setInfo(info)
- .setFileID(gen.getOriginalFileID(), gen.getUpdatedFileID());
+ TrailerDictionary trailerDictionary = new TrailerDictionary(this);
+ if (addRoot) {
+ trailerDictionary.setRoot(root).setInfo(info);
+ }
+ trailerDictionary.setFileID(gen.getOriginalFileID(), gen.getUpdatedFileID());
if (isEncryptionActive()) {
trailerDictionary.setEncryption(encryption);
}
/**
* @return the offset of the cross-reference object (the value of startxref)
*/
- long outputCrossReferenceObject(OutputStream stream, TrailerDictionary trailerDictionary)
+ long outputCrossReferenceObject(OutputStream stream, TrailerDictionary trailerDictionary,
+ int first, int last, int size)
throws IOException;
}
}
public long outputCrossReferenceObject(OutputStream stream,
- TrailerDictionary trailerDictionary) throws IOException {
+ TrailerDictionary trailerDictionary, int first, int last, int size) throws IOException {
new CrossReferenceTable(trailerDictionary, position,
- indirectObjectOffsets).output(stream);
+ indirectObjectOffsets, first, last, size).output(stream);
return position;
}
}
}
public long outputCrossReferenceObject(OutputStream stream,
- TrailerDictionary trailerDictionary) throws IOException {
+ TrailerDictionary trailerDictionary, int first, int last, int size) throws IOException {
// Outputting the object streams should not have created new indirect objects
assert objects.isEmpty();
new CrossReferenceStream(PDFDocument.this, ++objectcount, trailerDictionary, position,
}
return fileIDGenerator;
}
+
+ public boolean isLinearizationEnabled() {
+ return linearizationEnabled;
+ }
+
+ public void setLinearizationEnabled(boolean b) {
+ linearizationEnabled = b;
+ }
+
}
private class EncryptionFilter extends PDFFilter {
- private int streamNumber;
+ private PDFObjectNumber streamNumber;
private int streamGeneration;
- EncryptionFilter(int streamNumber, int streamGeneration) {
+ EncryptionFilter(PDFObjectNumber streamNumber, int streamGeneration) {
this.streamNumber = streamNumber;
this.streamGeneration = streamGeneration;
}
out.flush();
return new CipherOutputStream(out, cipher);
} else {
- byte[] key = createEncryptionKey(streamNumber, streamGeneration);
+ byte[] key = createEncryptionKey(streamNumber.getNumber(), streamGeneration);
Cipher cipher = initCipher(key);
return new CipherOutputStream(out, cipher);
}
}
- private PDFEncryptionJCE(int objectNumber, PDFEncryptionParams params, PDFDocument pdf) {
+ private PDFEncryptionJCE(PDFObjectNumber objectNumber, PDFEncryptionParams params, PDFDocument pdf) {
setObjectNumber(objectNumber);
try {
if (params.getEncryptionLengthInBits() == 256) {
* @return the newly created encryption object
*/
public static PDFEncryption make(
- int objectNumber, PDFEncryptionParams params, PDFDocument pdf) {
+ PDFObjectNumber objectNumber, PDFEncryptionParams params, PDFDocument pdf) {
return new PDFEncryptionJCE(objectNumber, params, pdf);
}
System.arraycopy(encryptedData, 0, storedData, 16, encryptedData.length);
return storedData;
} else {
- byte[] key = createEncryptionKey(o.getObjectNumber(), o.getGeneration());
+ byte[] key = createEncryptionKey(o.getObjectNumber().getNumber(), o.getGeneration());
return encryptWithKey(key, data);
}
}
/**
* Creates a new PDFEncryption instance if PDF encryption is available.
- * @param objnum PDF object number
* @param params PDF encryption parameters
* @param pdf the PDF document to encrypt
* @return PDFEncryption the newly created instance, null if PDF encryption
* is unavailable.
*/
- public static PDFEncryption newInstance(int objnum, PDFEncryptionParams params,
+ public static PDFEncryption newInstance(PDFEncryptionParams params,
PDFDocument pdf) {
try {
+ PDFObjectNumber pdfObjectNumber = new PDFObjectNumber();
+ pdfObjectNumber.setDocument(pdf);
Class<?> clazz = Class.forName("org.apache.fop.pdf.PDFEncryptionJCE");
Method makeMethod = clazz.getMethod("make",
- new Class[] {int.class, PDFEncryptionParams.class, PDFDocument.class});
+ new Class[] {PDFObjectNumber.class, PDFEncryptionParams.class, PDFDocument.class});
Object obj = makeMethod.invoke(null,
- new Object[] {new Integer(objnum), params, pdf});
+ new Object[] {pdfObjectNumber, params, pdf});
return (PDFEncryption)obj;
} catch (ClassNotFoundException e) {
if (checkAvailableAlgorithms()) {
*/
public PDFRoot makeRoot(PDFPages pages) {
//Make a /Pages object. This object is written in the trailer.
- PDFRoot pdfRoot = new PDFRoot(++this.document.objectcount, pages);
+ PDFRoot pdfRoot = new PDFRoot(document, pages);
pdfRoot.setDocument(getDocument());
getDocument().addTrailerObject(pdfRoot);
return pdfRoot;
* @return a new PDF Pages object for adding pages to
*/
public PDFPages makePages() {
- PDFPages pdfPages = new PDFPages(++(this.document.objectcount));
+ PDFPages pdfPages = new PDFPages(getDocument());
pdfPages.setDocument(getDocument());
getDocument().addTrailerObject(pdfPages);
return pdfPages;
* @return a new PDF resources object
*/
public PDFResources makeResources() {
- PDFResources pdfResources = new PDFResources(++this.document.objectcount);
+ PDFResources pdfResources = new PDFResources(getDocument());
pdfResources.setDocument(getDocument());
getDocument().addTrailerObject(pdfResources);
return pdfResources;
* create a PDFPage with the next object number, the given
* resources, contents and dimensions
*/
- PDFPage page = new PDFPage(resources, pageIndex, mediaBox, cropBox, bleedBox, trimBox);
+ PDFResources res = getDocument().getFactory().makeResources();
+ if (getDocument().isLinearizationEnabled()) {
+ getDocument().trailerObjects.remove(resources);
+ }
+ res.setParentResources(resources);
+
+ PDFPage page = new PDFPage(res, pageIndex, mediaBox, cropBox, bleedBox, trimBox);
+ res.setParent(page);
getDocument().assignObjectNumber(page);
getDocument().getPages().addPage(page);
* @return the new PDF outline object
*/
public PDFOutline makeOutline(PDFOutline parent, String label,
- String actionRef, boolean showSubItems) {
+ PDFReference actionRef, boolean showSubItems) {
PDFOutline pdfOutline = new PDFOutline(label, actionRef, showSubItems);
if (parent != null) {
parent.addOutline(pdfOutline);
PDFAction pdfAction, boolean showSubItems) {
return pdfAction == null
? null
- : makeOutline(parent, label, pdfAction.getAction(), showSubItems);
+ : makeOutline(parent, label, new PDFReference(pdfAction.getAction()), showSubItems);
}
// This one is obsolete now, at least it isn't called from anywhere inside FOP
boolean showSubItems) {
String goToRef = getGoToReference(destination, yoffset);
- return makeOutline(parent, label, goToRef, showSubItems);
+ return makeOutline(parent, label, new PDFReference(goToRef), showSubItems);
}
}
private Map values = new java.util.HashMap();
+ private int objNum;
/**
* Returns the name of this object
* @return the name
*/
public String getName() {
- return "GS" + getObjectNumber();
+ if (objNum == 0) {
+ objNum = ++getDocument().gStateObjectCount;
+ }
+ return "GS" + objNum;
}
/**
/**
* the pageReference
*/
- private String pageReference;
+ private PDFReference pageReference;
private String destination;
private float xPosition;
private float yPosition;
*/
public PDFGoTo(String pageReference) {
super();
- setPageReference(pageReference);
+ if (pageReference != null) {
+ setPageReference(new PDFReference(pageReference));
+ }
}
/**
*
* @param pageReference the new page reference to use
*/
- public void setPageReference(String pageReference) {
+ public void setPageReference(PDFReference pageReference) {
this.pageReference = pageReference;
}
import java.awt.color.ICC_Profile;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Set;
import org.apache.commons.io.IOUtils;
return sRGBProfile;
}
+ @Override
+ public void getChildren(Set<PDFObject> children) {
+ super.getChildren(children);
+ children.add(iccStream);
+ iccStream.getChildren(children);
+ }
}
// Java
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Set;
/* modified by JKT to integrate with 0.12.0 */
/* modified by Eric SCHAEFFER to integrate with 0.13.0 */
return pdfimage.multipleFiltersAllowed();
}
+ @Override
+ public void getChildren(Set<PDFObject> children) {
+ super.getChildren(children);
+ PDFICCStream pdfICCStream = pdfimage.getICCStream();
+ if (pdfICCStream != null) {
+ children.add(pdfICCStream);
+ pdfICCStream.getChildren(children);
+ }
+ }
+
}
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Date;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TimeZone;
throw new IllegalArgumentException(key + " is a reserved keyword");
}
if (customProperties == null) {
- customProperties = new HashMap<PDFName, String>();
+ customProperties = new LinkedHashMap<PDFName, String>();
}
customProperties.put(new PDFName(key), value);
}
--- /dev/null
+/*
+ * 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.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.io.output.CountingOutputStream;
+
+public class PDFLinearization {
+ private PDFDocument doc;
+ private Map<PDFPage, Set<PDFObject>> pageObjsMap = new HashMap<PDFPage, Set<PDFObject>>();
+ private PDFDictionary linearDict;
+ private HintTable hintTable;
+
+ public PDFLinearization(PDFDocument doc) {
+ this.doc = doc;
+ }
+
+ static class HintTable extends PDFStream {
+ private List<PDFPage> pages;
+ int pageStartPos;
+ List<Integer> sharedLengths = new ArrayList<Integer>();
+ List<Integer> pageLengths = new ArrayList<Integer>();
+ List<Integer> contentStreamLengths = new ArrayList<Integer>();
+ List<Integer> objCount = new ArrayList<Integer>();
+ Map<String, int[]> hintGroups = new HashMap<String, int[]>();
+
+ public HintTable(PDFDocument doc) {
+ super(false);
+ doc.assignObjectNumber(this);
+ doc.addObject(this);
+ pages = doc.pageObjs;
+ for (int i = 0; i < pages.size(); i++) {
+ pageLengths.add(0);
+ contentStreamLengths.add(0);
+ objCount.add(0);
+ }
+ hintGroups.put("/C", new int[4]);
+ hintGroups.put("/L", new int[4]);
+ hintGroups.put("/I", new int[4]);
+ hintGroups.put("/E", new int[4]);
+ hintGroups.put("/O", new int[4]);
+ hintGroups.put("/V", new int[4]);
+ }
+
+ @Override
+ public PDFFilterList getFilterList() {
+ return new PDFFilterList(getDocument().isEncryptionActive());
+ }
+
+ @Override
+ protected void outputRawStreamData(OutputStream os) throws IOException {
+ CountingOutputStream bos = new CountingOutputStream(os);
+
+ //start header
+ writeULong(1, bos); //1
+ writeULong(pageStartPos, bos); //2
+ writeCard16(32, bos); //3
+ writeULong(0, bos); //4
+ writeCard16(32, bos); //5
+ writeULong(0, bos); //6
+ writeCard16(0, bos); //7
+ writeULong(0, bos); //8
+ writeCard16(32, bos); //9
+ writeCard16(0, bos); //10
+ writeCard16(0, bos); //11
+ writeCard16(0, bos); //12
+ writeCard16(4, bos); //13
+ //end header
+
+ for (PDFPage page : pages) {
+ writeULong(objCount.get(page.pageIndex) - 1, bos);
+ }
+ for (PDFPage page : pages) {
+ writeULong(pageLengths.get(page.pageIndex), bos);
+ }
+ for (PDFPage page : pages) {
+ writeULong(contentStreamLengths.get(page.pageIndex), bos);
+ }
+
+ writeSharedTable(bos);
+
+ for (Map.Entry<String, int[]> group : hintGroups.entrySet()) {
+ put(group.getKey(), bos.getCount());
+ for (int i : group.getValue()) {
+ writeULong(i, bos);
+ }
+ if (group.getKey().equals("/C")) {
+ writeULong(0, bos);
+ writeCard16(0, bos);
+ }
+ }
+ }
+
+ private void writeSharedTable(CountingOutputStream bos) throws IOException {
+ put("/S", bos.getCount());
+
+ //Shared object hint table, header section
+ writeULong(0, bos); //1
+ writeULong(0, bos); //2
+ writeULong(sharedLengths.size(), bos); //3
+ writeULong(sharedLengths.size(), bos); //4
+ writeCard16(0, bos); //5
+ writeULong(0, bos); //6
+ writeCard16(32, bos); //7
+
+ for (int i : sharedLengths) {
+ writeULong(i, bos);
+ }
+ writeULong(0, bos);
+ }
+
+ private void writeCard16(int s, OutputStream bos) throws IOException {
+ byte b1 = (byte)((s >> 8) & 0xff);
+ byte b2 = (byte)(s & 0xff);
+ bos.write(b1);
+ bos.write(b2);
+ }
+
+ private void writeULong(int s, OutputStream bos) throws IOException {
+ byte b1 = (byte)((s >> 24) & 0xff);
+ byte b2 = (byte)((s >> 16) & 0xff);
+ byte b3 = (byte)((s >> 8) & 0xff);
+ byte b4 = (byte)(s & 0xff);
+ bos.write(b1);
+ bos.write(b2);
+ bos.write(b3);
+ bos.write(b4);
+ }
+ }
+
+ static class LinearPDFDictionary extends PDFDictionary {
+ private int lastsize = -1;
+
+ public LinearPDFDictionary(PDFDocument doc) {
+ put("Linearized", 1);
+ put("/L", 0);
+ PDFArray larray = new PDFArray();
+ larray.add(0);
+ larray.add(0);
+ put("/H", larray);
+ doc.assignObjectNumber(this);
+ getObjectNumber().getNumber();
+ put("/O", getObjectNumber().getNumber() + 3);
+ put("/E", 0);
+ put("/N", doc.pageObjs.size());
+ put("/T", 0);
+ }
+
+ public int output(OutputStream stream) throws IOException {
+ int size = super.output(stream);
+ int padding = lastsize - size + 32;
+ if (lastsize == -1) {
+ padding = 32;
+ lastsize = size;
+ }
+ writePadding(padding, stream);
+ return size + padding;
+ }
+ }
+
+
+ private Set<PDFObject> assignNumbers() throws IOException {
+ Set<PDFObject> page1Children = getPage1Children();
+ if (!doc.pageObjs.isEmpty()) {
+ for (int i = 1; i < doc.pageObjs.size(); i++) {
+ PDFPage page = doc.pageObjs.get(i);
+ Set<PDFObject> children = pageObjsMap.get(page);
+ for (PDFObject c : children) {
+ if (!page1Children.contains(c) && c.hasObjectNumber()) {
+ c.getObjectNumber().getNumber();
+ }
+ }
+ }
+ for (PDFObject o : doc.objects) {
+ if (o instanceof PDFDests || o instanceof PDFOutline) {
+ for (PDFObject c : getChildren(o)) {
+ c.getObjectNumber().getNumber();
+ }
+ }
+ if (o instanceof PDFInfo || o instanceof PDFPageLabels) {
+ o.getObjectNumber().getNumber();
+ }
+ }
+ for (PDFObject o : doc.objects) {
+ if (!page1Children.contains(o)) {
+ o.getObjectNumber().getNumber();
+ }
+ }
+ }
+ linearDict = new LinearPDFDictionary(doc);
+ for (PDFObject o : page1Children) {
+ o.getObjectNumber().getNumber();
+ }
+ sort(doc.objects);
+ return page1Children;
+ }
+
+ private void sort(List<PDFObject> objects) {
+ Collections.sort(objects, new Comparator<PDFObject>() {
+ public int compare(PDFObject o1, PDFObject o2) {
+ return ((Integer) o1.getObjectNumber().getNumber()).compareTo(o2.getObjectNumber().getNumber());
+ }
+ });
+ }
+
+ private Set<PDFObject> getChildren(PDFObject o) {
+ Set<PDFObject> children = new LinkedHashSet<PDFObject>();
+ children.add(o);
+ o.getChildren(children);
+ return children;
+ }
+
+ public void outputPages(OutputStream stream) throws IOException {
+ Collections.sort(doc.pageObjs, new Comparator<PDFPage>() {
+ public int compare(PDFPage o1, PDFPage o2) {
+ return ((Integer) o1.pageIndex).compareTo(o2.pageIndex);
+ }
+ });
+ doc.objects.addAll(doc.trailerObjects);
+ doc.trailerObjects = null;
+ if (doc.getStructureTreeElements() != null) {
+ doc.objects.addAll(doc.getStructureTreeElements());
+ doc.structureTreeElements = null;
+ }
+ for (int i = 0; i < doc.objects.size() * 2; i++) {
+ doc.indirectObjectOffsets.add(0L);
+ }
+ Set<PDFObject> page1Children = assignNumbers();
+ doc.streamIndirectObject(linearDict, new ByteArrayOutputStream());
+ for (PDFObject o : page1Children) {
+ doc.objects.remove(o);
+ }
+ int sizeOfRest = doc.objects.size();
+
+ ByteArrayOutputStream fakeHeaderTrailerStream = new ByteArrayOutputStream();
+ long topTrailer = doc.position;
+ doc.writeTrailer(fakeHeaderTrailerStream, sizeOfRest, page1Children.size() + 1,
+ page1Children.size() + sizeOfRest + 1, Long.MAX_VALUE, 0);
+ doc.position += fakeHeaderTrailerStream.size();
+
+ ByteArrayOutputStream pageStream = new ByteArrayOutputStream();
+ writeObjects(page1Children, pageStream, sizeOfRest + 1);
+ long trailerOffset = doc.position;
+ ByteArrayOutputStream footerTrailerStream = new ByteArrayOutputStream();
+ doc.writeTrailer(footerTrailerStream, 0, sizeOfRest, sizeOfRest, 0, topTrailer);
+ doc.position += footerTrailerStream.size();
+
+ linearDict.put("/L", doc.position);
+
+ PDFDocument.outputIndirectObject(linearDict, stream);
+ CountingOutputStream realTrailer = new CountingOutputStream(stream);
+ doc.writeTrailer(realTrailer, sizeOfRest, page1Children.size() + 1,
+ page1Children.size() + sizeOfRest + 1, trailerOffset, 0);
+ writePadding(fakeHeaderTrailerStream.size() - realTrailer.getCount(), stream);
+ for (PDFObject o : page1Children) {
+ PDFDocument.outputIndirectObject(o, stream);
+ if (o instanceof HintTable) {
+ break;
+ }
+ }
+ stream.write(pageStream.toByteArray());
+ stream.write(footerTrailerStream.toByteArray());
+ }
+
+ private Set<PDFObject> getPage1Children() throws IOException {
+ Set<PDFObject> page1Children = new LinkedHashSet<PDFObject>();
+ if (!doc.pageObjs.isEmpty()) {
+ PDFPage page1 = doc.pageObjs.get(0);
+ page1Children.add(doc.getRoot());
+ hintTable = new HintTable(doc);
+ page1Children.add(hintTable);
+ page1Children.add(page1);
+ page1.getChildren(page1Children);
+ doc.objects.remove(doc.getPages());
+ doc.objects.add(0, doc.getPages());
+ pageObjsMap.put(page1, page1Children);
+
+ for (int i = 1; i < doc.pageObjs.size(); i++) {
+ PDFPage page = doc.pageObjs.get(i);
+ pageObjsMap.put(page, getChildren(page));
+ }
+ }
+ return page1Children;
+ }
+
+ private static void writePadding(int padding, OutputStream stream) throws IOException {
+ for (int i = 0; i < padding; i++) {
+ stream.write(" ".getBytes("UTF-8"));
+ }
+ }
+
+ private void writeObjects(Set<PDFObject> children1, OutputStream pageStream, int sizeOfRest) throws IOException {
+ writePage1(children1, pageStream);
+ linearDict.put("/E", doc.position);
+ for (PDFPage page : doc.pageObjs) {
+ if (page.pageIndex != 0) {
+ writePage(page, pageStream);
+ }
+ }
+ while (!doc.objects.isEmpty()) {
+ PDFObject o = doc.objects.remove(0);
+ if (o instanceof PDFOutline) {
+ writeObjectGroup("/O", getChildren(o), pageStream);
+ } else if (o instanceof PDFDests) {
+ writeObjectGroup("/E", getChildren(o), pageStream);
+ } else if (o instanceof PDFInfo) {
+ writeObjectGroup("/I", getChildren(o), pageStream);
+ } else if (o instanceof PDFPageLabels) {
+ writeObjectGroup("/L", getChildren(o), pageStream);
+ } else if (o instanceof PDFStructTreeRoot) {
+ writeObjectGroup("/C", getChildren(o), pageStream);
+ } else {
+ doc.streamIndirectObject(o, pageStream);
+ }
+ }
+ linearDict.put("/T", doc.position + 8 + String.valueOf(sizeOfRest).length());
+ }
+
+ private void writeObjectGroup(String name, Set<PDFObject> objects, OutputStream pageStream)
+ throws IOException {
+ List<PDFObject> children = new ArrayList<PDFObject>(objects);
+ sort(children);
+
+ int[] values = hintTable.hintGroups.get(name);
+ values[0] = children.iterator().next().getObjectNumber().getNumber();
+ values[1] = (int) doc.position;
+ values[2] = children.size();
+ for (PDFObject o : children) {
+ values[3] += doc.streamIndirectObject(o, pageStream);
+ doc.objects.remove(o);
+ }
+ }
+
+ private void writePage1(Set<PDFObject> children1, OutputStream pageStream) throws IOException {
+ hintTable.pageStartPos = (int) doc.position;
+ OutputStream stream = new ByteArrayOutputStream();
+
+ Set<PDFObject> sharedChildren = getSharedObjects();
+
+ int page1Len = 0;
+ int objCount = 0;
+ int sharedCount = 0;
+ for (PDFObject o : children1) {
+ if (o instanceof HintTable) {
+ PDFArray a = (PDFArray) linearDict.get("/H");
+ a.set(0, doc.position);
+ doc.streamIndirectObject(o, stream);
+ a.set(1, doc.position - (Double)a.get(0));
+ stream = pageStream;
+ } else {
+ int len = doc.streamIndirectObject(o, stream);
+ if (o instanceof PDFStream && hintTable.contentStreamLengths.get(0) == 0) {
+ hintTable.contentStreamLengths.set(0, len);
+ }
+ if (!(o instanceof PDFRoot)) {
+ page1Len += len;
+ objCount++;
+ }
+ if (sharedChildren.contains(o)) {
+ hintTable.sharedLengths.set(sharedCount, len);
+ sharedCount++;
+ }
+ }
+ }
+ hintTable.pageLengths.set(0, page1Len);
+ hintTable.objCount.set(0, objCount);
+ }
+
+ private Set<PDFObject> getSharedObjects() {
+ Set<PDFObject> pageSharedChildren = getChildren(doc.pageObjs.get(0));
+ for (int i = 0; i < pageSharedChildren.size(); i++) {
+ hintTable.sharedLengths.add(0);
+ }
+ return pageSharedChildren;
+ }
+
+ private void writePage(PDFPage page, OutputStream pageStream) throws IOException {
+ Set<PDFObject> children = pageObjsMap.get(page);
+ int pageLen = 0;
+ int objCount = 0;
+ for (PDFObject c : children) {
+ if (doc.objects.contains(c)) {
+ int len = doc.streamIndirectObject(c, pageStream);
+ if (c instanceof PDFStream) {
+ hintTable.contentStreamLengths.set(page.pageIndex, len);
+ }
+ pageLen += len;
+ doc.objects.remove(c);
+ objCount++;
+ }
+ }
+ hintTable.pageLengths.set(page.pageIndex, pageLen);
+ hintTable.objCount.set(page.pageIndex, objCount);
+ }
+}
// Java
import java.awt.geom.Rectangle2D;
+import java.util.Set;
/**
* class representing an /Annot object of /Subtype /Link
return true;
}
+
+ @Override
+ public void getChildren(Set<PDFObject> children) {
+ super.getChildren(children);
+ if (action.hasObjectNumber()) {
+ children.add(action);
+ }
+ action.getChildren(children);
+ }
}
// Java
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* the object's number
*/
- private int objnum;
+ private boolean hasObjNum;
+ private PDFObjectNumber objNum = new PDFObjectNumber();
/**
* the object's generation (0 in new documents)
* Returns the object's number.
* @return the PDF Object number
*/
- public int getObjectNumber() {
- if (this.objnum == 0) {
+ public PDFObjectNumber getObjectNumber() {
+ if (!hasObjNum) {
throw new IllegalStateException("Object has no number assigned: " + this.toString());
}
- return this.objnum;
+ return objNum;
}
/**
* @return True if it has an object number
*/
public boolean hasObjectNumber() {
- return this.objnum > 0;
+ return hasObjNum;
}
/**
* Sets the object number
- * @param objnum the object number
*/
- public void setObjectNumber(int objnum) {
- this.objnum = objnum;
+ public void setObjectNumber(PDFDocument document) {
+ objNum.setDocument(document);
+ hasObjNum = true;
PDFDocument doc = getDocument();
setParent(null);
setDocument(doc); //Restore reference to PDFDocument after setting parent to null
if (log.isTraceEnabled()) {
- log.trace("Assigning " + this + " object number " + objnum);
+ log.trace("Assigning " + this + " object number " + objNum);
}
}
+ public void setObjectNumber(PDFObjectNumber objectNumber) {
+ objNum = objectNumber;
+ hasObjNum = true;
+ }
+
+ public void setObjectNumber(int objectNumber) {
+ objNum = new PDFObjectNumber(objectNumber);
+ hasObjNum = true;
+ }
+
/**
* Returns this object's generation.
* @return the PDF Object generation
throw new IllegalArgumentException(
"Cannot reference this object. It doesn't have an object number");
}
- String ref = getObjectNumber() + " " + getGeneration() + " R";
- return ref;
+ return makeReference().toString();
}
/**
protected boolean contentEquals(PDFObject o) {
return this.equals(o);
}
+
+ public void getChildren(Set<PDFObject> children) {
+ }
}
--- /dev/null
+/*
+ * 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;
+
+public class PDFObjectNumber {
+ private int num;
+ private PDFDocument doc;
+
+ public PDFObjectNumber() {
+ }
+ public PDFObjectNumber(int num) {
+ this.num = num;
+ }
+
+ public void setDocument(PDFDocument doc) {
+ this.doc = doc;
+ }
+
+ public int getNumber() {
+ if (num == 0 && doc != null) {
+// assert doc.outputStarted;
+ num = ++doc.objectcount;
+ }
+ return num;
+ }
+
+ public String toString() {
+ return String.valueOf(getNumber());
+ }
+}
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
+import java.util.Set;
/**
* <p>This represents a single Outline object in a PDF, including the root Outlines
*/
private String title;
- private String actionRef;
+ private PDFReference actionRef;
/**
* Create a PDF outline with the title and action.
* @param action the action for this outline
* @param openItem indicator of whether child items are visible or not
*/
- public PDFOutline(String title, String action, boolean openItem) {
+ public PDFOutline(String title, PDFReference action, boolean openItem) {
super();
subentries = new java.util.ArrayList();
count = 0;
return bout.toByteArray();
}
+ @Override
+ public void getChildren(Set<PDFObject> children) {
+ if (parent != null) {
+ children.add(parent);
+ }
+ if (first != null && last != null) {
+ children.add(first);
+ children.add(last);
+ first.getChildren(children);
+ last.getChildren(children);
+ }
+ if (actionRef != null) {
+ children.add(actionRef.getObject());
+ }
+ }
}
* It must also be allocated an object ID (so that the kids
* can refer to the parent) so that the XRef table needs to
* be updated before this object is written.
- *
- * @param objnum the object's number
*/
- public PDFPages(int objnum) {
- setObjectNumber(objnum);
+ public PDFPages(PDFDocument document) {
+ setObjectNumber(document);
}
/**
throw new IllegalStateException("A page already exists at index "
+ idx + " (zero-based).");
}
- this.kids.set(idx, page.referencePDF());
+ this.kids.set(idx, page.makeReference());
} else {
- this.kids.add(page.referencePDF());
+ this.kids.add(page.makeReference());
}
}
*/
public class PDFReference implements PDFWritable {
- private int objectNumber;
+ private PDFObjectNumber objectNumber;
private int generation;
private Reference<PDFObject> objReference;
}
String[] parts = ref.split(" ");
assert parts.length == 3;
- this.objectNumber = Integer.parseInt(parts[0]);
+ this.objectNumber = new PDFObjectNumber(Integer.parseInt(parts[0]));
this.generation = Integer.parseInt(parts[1]);
assert "R".equals(parts[2]);
}
* Returns the object number.
* @return the object number
*/
- public int getObjectNumber() {
+ public PDFObjectNumber getObjectNumber() {
return this.objectNumber;
}
/** {@inheritDoc} */
public void outputInline(OutputStream out, StringBuilder textBuffer) {
- textBuffer.append(getObjectNumber()).append(' ').append(getGeneration()).append(" R");
+ textBuffer.append(getObjectNumber().getNumber()).append(' ').append(getGeneration()).append(" R");
}
}
*/
protected Set<PDFXObject> xObjects = new LinkedHashSet<PDFXObject>();
/** Map of color spaces (key: color space name) */
- protected Map<PDFName, PDFColorSpace> colorSpaces = new LinkedHashMap<PDFName, PDFColorSpace>();
+ protected Map<LazyName, PDFColorSpace> colorSpaces = new LinkedHashMap<LazyName, PDFColorSpace>();
/** Map of ICC color spaces (key: ICC profile description) */
protected Map<String, PDFICCBasedColorSpace> iccColorSpaces = new LinkedHashMap<String, PDFICCBasedColorSpace>();
/**
* create a /Resources object.
- *
- * @param objnum the object's number
*/
- public PDFResources(int objnum) {
+ public PDFResources(PDFDocument doc) {
/* generic creation of object */
super();
- setObjectNumber(objnum);
+ setObjectNumber(doc);
}
public void addContext(PDFResourceContext c) {
* @param colorSpace the color space
*/
public void addColorSpace(PDFColorSpace colorSpace) {
- this.colorSpaces.put(new PDFName(colorSpace.getName()), colorSpace);
+ this.colorSpaces.put(new LazyName(colorSpace), colorSpace);
if (colorSpace instanceof PDFICCBasedColorSpace) {
PDFICCBasedColorSpace icc = (PDFICCBasedColorSpace)colorSpace;
String desc = ColorProfileUtil.getICCProfileDescription(
}
}
+ static class LazyName {
+ private PDFColorSpace colorSpace;
+ public LazyName(PDFColorSpace colorSpace) {
+ this.colorSpace = colorSpace;
+ }
+ public PDFName getName() {
+ return new PDFName(colorSpace.getName());
+ }
+ }
+
/**
* Returns a ICCBased color space by profile name.
* @param desc the name of the color space
* @return the requested color space or null if it wasn't found
*/
public PDFColorSpace getColorSpace(PDFName name) {
- PDFColorSpace cs = this.colorSpaces.get(name);
- return cs;
+ for (Map.Entry<LazyName, PDFColorSpace> x : colorSpaces.entrySet()) {
+ if (x.getKey().getName().equals(name)) {
+ return x.getValue();
+ }
+ }
+ return null;
}
/**
}
}
+ @Override
+ public void getChildren(Set<PDFObject> children) {
+ getChildren(children, false);
+ }
+
+ private void getChildren(Set<PDFObject> children, boolean isParent) {
+ super.getChildren(children);
+ for (PDFDictionary f : fonts.values()) {
+ children.add(f);
+ f.getChildren(children);
+ }
+ for (PDFResourceContext c : contexts) {
+ for (PDFXObject x : c.getXObjects()) {
+ children.add(x);
+ x.getChildren(children);
+ }
+ for (PDFPattern x : c.getPatterns()) {
+ children.add(x);
+ x.getChildren(children);
+ }
+ for (PDFShading x : c.getShadings()) {
+ children.add(x);
+ x.getChildren(children);
+ }
+ for (PDFGState x : c.getGStates()) {
+ children.add(x);
+ x.getChildren(children);
+ }
+ }
+ if (!isParent) {
+ for (PDFColorSpace x : colorSpaces.values()) {
+ children.add((PDFObject)x);
+ ((PDFObject)x).getChildren(children);
+ }
+ }
+ if (parent != null) {
+ parent.getChildren(children, true);
+ }
+ }
}
*/
public static final int PAGEMODE_FULLSCREEN = 3;
+ private final PDFDocument document;
+
+
private static final PDFName[] PAGEMODE_NAMES = new PDFName[] {
new PDFName("UseNone"),
new PDFName("UseOutlines"),
* @param objnum the object's number
* @param pages the PDFPages object
*/
- public PDFRoot(int objnum, PDFPages pages) {
- super();
- setObjectNumber(objnum);
+ public PDFRoot(PDFDocument document, PDFPages pages) {
+ this.document = document;
+ setObjectNumber(document);
put("Type", new PDFName("Catalog"));
setRootPages(pages);
setLanguage("x-unknown");
final int len = super.output(stream);
//Now that the data has been written, it can be discarded.
- this.data = null;
+// this.data = null;
return len;
}
import java.io.DataOutputStream;
import java.io.IOException;
+import org.apache.fop.pdf.PDFObjectNumber;
+
/**
* 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 PDFObjectNumber objectNumber;
- private final int objectStreamNumber;
+ private final PDFObjectNumber objectStreamNumber;
private final int index;
* 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) {
+ public CompressedObjectReference(PDFObjectNumber objectNumber, PDFObjectNumber 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.writeLong(objectStreamNumber.getNumber());
out.write(0);
out.write(index);
}
- public int getObjectNumber() {
+ public PDFObjectNumber getObjectNumber() {
return objectNumber;
}
- public int getObjectStreamNumber() {
+ public PDFObjectNumber getObjectStreamNumber() {
return objectStreamNumber;
}
import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFFilterList;
import org.apache.fop.pdf.PDFName;
+import org.apache.fop.pdf.PDFObjectNumber;
import org.apache.fop.pdf.PDFStream;
/**
objectReferences.add(offset == null ? null : new UncompressedObjectReference(offset));
}
for (CompressedObjectReference ref : compressedObjectReferences) {
- this.objectReferences.set(ref.getObjectNumber() - 1, ref);
+ while (ref.getObjectNumber().getNumber() > objectReferences.size()) {
+ objectReferences.add(null);
+ }
+ this.objectReferences.set(ref.getObjectNumber().getNumber() - 1, ref);
}
}
}
};
- helperStream.setObjectNumber(objectNumber);
+ helperStream.setObjectNumber(new PDFObjectNumber(objectNumber));
helperStream.setDocument(document);
ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
DataOutputStream data = new DataOutputStream(byteArray);
private final StringBuilder pdf = new StringBuilder(256);
+ private int last;
+ private int first;
+ private int size;
+
public CrossReferenceTable(TrailerDictionary trailerDictionary, long startxref,
- List<Long> location) {
+ List<Long> location, int first, int last, int size) {
super(trailerDictionary, startxref);
this.objectReferences = location;
+ this.first = first;
+ this.last = last;
+ this.size = size;
}
public void output(OutputStream stream) throws IOException {
}
private void outputXref() throws IOException {
- pdf.append("xref\n0 ");
- pdf.append(objectReferences.size() + 1);
- pdf.append("\n0000000000 65535 f \n");
- for (Long objectReference : objectReferences) {
+ if (first == 0) {
+ pdf.append("xref\n0 ");
+ pdf.append(last + 1);
+ pdf.append("\n0000000000 65535 f \n");
+ } else {
+ pdf.append("xref\n" + (first + 1) + " ");
+ pdf.append(last + "\n");
+ }
+ for (int i = first; i < first + last; i++) {
+ Long objectReference = objectReferences.get(i);
+ assert objectReference != null;
final String padding = "0000000000";
String s = String.valueOf(objectReference);
if (s.length() > 10) {
pdf.append("trailer\n");
stream.write(PDFDocument.encode(pdf.toString()));
PDFDictionary dictionary = trailerDictionary.getDictionary();
- dictionary.put("/Size", objectReferences.size() + 1);
+ dictionary.put("/Size", size + 1);
dictionary.output(stream);
}
return this;
}
- PDFDictionary getDictionary() {
+ public PDFDictionary getDictionary() {
return dictionary;
}
import org.apache.fop.pdf.PDFColorHandler;
import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFFilterList;
+import org.apache.fop.pdf.PDFLinearization;
import org.apache.fop.pdf.PDFNumber;
import org.apache.fop.pdf.PDFPaintingState;
import org.apache.fop.pdf.PDFReference;
* @throws IOException if an error occurs while flushing the PDF objects
*/
public void flushPDFDoc() throws IOException {
+ if (document.isLinearizationEnabled()) {
+ new PDFLinearization(document).outputPages(outputStream);
+ }
this.document.output(this.outputStream);
}
import org.apache.fop.pdf.PDFArray;
import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFPage;
+import org.apache.fop.pdf.PDFReference;
import org.apache.fop.pdf.PDFResources;
import org.apache.fop.render.extensions.prepress.PageBoundaries;
import org.apache.fop.render.extensions.prepress.PageScale;
/** {@inheritDoc} */
public void endDocument() throws IFException {
+ pdfDoc.getResources().addFonts(pdfDoc, fontInfo);
try {
- pdfDoc.getResources().addFonts(pdfDoc, fontInfo);
- pdfDoc.outputTrailer(this.outputStream);
+ if (pdfDoc.isLinearizationEnabled()) {
+ generator.flushPDFDoc();
+ } else {
+ pdfDoc.outputTrailer(this.outputStream);
+ }
this.pdfDoc = null;
pdfResources = null;
this.pdfDoc.addObject(annots);
}
this.pdfDoc.addObject(currentPage);
- this.generator.flushPDFDoc();
- this.generator = null;
+
+ if (!pdfDoc.isLinearizationEnabled()) {
+ this.generator.flushPDFDoc();
+ this.generator = null;
+ }
} catch (IOException ioe) {
throw new IFException("I/O error in endPage()", ioe);
}
static final class PageReference {
- private final String pageRef;
+ private final PDFReference pageRef;
private final Dimension pageDimension;
private PageReference(PDFPage page, Dimension dim) {
- // Avoid keeping references to PDFPage as memory usage is
- // considerably increased when handling thousands of pages.
- this.pageRef = page.makeReference().toString();
+ this.pageRef = page.makeReference();
this.pageDimension = new Dimension(dim);
}
- public String getPageRef() {
+ public PDFReference getPageRef() {
return this.pageRef;
}
import org.apache.fop.pdf.PDFGoTo;
import org.apache.fop.pdf.PDFLink;
import org.apache.fop.pdf.PDFOutline;
+import org.apache.fop.pdf.PDFReference;
import org.apache.fop.pdf.PDFStructElem;
import org.apache.fop.render.intermediate.IFDocumentNavigationHandler;
import org.apache.fop.render.intermediate.IFException;
parent = getPDFDoc().getOutlineRoot();
}
PDFAction action = getAction(bookmark.getAction());
- String actionRef = (action != null ? action.makeReference().toString() : null);
+ PDFReference actionRef = (action != null ? action.makeReference() : null);
PDFOutline pdfOutline = getPDFDoc().getFactory().makeOutline(parent,
bookmark.getTitle(), actionRef, bookmark.isShown());
Iterator iter = bookmark.getChildBookmarks().iterator();
p2d = new Point2D.Double(
action.getTargetLocation().x / 1000.0,
(pageRef.getPageDimension().height - action.getTargetLocation().y) / 1000.0);
- String pdfPageRef = pageRef.getPageRef();
+ PDFReference pdfPageRef = pageRef.getPageRef();
pdfGoTo.setPageReference(pdfPageRef);
pdfGoTo.setPosition(p2d);
final boolean textAsShapes = false;
PDFGraphics2D graphics = new PDFGraphics2D(textAsShapes,
pdfContext.getFontInfo(), generator.getDocument(),
- generator.getResourceContext(), pdfContext.getPage().referencePDF(),
+ generator.getResourceContext(), pdfContext.getPage().makeReference(),
"", 0.0f, null);
graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
PDFGraphics2D graphics = new PDFGraphics2D(true, pdfContext.getFontInfo(),
generator.getDocument(),
- generator.getResourceContext(), pdfContext.getPage().referencePDF(),
+ generator.getResourceContext(), pdfContext.getPage().makeReference(),
"", 0, new TransparencyIgnoredEventListener(pdfContext, imageSVG));
graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
prepareImageMCID(structElem);
}
drawImageUsingURI(uri, rect);
- flushPDFDoc();
+ if (!getDocumentHandler().getPDFDocument().isLinearizationEnabled()) {
+ flushPDFDoc();
+ }
}
}
prepareImageMCID(structElem);
}
drawImageUsingDocument(doc, rect);
- flushPDFDoc();
+ if (!getDocumentHandler().getPDFDocument().isLinearizationEnabled()) {
+ flushPDFDoc();
+ }
}
private void flushPDFDoc() throws IFException {
import static org.apache.fop.render.pdf.PDFEncryptionOption.USER_PASSWORD;
import static org.apache.fop.render.pdf.PDFRendererOption.DISABLE_SRGB_COLORSPACE;
import static org.apache.fop.render.pdf.PDFRendererOption.FILTER_LIST;
+import static org.apache.fop.render.pdf.PDFRendererOption.LINEARIZATION;
import static org.apache.fop.render.pdf.PDFRendererOption.MERGE_FONTS;
import static org.apache.fop.render.pdf.PDFRendererOption.OUTPUT_PROFILE;
import static org.apache.fop.render.pdf.PDFRendererOption.PDF_A_MODE;
parseAndPut(OUTPUT_PROFILE, cfg);
parseAndPut(DISABLE_SRGB_COLORSPACE, cfg);
parseAndPut(MERGE_FONTS, cfg);
+ parseAndPut(LINEARIZATION, cfg);
parseAndPut(VERSION, cfg);
} catch (ConfigurationException e) {
return Boolean.valueOf(value);
}
},
+ LINEARIZATION("linearization", false) {
+ @Override
+ Boolean deserialize(String value) {
+ return Boolean.valueOf(value);
+ }
+ },
/** Rendering Options key for the ICC profile for the output intent. */
OUTPUT_PROFILE("output-profile") {
@Override
import static org.apache.fop.render.pdf.PDFRendererOption.DISABLE_SRGB_COLORSPACE;
import static org.apache.fop.render.pdf.PDFRendererOption.FILTER_LIST;
+import static org.apache.fop.render.pdf.PDFRendererOption.LINEARIZATION;
import static org.apache.fop.render.pdf.PDFRendererOption.MERGE_FONTS;
import static org.apache.fop.render.pdf.PDFRendererOption.OUTPUT_PROFILE;
import static org.apache.fop.render.pdf.PDFRendererOption.PDF_A_MODE;
public Boolean getMergeFontsEnabled() {
return (Boolean)properties.get(MERGE_FONTS);
}
+
+ public Boolean getLinearizationEnabled() {
+ return (Boolean)properties.get(LINEARIZATION);
+ }
}
this.pdfDoc.enableAccessibility(userAgent.isAccessibilityEnabled());
pdfDoc.setMergeFontsEnabled(rendererConfig.getMergeFontsEnabled());
+ pdfDoc.setLinearizationEnabled(rendererConfig.getLinearizationEnabled());
return this.pdfDoc;
}
width, height);
resourceContext = page;
pdfContext.setCurrentPage(page);
- pageRef = page.referencePDF();
+ pageRef = page.makeReference();
currentStream.write("q\n");
AffineTransform at = new AffineTransform(1.0, 0.0, 0.0, -1.0,
import org.apache.fop.pdf.PDFNumber;
import org.apache.fop.pdf.PDFPaintingState;
import org.apache.fop.pdf.PDFPattern;
+import org.apache.fop.pdf.PDFReference;
import org.apache.fop.pdf.PDFResourceContext;
import org.apache.fop.pdf.PDFResources;
import org.apache.fop.pdf.PDFShading;
/**
* The PDF reference of the current page.
*/
- protected String pageRef;
+ protected PDFReference pageRef;
/**
* The PDF painting state
* @param size the current font size
*/
public PDFGraphics2D(boolean textAsShapes, FontInfo fi, PDFDocument doc,
- PDFResourceContext page, String pref, String font, float size,
+ PDFResourceContext page, PDFReference pref, String font, float size,
TransparencyIgnoredEventListener listener) {
this(textAsShapes);
pdfDoc = doc;
* Gets the PDF reference of the current page.
* @return the PDF reference of the current page
*/
- public String getPageReference() {
+ public PDFReference getPageReference() {
return this.pageRef;
}
if (linkType != PDFLink.EXTERNAL) {
String pdfdest = "/FitR " + dest;
resourceContext.addAnnotation(
- pdfDoc.getFactory().makeLink(rect, getPageReference(), pdfdest));
+ pdfDoc.getFactory().makeLink(rect, getPageReference().toString(), pdfdest));
} else {
resourceContext.addAnnotation(
pdfDoc.getFactory().makeLink(rect, dest, linkType, 0));
}
private void flushPDFDocument() {
- if (outputStream != null) {
+ if (outputStream != null && !pdfDoc.isLinearizationEnabled()) {
try {
this.pdfDoc.output(outputStream);
} catch (IOException ioe) {
}
}
- String maskRef = null;
+ PDFReference maskRef = null;
if (mask != null) {
BitmapImage fopimg = new BitmapImage(
"TempImageMask:" + pctx.toString(), devW, devH, mask, null);
fopimg.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));
PDFImageXObject xobj = pdfDoc.addImage(resourceContext, fopimg);
- maskRef = xobj.referencePDF();
+ maskRef = xobj.makeReference();
flushPDFDocument();
}
encodedBytes[i++] = (byte) (in & 0xff);
}
}
- private String startStream = "<< /Length 5 0 R /Filter /FlateDecode >>\n"
+ private String startStream = "<< /Length 1 0 R /Filter /FlateDecode >>\n"
+ "stream\n";
private String endStream = "endstream";
int objectStreamNumber2 = assertSameObjectStream(expectedCapacity, expectedCapacity * 2);
int objectStreamNumber3 = assertSameObjectStream(expectedCapacity * 2, numCompressedObjects);
assertDifferent(objectStreamNumber1, objectStreamNumber2, objectStreamNumber3);
- assertEquals(objectStreamNumber3, pdfDocument.previous.getObjectNumber());
+ assertEquals(objectStreamNumber3, pdfDocument.previous.getObjectNumber().getNumber());
}
private void createCompressObjectReferences(int numObjects) {
private CompressedObject createCompressedObject(final int objectNumber) {
return new CompressedObject() {
- public int getObjectNumber() {
- return objectNumber;
+ public PDFObjectNumber getObjectNumber() {
+ return new PDFObjectNumber(objectNumber);
}
public int output(OutputStream outputStream) throws IOException {
}
private int getObjectStreamNumber(int index) {
- return compressedObjectReferences.get(index).getObjectStreamNumber();
+ return compressedObjectReferences.get(index).getObjectStreamNumber().getNumber();
}
private void assertDifferent(int objectStreamNumber1, int objectStreamNumber2,
private String getExpectedOutput() {
int numObs = compressedObjects.size();
- int objectStreamNumber = objectStream.getObjectNumber();
+ int objectStreamNumber = objectStream.getObjectNumber().getNumber();
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");
+ objectStreamNumber++;
}
expected.append(" /Type /ObjStm\n")
.append(" /N ").append(numObs).append("\n")
public final void testMake() {
PDFEncryption testEncryptionObj = createEncryptionObject(new PDFEncryptionParams());
assertTrue(testEncryptionObj instanceof PDFEncryptionJCE);
- assertEquals(1, ((PDFEncryptionJCE) testEncryptionObj).getObjectNumber());
+ assertEquals(1, ((PDFEncryptionJCE) testEncryptionObj).getObjectNumber().getNumber());
}
@Test
};
}
};
- return (PDFEncryptionJCE) PDFEncryptionJCE.make(1, params, doc);
+ return (PDFEncryptionJCE) PDFEncryptionJCE.make(new PDFObjectNumber(1), params, doc);
}
private void runEncryptionTests() throws IOException {
--- /dev/null
+/*
+ * 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.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.geom.Rectangle2D;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.stream.StreamResult;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.render.intermediate.IFContext;
+import org.apache.fop.render.pdf.PDFContentGenerator;
+import org.apache.fop.render.pdf.PDFDocumentHandler;
+import org.apache.fop.render.pdf.PDFPainter;
+
+public class PDFLinearizationTestCase {
+ private int objectLeast;
+ private int[] objects;
+
+ @Test
+ public void testPDF() throws IOException {
+ PDFDocument doc = new PDFDocument("");
+ doc.setLinearizationEnabled(true);
+ PDFResources resources = new PDFResources(doc);
+ PDFResourceContext context = new PDFResourceContext(resources);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ PDFContentGenerator gen = null;
+ for (int i = 0; i < 2; i++) {
+ gen = new PDFContentGenerator(doc, out, context);
+ Rectangle2D.Float f = new Rectangle2D.Float();
+ PDFPage page = new PDFPage(resources, i, f, f, f, f);
+ doc.registerObject(page);
+ doc.registerObject(gen.getStream());
+ page.setContents(gen.getStream());
+ }
+ gen.flushPDFDoc();
+ byte[] data = out.toByteArray();
+ checkPDF(data);
+ }
+
+ @Test
+ public void testImage() throws Exception {
+ String fopxconf = "<fop version=\"1.0\"><renderers>"
+ + "<renderer mime=\"application/pdf\">"
+ + "<linearization>true</linearization>"
+ + "</renderer></renderers></fop>";
+ FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI(),
+ new ByteArrayInputStream(fopxconf.getBytes()));
+ FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
+ IFContext ifContext = new IFContext(foUserAgent);
+ PDFDocumentHandler documentHandler = new PDFDocumentHandler(ifContext);
+ documentHandler.getConfigurator().configure(documentHandler);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ documentHandler.setFontInfo(new FontInfo());
+ documentHandler.setResult(new StreamResult(out));
+ documentHandler.startDocument();
+ documentHandler.startPage(0, "", "", new Dimension());
+ PDFPainter pdfPainter = new PDFPainter(documentHandler, null);
+ pdfPainter.drawImage("test/resources/fop/svg/logo.jpg", new Rectangle());
+ documentHandler.endPage();
+ Assert.assertFalse(out.toString().contains("/Subtype /Image"));
+ documentHandler.endDocument();
+ Assert.assertTrue(out.toString().contains("/Subtype /Image"));
+ }
+
+ private void checkPDF(byte[] data) throws IOException {
+ checkHintTable(data);
+ InputStream is = new ByteArrayInputStream(data);
+ Map<String, StringBuilder> objs = readObjs(is);
+
+ List<String> keys = new ArrayList<String>(objs.keySet());
+ int start = keys.indexOf("1 0 obj");
+ Assert.assertTrue(start > 1);
+ int j = 1;
+ for (int i = start; i < keys.size(); i++) {
+ Assert.assertEquals(keys.get(i), j + " 0 obj");
+ j++;
+ }
+ for (int i = 0; i < start; i++) {
+ Assert.assertEquals(keys.get(i), j + " 0 obj");
+ j++;
+ }
+
+ checkFirstObj(data);
+ checkTrailer(data);
+
+ String firstObj = objs.values().iterator().next().toString().replace("\n", "");
+ Assert.assertTrue(firstObj.startsWith("<< /Linearized 1 /L " + data.length));
+ Assert.assertTrue(firstObj.endsWith("startxref0%%EOF"));
+ int pageObjNumber = getValue("/O", firstObj);
+ Assert.assertTrue(objs.get(pageObjNumber + " 0 obj").toString().contains("/Type /Page"));
+ Assert.assertTrue(objs.get("5 0 obj").toString().contains("/Type /Pages"));
+
+ int total = 0;
+ for (int i : objects) {
+ total += i;
+ }
+ Assert.assertEquals(total, objs.size() - 6);
+ }
+
+ private void checkFirstObj(byte[] data) throws IOException {
+ int firstObjPos = getValue("/E", getFirstObj(data));
+ InputStream is = new ByteArrayInputStream(data);
+ Assert.assertEquals(is.skip(firstObjPos), firstObjPos);
+ byte[] obj = new byte[10];
+ Assert.assertEquals(is.read(obj), obj.length);
+ Assert.assertTrue(new String(obj).startsWith("1 0 obj"));
+ }
+
+ private void checkTrailer(byte[] data) throws IOException {
+ int trailerPos = getValue("/T", getFirstObj(data));
+ InputStream is = new ByteArrayInputStream(data);
+ Assert.assertEquals(is.skip(trailerPos), trailerPos);
+ byte[] obj = new byte[20];
+ Assert.assertEquals(is.read(obj), obj.length);
+ Assert.assertTrue(new String(obj).startsWith("0000000000 65535 f"));
+ }
+
+ private int getValue(String name, String firstObj) throws IOException {
+ String[] split = firstObj.split(" ");
+ for (int i = 0; i < split.length; i++) {
+ if (split[i].equals(name)) {
+ return Integer.valueOf(split[i + 1].replace(">>", ""));
+ }
+ }
+ throw new IOException(name + " not found " + firstObj);
+ }
+
+ private int[] getArrayValue(String name, String firstObj) throws IOException {
+ String[] split = firstObj.split(" ");
+ for (int i = 0; i < split.length; i++) {
+ if (split[i].equals(name)) {
+ int[] v = new int[2];
+ v[0] = Integer.valueOf(split[i + 1].replace("[", ""));
+ v[1] = Integer.valueOf(split[i + 2].replace("]", ""));
+ return v;
+ }
+ }
+ throw new IOException(name + " not found " + firstObj);
+ }
+
+ private String getFirstObj(byte[] out) throws IOException {
+ InputStream data = new ByteArrayInputStream(out);
+ Map<String, StringBuilder> objs = readObjs(data);
+ return objs.values().iterator().next().toString().replace("\n", "");
+ }
+
+ private void checkHintTable(byte[] out) throws IOException {
+ String firstObj = getFirstObj(out);
+ int hintPos = getArrayValue("/H", firstObj)[0];
+ int hintLength = getArrayValue("/H", firstObj)[1];
+
+ InputStream data = new ByteArrayInputStream(out);
+ Assert.assertEquals(data.skip(hintPos), hintPos);
+
+ byte[] hintTable = new byte[hintLength];
+ Assert.assertEquals(data.read(hintTable), hintLength);
+ String hintTableStr = new String(hintTable);
+
+ Assert.assertTrue(hintTableStr.contains("/S "));
+ Assert.assertTrue(hintTableStr.contains("/C "));
+ Assert.assertTrue(hintTableStr.contains("/E "));
+ Assert.assertTrue(hintTableStr.contains("/L "));
+ Assert.assertTrue(hintTableStr.contains("/V "));
+ Assert.assertTrue(hintTableStr.contains("/O "));
+ Assert.assertTrue(hintTableStr.contains("/I "));
+ Assert.assertTrue(hintTableStr.contains("/Length "));
+ Assert.assertTrue(hintTableStr.contains("stream"));
+ Assert.assertTrue(hintTableStr.contains("endstream"));
+ Assert.assertTrue(hintTableStr.endsWith("endobj\n"));
+
+ data = new ByteArrayInputStream(hintTable);
+ readStart(data);
+ int pages = getValue("/N", firstObj);
+ readObjectsTable(data, pages);
+ readSharedObjectsTable(data);
+ Assert.assertEquals(objectLeast, 1);
+ }
+
+ private void readObjectsTable(InputStream data, int pages)
+ throws IOException {
+ objectLeast = read32(data);
+ read32(data);
+ int bitsDiffObjects = read16(data);
+ read32(data);
+ int bitsDiffPageLength = read16(data);
+ read32(data);
+ read16(data);
+ read32(data);
+ read16(data);
+ read16(data);
+ read16(data);
+ read16(data);
+ read16(data);
+
+ objects = new int[pages];
+ for (int i = 0; i < pages; i++) {
+ objects[i] = objectLeast + readBits(bitsDiffObjects, data);
+ }
+ for (int i = 0; i < pages; i++) {
+ readBits(bitsDiffPageLength, data);
+ }
+ for (int i = 0; i < pages; i++) {
+ readBits(32, data);
+ }
+ }
+
+ private void readSharedObjectsTable(InputStream str) throws IOException {
+ readBits(32, str);
+ readBits(32, str);
+ readBits(32, str);
+ int sharedGroups = readBits(32, str);
+ readBits(16, str);
+ readBits(32, str);
+ int bitsDiffGroupLength = readBits(16, str);
+ for (int i = 0; i < sharedGroups; i++) {
+ readBits(bitsDiffGroupLength, str);
+ }
+ }
+
+ private int readBits(int bits, InputStream data) throws IOException {
+ if (bits == 32) {
+ return read32(data);
+ }
+ if (bits == 16) {
+ return read16(data);
+ }
+ throw new IOException("Wrong bits");
+ }
+
+ private int read32(InputStream data) throws IOException {
+ int ch1 = data.read();
+ int ch2 = data.read();
+ int ch3 = data.read();
+ int ch4 = data.read();
+ return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4));
+ }
+
+ private int read16(InputStream data) throws IOException {
+ int ch1 = data.read();
+ int ch2 = data.read();
+ return (ch1 << 8) + (ch2);
+ }
+
+ private void readStart(InputStream inputStream) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ while (inputStream.available() > 0) {
+ int data = inputStream.read();
+ if (data == '\n') {
+ if (sb.toString().equals("stream")) {
+ return;
+ }
+ sb.setLength(0);
+ } else {
+ sb.append((char)data);
+ }
+ }
+ }
+
+ private Map<String, StringBuilder> readObjs(InputStream inputStream) throws IOException {
+ Map<String, StringBuilder> objs = new LinkedHashMap<String, StringBuilder>();
+ StringBuilder sb = new StringBuilder();
+ String key = null;
+ while (inputStream.available() > 0) {
+ int data = inputStream.read();
+ if (data == '\n') {
+ if (sb.toString().endsWith(" 0 obj")) {
+ key = sb.toString().trim();
+ objs.put(key, new StringBuilder());
+ } else if (key != null) {
+ objs.get(key).append(sb).append("\n");
+ }
+ sb.setLength(0);
+ } else {
+ sb.append((char)data);
+ }
+ }
+ return objs;
+ }
+}
@Test
public void testSetObjectNumber() {
pdfObjectUnderTest.setObjectNumber(1);
- assertEquals(1, pdfObjectUnderTest.getObjectNumber());
+ assertEquals(1, pdfObjectUnderTest.getObjectNumber().getNumber());
pdfObjectUnderTest.setObjectNumber(5);
- assertEquals(5, pdfObjectUnderTest.getObjectNumber());
+ assertEquals(5, pdfObjectUnderTest.getObjectNumber().getNumber());
}
/**
PDFDictionary dict = new PDFDictionary();
dict.setObjectNumber(7);
PDFReference ref = dict.makeReference();
- assertEquals(ref.getObjectNumber(), 7);
+ assertEquals(ref.getObjectNumber().getNumber(), 7);
assertEquals(ref.getGeneration(), 0);
assertEquals(ref.toString(), "7 0 R");
ref = new PDFReference("8 0 R");
- assertEquals(ref.getObjectNumber(), 8);
+ assertEquals(ref.getObjectNumber().getNumber(), 8);
assertEquals(ref.getGeneration(), 0);
assertEquals(ref.toString(), "8 0 R");
}
import static org.junit.Assert.assertArrayEquals;
+import org.apache.fop.pdf.PDFObjectNumber;
+
public class CompressedObjectReferenceTestCase extends ObjectReferenceTest {
@Test
private void runTest(List<Integer> expectedObjectStreamBytes, int index) throws IOException {
int objectStreamNumber = (int) computeNumberFromBytes(expectedObjectStreamBytes);
- sut = new CompressedObjectReference(0, objectStreamNumber, index);
+ sut = new CompressedObjectReference(new PDFObjectNumber(0), new PDFObjectNumber(objectStreamNumber), index);
byte[] expected = createExpectedOutput((byte) 2, expectedObjectStreamBytes, index);
byte[] actual = getActualOutput();
assertArrayEquals(expected, actual);
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));
+ PDFRoot root = new PDFRoot(pdfDocument, new PDFPages(pdfDocument));
PDFInfo info = new PDFInfo();
info.setObjectNumber(2);
byte[] fileID =
import org.junit.Test;
+import org.apache.fop.pdf.PDFObjectNumber;
+
public class CrossReferenceStreamTestCase extends CrossReferenceObjectTest {
private List<Long> uncompressedObjectOffsets;
@Test
public void testWithObjectStreams1() throws IOException {
List<CompressedObjectReference> compressedObjectReferences =
- Arrays.asList(new CompressedObjectReference(2, 1, 0));
+ Arrays.asList(new CompressedObjectReference(new PDFObjectNumber(2), new PDFObjectNumber(1), 0));
test(Arrays.asList(0L, null), compressedObjectReferences);
}
for (int index = 0; index < numCompressedObjects; index++) {
indirectObjectOffsets.add(null);
int obNum = numIndirectObjects + index + 1;
- compressedObjectReferences.add(new CompressedObjectReference(obNum,
- numIndirectObjects, index));
+ compressedObjectReferences.add(new CompressedObjectReference(new PDFObjectNumber(obNum),
+ new PDFObjectNumber(numIndirectObjects), index));
}
test(indirectObjectOffsets, compressedObjectReferences);
}
objectReferences.add(offset == null ? null : new UncompressedObjectReference(offset));
}
for (CompressedObjectReference ref : compressedObjectReferences) {
- objectReferences.set(ref.getObjectNumber() - 1, ref);
+ objectReferences.set(ref.getObjectNumber().getNumber() - 1, ref);
}
int maxObjectNumber = objectReferences.size() + 1;
ByteArrayOutputStream stream = new ByteArrayOutputStream();
@Override
protected CrossReferenceObject createCrossReferenceObject() {
- return new CrossReferenceTable(trailerDictionary, STARTXREF, offsets);
+ return new CrossReferenceTable(trailerDictionary, STARTXREF, offsets, 0, offsets.size(), offsets.size());
}
@Override