From b222b4dcad05edf7e6e1b6f2028f68728fede500 Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Mon, 7 May 2007 14:30:23 +0000 Subject: [PATCH] Removed newly introduced dependency from the PDF library to the area tree. Removed PDFDocument.setHasDestinations() (PDFDocument knows that already). Started to add some generic PDF data structures (array, dictionary). git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@535883 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/area/BookmarkData.java | 3 +- .../org/apache/fop/area/DestinationData.java | 21 --- .../org/apache/fop/fo/pagination/Root.java | 9 +- src/java/org/apache/fop/pdf/PDFArray.java | 108 ++++++++++++++-- .../org/apache/fop/pdf/PDFDestination.java | 97 ++++++-------- src/java/org/apache/fop/pdf/PDFDests.java | 52 ++------ .../org/apache/fop/pdf/PDFDictionary.java | 100 +++++++++++++++ src/java/org/apache/fop/pdf/PDFDocument.java | 109 +++++++--------- src/java/org/apache/fop/pdf/PDFFactory.java | 106 +++++++-------- src/java/org/apache/fop/pdf/PDFLimits.java | 93 -------------- .../org/apache/fop/pdf/PDFNameTreeNode.java | 121 ++++++++++++++++++ src/java/org/apache/fop/pdf/PDFNames.java | 51 ++++++++ src/java/org/apache/fop/pdf/PDFObject.java | 27 +++- src/java/org/apache/fop/pdf/PDFReference.java | 67 ++++++++++ src/java/org/apache/fop/pdf/PDFRoot.java | 36 +++--- src/java/org/apache/fop/pdf/PDFWritable.java | 36 ++++++ .../apache/fop/render/AbstractRenderer.java | 2 +- src/java/org/apache/fop/render/Renderer.java | 4 +- .../apache/fop/render/pdf/PDFRenderer.java | 18 ++- 19 files changed, 686 insertions(+), 374 deletions(-) create mode 100644 src/java/org/apache/fop/pdf/PDFDictionary.java delete mode 100644 src/java/org/apache/fop/pdf/PDFLimits.java create mode 100644 src/java/org/apache/fop/pdf/PDFNameTreeNode.java create mode 100644 src/java/org/apache/fop/pdf/PDFNames.java create mode 100644 src/java/org/apache/fop/pdf/PDFReference.java create mode 100644 src/java/org/apache/fop/pdf/PDFWritable.java diff --git a/src/java/org/apache/fop/area/BookmarkData.java b/src/java/org/apache/fop/area/BookmarkData.java index d87b38592..2ad8273a9 100644 --- a/src/java/org/apache/fop/area/BookmarkData.java +++ b/src/java/org/apache/fop/area/BookmarkData.java @@ -19,7 +19,6 @@ package org.apache.fop.area; -import java.util.ArrayList; import java.util.List; import java.util.HashMap; @@ -32,7 +31,7 @@ import org.apache.fop.fo.pagination.bookmarks.Bookmark; * child bookmark-items under it. */ public class BookmarkData extends AbstractOffDocumentItem implements Resolvable { - private ArrayList subData = new ArrayList(); + private List subData = new java.util.ArrayList(); // bookmark-title for this fo:bookmark private String bookmarkTitle = null; diff --git a/src/java/org/apache/fop/area/DestinationData.java b/src/java/org/apache/fop/area/DestinationData.java index 7e59210e0..6b873ef84 100644 --- a/src/java/org/apache/fop/area/DestinationData.java +++ b/src/java/org/apache/fop/area/DestinationData.java @@ -28,9 +28,6 @@ import org.apache.fop.area.PageViewport; */ public class DestinationData extends AbstractOffDocumentItem implements Resolvable { - // PDFReference (object reference) for this destination - private String goToReference; - // ID Reference for this bookmark private String idRef; @@ -78,24 +75,6 @@ public class DestinationData extends AbstractOffDocumentItem implements Resolvab return pageRef; } - /** - * Set the GoToReference for this destination - * - * @param goToReference the GoToReference to associate with this destination - */ - public void setGoToReference(String goToReference) { - this.goToReference = goToReference; - } - - /** - * Get the GoToReference for this destination - * - * @return the GoToReference associated with this destination - */ - public String getGoToReference() { - return goToReference; - } - /** * Check if this resolvable object has been resolved. * For now, just return true. diff --git a/src/java/org/apache/fop/fo/pagination/Root.java b/src/java/org/apache/fop/fo/pagination/Root.java index 23f9cd391..929c721ec 100644 --- a/src/java/org/apache/fop/fo/pagination/Root.java +++ b/src/java/org/apache/fop/fo/pagination/Root.java @@ -21,7 +21,6 @@ package org.apache.fop.fo.pagination; // java import java.util.List; -import java.util.ArrayList; import org.xml.sax.Locator; @@ -45,7 +44,7 @@ public class Root extends FObj { private LayoutMasterSet layoutMasterSet; private Declarations declarations; private BookmarkTree bookmarkTree = null; - private ArrayList destinationList; + private List destinationList; private List pageSequences; // temporary until above list populated @@ -67,7 +66,7 @@ public class Root extends FObj { */ public Root(FONode parent) { super(parent); - pageSequences = new ArrayList(); + pageSequences = new java.util.ArrayList(); if (parent != null) { //throw new FOPException("root must be root element"); } @@ -260,7 +259,7 @@ public class Root extends FObj { */ public void addDestination(Destination destination) { if (destinationList == null) { - destinationList = new ArrayList(); + destinationList = new java.util.ArrayList(); } destinationList.add(destination); } @@ -269,7 +268,7 @@ public class Root extends FObj { * Public accessor for the list of Destination objects for this FO * @return the Destination object */ - public ArrayList getDestinationList() { + public List getDestinationList() { return destinationList; } diff --git a/src/java/org/apache/fop/pdf/PDFArray.java b/src/java/org/apache/fop/pdf/PDFArray.java index 2dcbd5224..0e7a2b986 100644 --- a/src/java/org/apache/fop/pdf/PDFArray.java +++ b/src/java/org/apache/fop/pdf/PDFArray.java @@ -19,40 +19,124 @@ package org.apache.fop.pdf; +import java.util.Collection; +import java.util.List; + /** - * class representing an array object + * Class representing an array object. */ public class PDFArray extends PDFObject { /** - * Array of calues for this pdf object. + * List holding the values of this array */ - protected int[] values; + protected List values = new java.util.ArrayList(); + + /** + * Create a new, empty array object + */ + public PDFArray() { + /* generic creation of PDF object */ + super(); + } /** - * create the array object + * Create the array object * * @param values the actual array wrapped by this object */ public PDFArray(int[] values) { - /* generic creation of PDF object */ super(); - /* set fields using paramaters */ - this.values = values; + for (int i = 0, c = values.length; i < c; i++) { + this.values.add(new Integer(values[i])); + } } + /** + * Create the array object + * + * @param values the actual values wrapped by this object + */ + public PDFArray(Collection values) { + /* generic creation of PDF object */ + super(); + + this.values.addAll(values); + } + + /** + * Create the array object + * + * @param values the actual array wrapped by this object + */ + public PDFArray(Object[] values) { + /* generic creation of PDF object */ + super(); + + for (int i = 0, c = values.length; i < c; i++) { + this.values.add(values[i]); + } + } + + /** + * Returns the length of the array + * @return the length of the array + */ + public int length() { + return this.values.size(); + } + + /** + * Sets an entry at a given location. + * @param index the index of the value to set + * @param obj the new value + */ + public void set(int index, Object obj) { + this.values.set(index, obj); + } + + /** + * Gets an entry at a given location. + * @param index the index of the value to set + * @return the requested value + */ + public Object get(int index) { + return this.values.get(index); + } + + /** + * Adds a new value to the array. + * @param obj the value + */ + public void add(Object obj) { + this.values.add(obj); + } + /** * @see org.apache.fop.pdf.PDFObject#toPDFString() */ public String toPDFString() { StringBuffer p = new StringBuffer(64); - p.append(getObjectID() + "["); - for (int i = 0; i < values.length; i++) { - p.append(" "); - p.append(values[i]); + if (hasObjectNumber()) { + p.append(getObjectID()); + } + p.append("["); + for (int i = 0; i < values.size(); i++) { + if (i > 0) { + p.append(" "); + } + Object obj = this.values.get(i); + if (obj instanceof PDFWritable) { + p.append(((PDFWritable)obj).toInlinePDFString()); + } else { + p.append("(").append(obj).append(")"); + } + } + p.append("]"); + if (hasObjectNumber()) { + p.append("\nendobj\n"); } - p.append("]\nendobj\n"); return p.toString(); } diff --git a/src/java/org/apache/fop/pdf/PDFDestination.java b/src/java/org/apache/fop/pdf/PDFDestination.java index b7e0989c1..2b7ec0a1f 100644 --- a/src/java/org/apache/fop/pdf/PDFDestination.java +++ b/src/java/org/apache/fop/pdf/PDFDestination.java @@ -19,102 +19,77 @@ package org.apache.fop.pdf; -import org.apache.fop.area.DestinationData; -import org.apache.fop.area.PageViewport; - /** * class representing a named destination */ public class PDFDestination extends PDFObject { - /** - * PDFReference (object reference) for this destination - */ - private String goToReference; - /** * ID Reference for this destination */ private String idRef; /** - * PageViewport to which the idRef item refers - */ - private PageViewport pageViewport = null; - - /** - * create a named destination + * PDFReference (object reference) for this destination */ - public PDFDestination(DestinationData destinationData) { - /* generic creation of PDF object */ - super(); - this.goToReference = destinationData.getGoToReference(); - this.idRef = destinationData.getIDRef(); - this.pageViewport = destinationData.getPageViewport(); - } + private Object goToReference; /** - * create a named destination - * - * @param idRef The ID reference for this destination - will be used as the name - * @param goToRef A PDF reference to a /GoTo pointing to the target area - * @param pv The PageViewport of the target area (merely informational) + * Create a named destination + * @param idRef ID Reference for this destination (the name of the destination) + * @param goToRef Object reference to the GoTo Action */ - public PDFDestination(String idRef, String goToRef, PageViewport pv) { - super(); - this.idRef = idRef; + public PDFDestination(String idRef, Object goToRef) { this.goToReference = goToRef; - this.pageViewport = pv; + this.idRef = idRef; } /** - * @see org.apache.fop.pdf.PDFObject#toPDFString() + * Creates the key/value pair for this destination entry for the name tree. + * @return the formatted key/value pair */ - public String toPDFString() { - String s = getObjectID() - + "<<\n" - + "/Limits [(" + idRef + ") (" + idRef + ")]\n" - + "/Names [(" + idRef + ") " + goToReference + "]" - + "\n>>\nendobj\n"; - return s; + public String toKeyValuePair() { + StringBuffer sb = new StringBuffer(); + sb.append("(").append(getIDRef()).append(") "); + if (goToReference instanceof PDFWritable) { + sb.append(((PDFWritable)goToReference).toInlinePDFString()); + } else { + sb.append(goToReference); + } + return sb.toString(); + } + + /** @see org.apache.fop.pdf.PDFObject#toPDFString() */ + protected String toPDFString() { + return toKeyValuePair(); } - - /* - * example: - * - * 249 0 obj - * << - * /Limits [(drivervariables) (drivervariables)] - * /Names [(drivervariables) 73 0 R] - * >> - * endobj - */ /** * Sets the GoToReference in the associated DestinationData object. * * @param goToReference the reference to set in the associated DestinationData object. + * @deprecated use setGoToReference(Object) instead */ public void setGoToReference(String goToReference) { this.goToReference = goToReference; } /** - * Returns the GoToReference from the associated DestinationData object. + * Sets the GoToReference in the associated DestinationData object. * - * @return the GoToReference from the associated DestinationData object. + * @param goToReference the reference to set in the associated DestinationData object. */ - public String getGoToReference() { - return this.goToReference; + public void setGoToReference(Object goToReference) { + this.goToReference = goToReference; } /** - * Get the PageViewport object that this destination refers to + * Returns the GoToReference from the associated DestinationData object. * - * @return the PageViewport that this destination points to + * @return the GoToReference from the associated DestinationData object. */ - public PageViewport getPageViewport() { - return this.pageViewport; + public Object getGoToReference() { + return this.goToReference; } /** @@ -148,5 +123,13 @@ public class PDFDestination extends PDFObject { return false; } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return getIDRef().hashCode(); + } + } diff --git a/src/java/org/apache/fop/pdf/PDFDests.java b/src/java/org/apache/fop/pdf/PDFDests.java index 96172c8e2..463ef8d7e 100644 --- a/src/java/org/apache/fop/pdf/PDFDests.java +++ b/src/java/org/apache/fop/pdf/PDFDests.java @@ -19,61 +19,29 @@ package org.apache.fop.pdf; -import org.apache.fop.area.DestinationData; +import java.util.List; /** - * class representing an /Dests object (part of a name dictionary) + * class representing an /Dests dictionary object */ -public class PDFDests extends PDFObject { - - private String limitsRef; +public class PDFDests extends PDFNameTreeNode { /** - * create a named destination + * Create a named destination */ - public PDFDests(String limitsRef) { + public PDFDests() { /* generic creation of PDF object */ super(); - this.limitsRef = limitsRef; } /** - * @see org.apache.fop.pdf.PDFObject#toPDFString() + * Create a named destination + * @param destinationList a list of destinations */ - public String toPDFString() { - String s = getObjectID() - + "<<\n" - + "/Dests " + limitsRef - + "\n>>\nendobj\n"; - return s; + public PDFDests(List destinationList) { + this(); + setNames(new PDFArray(destinationList)); } - /* - * example: - * - * 262 0 obj - * << - * /Dests 260 0 R - * >> - * endobj - */ - - /** - * Check if this equals another object. - * - * @param obj the object to compare - * @return true if this equals other object - */ - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (obj == null || !(obj instanceof PDFDests)) { - return false; - } - - return true; - } } diff --git a/src/java/org/apache/fop/pdf/PDFDictionary.java b/src/java/org/apache/fop/pdf/PDFDictionary.java new file mode 100644 index 000000000..aa15792b2 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFDictionary.java @@ -0,0 +1,100 @@ +/* + * 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.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Class representing a PDF dictionary object + */ +public class PDFDictionary extends PDFObject { + + /** + * the entry map + */ + protected Map entries = new java.util.HashMap(); + + /** + * maintains the order of the entries added to the entry map. Whenever you modify + * "entries", always make sure you adjust this list accordingly. + */ + protected List order = new java.util.ArrayList(); + + /** + * Create the dictionary object + */ + public PDFDictionary() { + /* generic creation of PDF object */ + super(); + } + + /** + * Puts a new name/value pair. + * @param name the name + * @param value the value + */ + public void put(String name, Object value) { + if (!entries.containsKey(name)) { + this.order.add(name); + } + this.entries.put(name, value); + } + + /** + * Returns the value given a name. + * @param name the name of the value + * @return the value or null, if there's no value with the given name. + */ + public Object get(String name) { + return this.entries.get(name); + } + + /** + * @see org.apache.fop.pdf.PDFObject#toPDFString() + */ + public String toPDFString() { + StringBuffer p = new StringBuffer(64); + if (hasObjectNumber()) { + p.append(getObjectID()); + } + p.append("<<"); + Iterator iter = this.order.iterator(); + while (iter.hasNext()) { + String key = (String)iter.next(); + p.append("\n /"); + p.append(key); + p.append(" "); + Object obj = this.entries.get(key); + if (obj instanceof PDFWritable) { + p.append(((PDFWritable)obj).toInlinePDFString()); + } else { + p.append("(").append(obj).append(")"); + } + } + p.append("\n>>\n"); + if (hasObjectNumber()) { + p.append("endobj\n"); + } + return p.toString(); + } + +} diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java index 00d6c8e08..748c6b3a8 100644 --- a/src/java/org/apache/fop/pdf/PDFDocument.java +++ b/src/java/org/apache/fop/pdf/PDFDocument.java @@ -5,9 +5,9 @@ * 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. @@ -19,7 +19,7 @@ package org.apache.fop.pdf; -/// Java +// Java import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -28,18 +28,15 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Iterator; -import java.util.ArrayList; -import java.util.Collections; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.fop.pdf.DestinationComparator; - /* image support modified from work of BoBoGi */ /* font support based on work by Takayuki Takeuchi */ @@ -67,13 +64,13 @@ import org.apache.fop.pdf.DestinationComparator; public class PDFDocument { private static final Integer LOCATION_PLACEHOLDER = new Integer(0); - + /** Integer constant to represent PDF 1.3 */ public static final int PDF_VERSION_1_3 = 3; /** Integer constant to represent PDF 1.4 */ public static final int PDF_VERSION_1_4 = 4; - + /** * the encoding to use when converting strings to PDF commandos. */ @@ -89,7 +86,7 @@ public class PDFDocument { /** * the character position of each object */ - protected List location = new ArrayList(); + protected List location = new java.util.ArrayList(); /** List of objects to write in the trailer */ private List trailerObjects = new java.util.ArrayList(); @@ -111,12 +108,12 @@ public class PDFDocument { /** Indicates what PDF version is active */ protected int pdfVersion = PDF_VERSION_1_4; - + /** * Indicates which PDF profiles are active (PDF/A, PDF/X etc.) */ protected PDFProfile pdfProfile = new PDFProfile(this); - + /** * the /Root object */ @@ -208,7 +205,7 @@ public class PDFDocument { /** * List of Destinations. */ - protected List destinations = new java.util.ArrayList(); + protected List destinations; /** * List of FileSpecs. @@ -231,19 +228,6 @@ public class PDFDocument { */ private PDFDests dests; - /** - * The PDFLimits object for the name dictionary. - * Note: This object is not a list. - */ - private PDFLimits limits; - - /** - * Whether this PDFDocument has named destinations - * (and thus needs PDFDestinations, PDFLimits, and - * PDFDests) - */ - private boolean hasDestinations = false; - private PDFFactory factory; private boolean encodingOnTheFly = true; @@ -283,7 +267,7 @@ public class PDFDocument { public int getPDFVersion() { return this.pdfVersion; } - + /** @return the String representing the active PDF version */ public String getPDFVersionString() { switch (getPDFVersion()) { @@ -300,7 +284,7 @@ public class PDFDocument { public PDFProfile getProfile() { return this.pdfProfile; } - + /** * Returns the factory for PDF objects. * @return PDFFactory the factory @@ -343,7 +327,7 @@ public class PDFDocument { /** * Set the creation date of the document. - * + * * @param date Date to be stored as creation date in the PDF. */ public void setCreationDate(Date date) { @@ -488,9 +472,6 @@ public class PDFDocument { if (obj instanceof PDFLink) { this.links.add(obj); } - if (obj instanceof PDFDestination) { - this.destinations.add(obj); - } if (obj instanceof PDFFileSpec) { this.filespecs.add(obj); } @@ -614,7 +595,12 @@ public class PDFDocument { * @return the link if found, null otherwise */ protected PDFDestination findDestination(PDFDestination compare) { - return (PDFDestination)findPDFObject(destinations, compare); + int index = getDestinationList().indexOf(compare); + if (index >= 0) { + return (PDFDestination)getDestinationList().get(index); + } else { + return null; + } } /** @@ -751,21 +737,27 @@ public class PDFDocument { } /** - * Gets the list of named destinations. - * - * @return the list of named destinations. + * Adds a destination to the document. + * @param destination the destination object */ - public ArrayList getDestinationList() { - return (ArrayList)destinations; + public void addDestination(PDFDestination destination) { + if (this.destinations == null) { + this.destinations = new java.util.ArrayList(); + } + this.destinations.add(destination); } - + /** - * Sets whether the document has named destinations. + * Gets the list of named destinations. * - * @param hasDestinations whether the document has named destinations. + * @return the list of named destinations. */ - public void setHasDestinations(boolean hasDestinations) { - this.hasDestinations = hasDestinations; + public List getDestinationList() { + if (hasDestinations()) { + return destinations; + } else { + return Collections.EMPTY_LIST; + } } /** @@ -773,17 +765,8 @@ public class PDFDocument { * * @return whether the document has named destinations. */ - public boolean getHasDestinations() { - return this.hasDestinations; - } - - /** - * Gets the PDFLimits object (part of the name dictionary). - * - * @return the PDFLimits object (part of the name dictionary). - */ - public PDFLimits getLimits() { - return limits; + public boolean hasDestinations() { + return this.destinations != null && !this.destinations.isEmpty(); } /** @@ -933,7 +916,7 @@ public class PDFDocument { this.position = 0; getProfile().verifyPDFVersion(); - + byte[] pdf = encode("%PDF-" + getPDFVersionString() + "\n"); stream.write(pdf); this.position += pdf.length; @@ -970,7 +953,7 @@ public class PDFDocument { } } } - + /** * write the trailer * @@ -978,11 +961,13 @@ public class PDFDocument { * @throws IOException if there is an exception writing to the output stream */ public void outputTrailer(OutputStream stream) throws IOException { - if (hasDestinations) { - Collections.sort((ArrayList)destinations, new DestinationComparator()); - limits = getFactory().makeLimits((ArrayList)destinations); - dests = getFactory().makeDests(limits.referencePDF()); - this.root.setNames(dests.referencePDF()); + if (hasDestinations()) { + Collections.sort(destinations, new DestinationComparator()); + dests = getFactory().makeDests(destinations); + if (this.root.getNames() == null) { + this.root.setNames(getFactory().makeNames()); + } + this.root.getNames().setDests(dests); } output(stream); for (int count = 0; count < trailerObjects.size(); count++) { @@ -1061,4 +1046,4 @@ public class PDFDocument { return pdfBytes.length; } -} \ No newline at end of file +} diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index 774b363b3..8011ffc92 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -54,8 +54,6 @@ import org.apache.fop.fonts.truetype.TTFSubSetFile; import org.apache.fop.fonts.type1.PFBData; import org.apache.fop.fonts.type1.PFBParser; import org.apache.xmlgraphics.xmp.Metadata; -import org.apache.fop.area.PageViewport; -import org.apache.fop.area.DestinationData; /** * This class provides method to create and register PDF objects. @@ -747,13 +745,11 @@ public class PDFFactory { for (currentPosition = 0; currentPosition < lastPosition; currentPosition++) { // for every consecutive color pair - PDFColor currentColor = - (PDFColor)theColors.get(currentPosition); + PDFColor currentColor = (PDFColor)theColors.get(currentPosition); PDFColor nextColor = (PDFColor)theColors.get(currentPosition + 1); // colorspace must be consistant - if (getDocument().getColorSpace() - != currentColor.getColorSpace()) { + if (getDocument().getColorSpace() != currentColor.getColorSpace()) { currentColor.setColorSpace( getDocument().getColorSpace()); } @@ -827,8 +823,7 @@ public class PDFFactory { if (existing != null) { return existing; } else { - getDocument().registerObject(newdest); - getDocument().setHasDestinations(true); + getDocument().addDestination(newdest); return newdest; } } @@ -836,58 +831,72 @@ public class PDFFactory { /** * Make a named destination. * - * @param destinationData the DestinationData object that holds the info about this named destination - * @return the new PDF named destination object + * @param idRef ID Reference for this destination (the name of the destination) + * @param goToRef Object reference to the GoTo Action + * @return the newly created destrination */ - public PDFDestination makeDestination(DestinationData destinationData) { - PageViewport pv = destinationData.getPageViewport(); - if (pv == null) { - log.warn("Unresolved destination item received: " + destinationData.getIDRef()); - } - PDFDestination destination = new PDFDestination(destinationData); + public PDFDestination makeDestination(String idRef, Object goToRef) { + PDFDestination destination = new PDFDestination(idRef, goToRef); return getUniqueDestination(destination); } /** - * Create/find a named destination object. - * - * @param idRef The ID of this destination. This will be used for the name. - * @param goToRef A PDF reference to the associated /GoTo - * @param pv The PageViewport of the destination area. Only for informational purposes. - * - * @return The new or existing named destination + * Make a names dictionary (the /Names object). + * @return the new PDFNames object */ - public PDFDestination makeDestination(String idRef, String goToRef, PageViewport pv) { - PDFDestination destination = new PDFDestination(idRef, goToRef, pv); - return getUniqueDestination(destination); + public PDFNames makeNames() { + PDFNames names = new PDFNames(); + getDocument().registerObject(names); + return names; } /** * Make a the head object of the name dictionary (the /Dests object). * + * @param destinationList a list of PDFDestination instances * @return the new PDFDests object */ - public PDFDests makeDests(String limitsRef) { - PDFDests dests = new PDFDests(limitsRef); + public PDFDests makeDests(List destinationList) { + PDFDests dests; + + final boolean deep = true; + //true for a "deep" structure (one node per entry), true for a "flat" structure + if (deep) { + dests = new PDFDests(); + PDFArray kids = new PDFArray(); + Iterator iter = destinationList.iterator(); + while (iter.hasNext()) { + PDFDestination dest = (PDFDestination)iter.next(); + PDFNameTreeNode node = new PDFNameTreeNode(); + getDocument().registerObject(node); + node.setLowerLimit(dest.getIDRef()); + node.setUpperLimit(dest.getIDRef()); + node.setNames(new PDFArray()); + node.getNames().add(dest); + kids.add(node); + } + dests.setLowerLimit(((PDFNameTreeNode)kids.get(0)).getLowerLimit()); + dests.setUpperLimit(((PDFNameTreeNode)kids.get(kids.length() - 1)).getUpperLimit()); + dests.setKids(kids); + } else { + dests = new PDFDests(destinationList); + } getDocument().registerObject(dests); - return dests; } /** - * Make a the limits object of the name dictionary (the /Limits object). + * Make a name tree node. * - * @return the new PDFLimits object + * @return the new name tree node */ - public PDFLimits makeLimits(ArrayList destinationList) { - PDFLimits limits = new PDFLimits(destinationList); - getDocument().registerObject(limits); - - return limits; - } - + public PDFNameTreeNode makeNameTreeNode() { + PDFNameTreeNode node = new PDFNameTreeNode(); + getDocument().registerObject(node); + return node; + } + /* ========================= links ===================================== */ - // Some of the "yoffset-only" functions in this part are obsolete and can // possibly be removed or deprecated. Some are still called by PDFGraphics2D // (although that could be changed, they don't need the yOffset param anyway). @@ -1182,15 +1191,12 @@ public class PDFFactory { * cmap.addContents(); * this.objects.add(cmap); */ - font = - (PDFFontNonBase14)PDFFont.createFont(fontname, fonttype, - basefont, - "Identity-H"); + font = (PDFFontNonBase14)PDFFont.createFont(fontname, fonttype, + basefont, "Identity-H"); } else { - font = - (PDFFontNonBase14)PDFFont.createFont(fontname, fonttype, - basefont, encoding); + font = (PDFFontNonBase14)PDFFont.createFont(fontname, fonttype, + basefont, encoding); } getDocument().registerObject(font); @@ -1203,12 +1209,12 @@ public class PDFFactory { } else { cidMetrics = (CIDFont)metrics; } - PDFCIDSystemInfo sysInfo = - new PDFCIDSystemInfo(cidMetrics.getRegistry(), + PDFCIDSystemInfo sysInfo + = new PDFCIDSystemInfo(cidMetrics.getRegistry(), cidMetrics.getOrdering(), cidMetrics.getSupplement()); - PDFCIDFont cidFont = - new PDFCIDFont(basefont, + PDFCIDFont cidFont + = new PDFCIDFont(basefont, cidMetrics.getCIDType(), cidMetrics.getDefaultWidth(), getSubsetWidths(cidMetrics), sysInfo, diff --git a/src/java/org/apache/fop/pdf/PDFLimits.java b/src/java/org/apache/fop/pdf/PDFLimits.java deleted file mode 100644 index 97c097463..000000000 --- a/src/java/org/apache/fop/pdf/PDFLimits.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 org.apache.fop.pdf.PDFDestination; - -/** - * class representing a Limits object (part of the names dictionary for named destinations) - */ -public class PDFLimits extends PDFObject { - - private ArrayList destinationList; - - /** - * create a named destination - */ - public PDFLimits(ArrayList destinationList) { - /* generic creation of PDF object */ - super(); - this.destinationList = destinationList; - } - - /** - * @see org.apache.fop.pdf.PDFObject#toPDFString() - */ - public String toPDFString() { - String[] idRefs = new String[destinationList.size()]; - String kidsString = ""; - for (int i = 0; i < destinationList.size(); i++) { - PDFDestination dest = (PDFDestination)destinationList.get(i); - idRefs[i] = dest.getIDRef(); - kidsString += dest.referencePDF(); - if (!(i == destinationList.size() - 1)) { - kidsString += " "; - } - } - String s = getObjectID() - + "<<\n" - + "/Limits [(" + idRefs[0] + ") (" + idRefs[destinationList.size() - 1] + ")]\n" - + "/Kids [" + kidsString + "]" - + "\n>>\nendobj\n"; - return s; - } - - /* - * example: - * - * 260 0 obj - * << - * /Limits [(Annotate) (thumbnails)] - * /Kids [248 0 R 253 0 R 254 0 R 259 0 R] - * >> - * endobj - */ - - /** - * Check if this equals another object. - * - * @param obj the object to compare - * @return true if this equals other object - */ - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (obj == null || !(obj instanceof PDFLimits)) { - return false; - } - - return true; - } -} - diff --git a/src/java/org/apache/fop/pdf/PDFNameTreeNode.java b/src/java/org/apache/fop/pdf/PDFNameTreeNode.java new file mode 100644 index 000000000..476cfc7a2 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFNameTreeNode.java @@ -0,0 +1,121 @@ +/* + * 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; + +/** + * Class representing a PDF name tree node. + */ +public class PDFNameTreeNode extends PDFDictionary { + + private static final String KIDS = "Kids"; + private static final String NAMES = "Names"; + private static final String LIMITS = "Limits"; + + /** + * create a named destination + */ + public PDFNameTreeNode() { + /* generic creation of PDF object */ + super(); + } + + /** + * Sets the Kids array. + * @param kids the Kids array + */ + public void setKids(PDFArray kids) { + put(KIDS, kids); + } + + /** + * Returns the Kids array. + * @return the Kids array + */ + public PDFArray getKids() { + return (PDFArray)get(KIDS); + } + + /** + * Sets the Names array. + * @param names the Names array + */ + public void setNames(PDFArray names) { + put(NAMES, names); + } + + /** + * Returns the Names array. + * @return the Names array + */ + public PDFArray getNames() { + return (PDFArray)get(NAMES); + } + + /** + * Sets the lower limit value of the Limits array. + * @param key the lower limit value + */ + public void setLowerLimit(String key) { + PDFArray limits = prepareLimitsArray(); + limits.set(0, key); + } + + /** + * Returns the lower limit value of the Limits array. + * @return the lower limit value + */ + public String getLowerLimit() { + PDFArray limits = prepareLimitsArray(); + return (String)limits.get(0); + } + + /** + * Sets the upper limit value of the Limits array. + * @param key the upper limit value + */ + public void setUpperLimit(String key) { + PDFArray limits = prepareLimitsArray(); + limits.set(1, key); + } + + /** + * Returns the upper limit value of the Limits array. + * @return the upper limit value + */ + public String getUpperLimit() { + PDFArray limits = prepareLimitsArray(); + return (String)limits.get(1); + } + + + private PDFArray prepareLimitsArray() { + PDFArray limits = (PDFArray)get(LIMITS); + if (limits == null) { + limits = new PDFArray(new Object[2]); + put(LIMITS, limits); + } + if (limits.length() != 2) { + throw new IllegalStateException("Limits array must have 2 entries"); + } + return limits; + } + +} + diff --git a/src/java/org/apache/fop/pdf/PDFNames.java b/src/java/org/apache/fop/pdf/PDFNames.java new file mode 100644 index 000000000..d2d895771 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFNames.java @@ -0,0 +1,51 @@ +/* + * 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; + +/** + * Class representing a PDF Names object + */ +public class PDFNames extends PDFDictionary { + + /** + * Create the Names object + */ + public PDFNames() { + /* generic creation of PDF object */ + super(); + } + + /** + * Returns the Dests object + * @return the Dests object, or null if it's not used + */ + public PDFDests getDests() { + return (PDFDests)get("Dests"); + } + + /** + * Set the Dests object + * @param dests the Dests object + */ + public void setDests(PDFDests dests) { + put("Dests", dests); + } + +} diff --git a/src/java/org/apache/fop/pdf/PDFObject.java b/src/java/org/apache/fop/pdf/PDFObject.java index b1e8e1245..806f7621d 100644 --- a/src/java/org/apache/fop/pdf/PDFObject.java +++ b/src/java/org/apache/fop/pdf/PDFObject.java @@ -37,7 +37,7 @@ import org.apache.commons.logging.LogFactory; * Object has a number and a generation (although the generation will always * be 0 in new documents). */ -public abstract class PDFObject { +public abstract class PDFObject implements PDFWritable { /** logger for all PDFObjects (and descendants) */ protected static Log log = LogFactory.getLog(PDFObject.class.getName()); @@ -138,10 +138,22 @@ public abstract class PDFObject { * @return the reference string */ public String referencePDF() { + if (!hasObjectNumber()) { + throw new IllegalArgumentException( + "Cannot reference this object. It doesn't have an object number"); + } String ref = getObjectNumber() + " " + getGeneration() + " R"; return ref; } + /** + * Creates and returns a reference to this object. + * @return the object reference + */ + public PDFReference makeReference() { + return new PDFReference(this); + } + /** * Write the PDF represention of this object * @@ -178,6 +190,19 @@ public abstract class PDFObject { + "Use output(OutputStream) instead."); } + /** + * Returns a representation of this object for in-object placement, i.e. if the object + * has an object number its reference is returned. Otherwise, its PDF representation is + * returned. + * @return the String representation + */ + public String toInlinePDFString() { + if (hasObjectNumber()) { + return referencePDF(); + } else { + return toPDFString(); + } + } /** * Converts text to a byte array for writing to a PDF file. diff --git a/src/java/org/apache/fop/pdf/PDFReference.java b/src/java/org/apache/fop/pdf/PDFReference.java new file mode 100644 index 000000000..4ca50a79f --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFReference.java @@ -0,0 +1,67 @@ +/* + * 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.lang.ref.Reference; +import java.lang.ref.SoftReference; + +/** + * Class representing a PDF object reference. The object holds a soft reference to the actual + * PDF object so the garbage collector can free the object if it's not referenced elsewhere. The + * important thing about the class is the reference information to the actual PDF object in the + * PDF file. + */ +public class PDFReference implements PDFWritable { + + private String indirectReference; + + private Reference objReference; + + /** + * Creates a new PDF reference. + * @param obj the object to be referenced + */ + public PDFReference(PDFObject obj) { + this.indirectReference = obj.referencePDF(); + this.objReference = new SoftReference(obj); + } + + /** + * Returns the PDF object + * @return the PDF object, or null if it has been released + */ + public PDFObject getObject() { + if (this.objReference != null) { + PDFObject obj = (PDFObject)this.objReference.get(); + if (obj == null) { + this.objReference = null; + } + return obj; + } else { + return null; + } + } + + /** @see org.apache.fop.pdf.PDFWritable#toInlinePDFString() */ + public String toInlinePDFString() { + return this.indirectReference; + } + +} diff --git a/src/java/org/apache/fop/pdf/PDFRoot.java b/src/java/org/apache/fop/pdf/PDFRoot.java index 60fe6b390..4a80de4c9 100644 --- a/src/java/org/apache/fop/pdf/PDFRoot.java +++ b/src/java/org/apache/fop/pdf/PDFRoot.java @@ -20,7 +20,6 @@ package org.apache.fop.pdf; import java.util.List; -import java.util.ArrayList; /** * class representing a Root (/Catalog) object @@ -63,11 +62,8 @@ public class PDFRoot extends PDFObject { /** The array of OutputIntents */ private List outputIntents; - /** - * The referencePDF value of the /Dests object, - * if this PDF has a Name Dictionary - */ - private String namesReferencePDF = null; + /** the /Dests object, if this PDF has a Names Dictionary */ + private PDFNames names; private int pageMode = PAGEMODE_USENONE; @@ -133,12 +129,20 @@ public class PDFRoot extends PDFObject { } /** - * Set the optional Metadata object. - * @param meta the Metadata object - * @since PDF 1.4 + * Set the Names object. + * @param names the Names object + * @since PDF 1.2 + */ + public void setNames(PDFNames names) { + this.names = names; + } + + /** + * @return the Names object if set, null otherwise. + * @since PDF 1.2 */ - public void setNames(String referencePDF) { - this.namesReferencePDF = referencePDF; + public PDFNames getNames() { + return this.names; } /** @@ -175,7 +179,7 @@ public class PDFRoot extends PDFObject { public String toPDFString() { StringBuffer p = new StringBuffer(128); p.append(getObjectID()); - p.append("<< /Type /Catalog\n/Pages " + p.append("<< /Type /Catalog\n /Pages " + this.rootPages.referencePDF() + "\n"); if (outline != null) { @@ -197,17 +201,17 @@ public class PDFRoot extends PDFObject { break; } } - if (getDocumentSafely().getHasDestinations() && namesReferencePDF != null) { - p.append(" /Names " + namesReferencePDF + "\n"); + if (getDocumentSafely().hasDestinations() && getNames() != null) { + p.append(" /Names " + getNames().referencePDF() + "\n"); } if (getMetadata() != null && getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) { - p.append("/Metadata " + getMetadata().referencePDF() + "\n"); + p.append(" /Metadata " + getMetadata().referencePDF() + "\n"); } if (this.outputIntents != null && this.outputIntents.size() > 0 && getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) { - p.append("/OutputIntents ["); + p.append(" /OutputIntents ["); for (int i = 0, c = this.outputIntents.size(); i < c; i++) { PDFOutputIntent outputIntent = (PDFOutputIntent)this.outputIntents.get(i); if (i > 0) { diff --git a/src/java/org/apache/fop/pdf/PDFWritable.java b/src/java/org/apache/fop/pdf/PDFWritable.java new file mode 100644 index 000000000..23bcf9ad8 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFWritable.java @@ -0,0 +1,36 @@ +/* + * 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; + +/** + * This interface is implemented by classes that can be serialized to a PDF file either by + * serializing the object or by writing a indirect reference to the actual object. + */ +public interface PDFWritable { + + /** + * Returns a representation of this object for in-object placement, i.e. if the object + * has an object number its reference is returned. Otherwise, its PDF representation is + * returned. + * @return the String representation + */ + String toInlinePDFString(); + +} diff --git a/src/java/org/apache/fop/render/AbstractRenderer.java b/src/java/org/apache/fop/render/AbstractRenderer.java index 835445603..50c314f15 100644 --- a/src/java/org/apache/fop/render/AbstractRenderer.java +++ b/src/java/org/apache/fop/render/AbstractRenderer.java @@ -165,7 +165,7 @@ public abstract class AbstractRenderer /** * @see org.apache.fop.render.Renderer#processOffDocumentItem(OffDocumentItem) */ - public void processOffDocumentItem(OffDocumentItem oDI) { } + public void processOffDocumentItem(OffDocumentItem odi) { } /** @see org.apache.fop.render.Renderer#getGraphics2DAdapter() */ public Graphics2DAdapter getGraphics2DAdapter() { diff --git a/src/java/org/apache/fop/render/Renderer.java b/src/java/org/apache/fop/render/Renderer.java index 0b763b996..d54a61025 100644 --- a/src/java/org/apache/fop/render/Renderer.java +++ b/src/java/org/apache/fop/render/Renderer.java @@ -113,9 +113,9 @@ public interface Renderer { * document (e.g., PDF bookmarks). Note - not all renderers will process * all off-document items. * - * @param ext The extension element to be rendered + * @param odi The off-document item to be rendered */ - void processOffDocumentItem(OffDocumentItem ext); + void processOffDocumentItem(OffDocumentItem odi); /** * @return the adapter for painting Java2D images (or null if not supported) diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java index 9813d08db..7d584c036 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java @@ -83,6 +83,7 @@ import org.apache.fop.pdf.PDFEncryptionManager; import org.apache.fop.pdf.PDFEncryptionParams; import org.apache.fop.pdf.PDFFactory; import org.apache.fop.pdf.PDFFilterList; +import org.apache.fop.pdf.PDFGoTo; import org.apache.fop.pdf.PDFICCBasedColorSpace; import org.apache.fop.pdf.PDFICCStream; import org.apache.fop.pdf.PDFGoTo; @@ -570,11 +571,11 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { * @see org.apache.fop.render.Renderer#processOffDocumentItem(OffDocumentItem) */ public void processOffDocumentItem(OffDocumentItem odi) { - // render Destinations if (odi instanceof DestinationData) { + // render Destinations renderDestination((DestinationData) odi); - // render Bookmark-Tree } else if (odi instanceof BookmarkData) { + // render Bookmark-Tree renderBookmarkTree((BookmarkData) odi); } else if (odi instanceof OffDocumentExtensionAttachment) { ExtensionAttachment attachment = ((OffDocumentExtensionAttachment)odi).getAttachment(); @@ -588,15 +589,12 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { String targetID = dd.getIDRef(); if (targetID != null && targetID.length() > 0) { PageViewport pv = dd.getPageViewport(); - if (pv != null) { - String pvKey = pv.getKey(); - PDFGoTo gt = getPDFGoToForID(targetID, pvKey); - // create/find and register PDFDestination object: - pdfDoc.getFactory().makeDestination(targetID, gt.referencePDF(), pv); - } else { - log.warn("DestinationData item with IDRef \"" - + targetID + "\" has a null PageViewport."); + if (pv == null) { + log.warn("Unresolved destination item received: " + dd.getIDRef()); } + PDFGoTo gt = getPDFGoToForID(targetID, pv.getKey()); + pdfDoc.getFactory().makeDestination( + dd.getIDRef(), gt.makeReference()); } else { log.warn("DestinationData item with null or empty IDRef received."); } -- 2.39.5