From fdb46ddc0d5825898e684527a43c0732b7b2339d Mon Sep 17 00:00:00 2001 From: Jay Bryant Date: Sun, 1 Apr 2007 14:50:36 +0000 Subject: [PATCH] changes to support named destinations git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@524606 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/pdf/PDFDestination.java | 140 ++++++++++++++++++ src/java/org/apache/fop/pdf/PDFDests.java | 79 ++++++++++ src/java/org/apache/fop/pdf/PDFDocument.java | 89 ++++++++++- src/java/org/apache/fop/pdf/PDFFactory.java | 71 +++++++-- src/java/org/apache/fop/pdf/PDFLimits.java | 93 ++++++++++++ src/java/org/apache/fop/pdf/PDFRoot.java | 19 +++ 6 files changed, 481 insertions(+), 10 deletions(-) create mode 100644 src/java/org/apache/fop/pdf/PDFDestination.java create mode 100644 src/java/org/apache/fop/pdf/PDFDests.java create mode 100644 src/java/org/apache/fop/pdf/PDFLimits.java diff --git a/src/java/org/apache/fop/pdf/PDFDestination.java b/src/java/org/apache/fop/pdf/PDFDestination.java new file mode 100644 index 000000000..e8228acfd --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFDestination.java @@ -0,0 +1,140 @@ +/* + * 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: PDFLink.java 426576 2006-07-28 15:44:37Z jeremias $ */ + +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 + */ + public PDFDestination(DestinationData destinationData) { + /* generic creation of PDF object */ + super(); + this.goToReference = destinationData.getGoToReference(); + this.idRef = destinationData.getIDRef(); + this.pageViewport = destinationData.getPageViewport(); + } + + /** + * @see org.apache.fop.pdf.PDFObject#toPDFString() + */ + public String toPDFString() { + String s = getObjectID() + + "<<" + + "/Limits [(" + idRef + ") (" + idRef + ")]\n" + + "/Names [(" + idRef + ") " + goToReference + "]" + + "\n>>\nendobj\n"; + return s; + } + + /* + * example: + * + * 249 0 obj + * << + * /Limits [(drivervariables) (drivervariables)] + * /Names [(drivervariables) 73 0 R] + * >> + * endobj + */ + + /** + * Sets the GoToReference in the associated DestinationData object. + * + * @param the GoToReference to set in the associated DestinationData object. + */ + public void setGoToReference(String goToReference) { + this.goToReference = goToReference; + } + + /** + * Returns the GoToReference from the associated DestinationData object. + * + * @return the GoToReference from the associated DestinationData object. + */ + public String getGoToReference() + { + return this.goToReference; + } + + /** + * Get the PageViewport object that this destination refers to + * + * @return the PageViewport that this destination points to + */ + public PageViewport getPageViewport() { + return this.pageViewport; + } + + /** + * Returns the RefID from the associated DestinationData object. + * + * @return the RefID from the associated DestinationData object. + */ + public String getIDRef() + { + return this.idRef; + } + + /** + * 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 PDFDestination)) { + return false; + } + + PDFDestination dest = (PDFDestination)obj; + if (dest.getIDRef() == this.getIDRef()) { + return true; + } + + return true; + } +} + diff --git a/src/java/org/apache/fop/pdf/PDFDests.java b/src/java/org/apache/fop/pdf/PDFDests.java new file mode 100644 index 000000000..3989b397a --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFDests.java @@ -0,0 +1,79 @@ +/* + * 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: PDFLink.java 426576 2006-07-28 15:44:37Z jeremias $ */ + +package org.apache.fop.pdf; + +import org.apache.fop.area.DestinationData; + +/** + * class representing an /Dests object (part of a name dictionary) + */ +public class PDFDests extends PDFObject { + + private String limitsRef; + + /** + * create a named destination + */ + public PDFDests(String limitsRef) { + /* generic creation of PDF object */ + super(); + this.limitsRef = limitsRef; + } + + /** + * @see org.apache.fop.pdf.PDFObject#toPDFString() + */ + public String toPDFString() { + String s = getObjectID() + + "<<\n" + + "/Dests " + limitsRef + + "\n>>\nendobj\n"; + return s; + } + + /* + * 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/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java index 4ce82fab9..042d3d00b 100644 --- a/src/java/org/apache/fop/pdf/PDFDocument.java +++ b/src/java/org/apache/fop/pdf/PDFDocument.java @@ -32,6 +32,7 @@ import java.util.Date; import java.util.List; import java.util.Map; import java.util.Iterator; +import java.util.ArrayList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -85,7 +86,7 @@ public class PDFDocument { /** * the character position of each object */ - protected List location = new java.util.ArrayList(); + protected List location = new ArrayList(); /** List of objects to write in the trailer */ private List trailerObjects = new java.util.ArrayList(); @@ -201,6 +202,11 @@ public class PDFDocument { */ protected List links = new java.util.ArrayList(); + /** + * List of Destinations. + */ + protected List destinations = new java.util.ArrayList(); + /** * List of FileSpecs. */ @@ -216,6 +222,25 @@ public class PDFDocument { */ protected List gotos = new java.util.ArrayList(); + /** + * The PDFDests object for the name dictionary. + * Note: This object is not a list. + */ + 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; @@ -460,6 +485,9 @@ 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); } @@ -577,6 +605,15 @@ public class PDFDocument { return (PDFFont)fontMap.get(fontname); } + /** + * Finds a named destination. + * @param compare reference object to use as search template + * @return the link if found, null otherwise + */ + protected PDFDestination findDestination(PDFDestination compare) { + return (PDFDestination)findPDFObject(destinations, compare); + } + /** * Finds a link. * @param compare reference object to use as search template @@ -701,6 +738,51 @@ public class PDFDocument { return xObject; } + /** + * Gets the PDFDests object (which represents the /Dests entry). + * + * @return the PDFDests object (which represents the /Dests entry). + */ + public PDFDests getDests() { + return dests; + } + + /** + * Gets the list of named destinations. + * + * @return the list of named destinations. + */ + public ArrayList getDestinationList() { + return (ArrayList)destinations; + } + + /** + * Sets whether the document has named destinations. + * + * @param whether the document has named destinations. + */ + public void setHasDestinations(boolean hasDestinations) { + this.hasDestinations = hasDestinations; + } + + /** + * Gets whether the document has named destinations. + * + * @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; + } + /** * Add an image to the PDF document. * This adds an image to the PDF objects. @@ -893,6 +975,11 @@ public class PDFDocument { * @throws IOException if there is an exception writing to the output stream */ public void outputTrailer(OutputStream stream) throws IOException { + if (hasDestinations) { + limits = getFactory().makeLimits((ArrayList)destinations); + dests = getFactory().makeDests(limits.referencePDF()); + this.root.setNames(dests.referencePDF()); + } output(stream); for (int count = 0; count < trailerObjects.size(); count++) { PDFObject o = (PDFObject)trailerObjects.get(count); diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index 4ab6ebd7b..3005f2018 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.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. @@ -31,6 +31,7 @@ import java.util.List; import java.util.Map; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; +import java.util.ArrayList; // Apache libs import org.apache.commons.io.IOUtils; @@ -52,6 +53,8 @@ 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. @@ -731,7 +734,7 @@ public class PDFFactory { PDFPattern myPattern; //PDFColorSpace theColorSpace; double interpolation = (double)1.000; - List theFunctions = new java.util.ArrayList(); + List theFunctions = new ArrayList(); int currentPosition; int lastPosition = theColors.size() - 1; @@ -782,7 +785,7 @@ public class PDFFactory { } else { // if the center x, center y, and radius specifiy // the gradient, then assume the same center x, center y, // and radius of zero for the other necessary component - List newCoords = new java.util.ArrayList(); + List newCoords = new ArrayList(); newCoords.add(theCoords.get(0)); newCoords.add(theCoords.get(1)); newCoords.add(theCoords.get(2)); @@ -809,6 +812,56 @@ public class PDFFactory { return (myPattern); } + /* ============= named destinations and the name dictionary ============ */ + + /** + * Make a named destination. + * + * @param destinationData the DestinationData object that holds the info about this named destination + * @return the new PDF named destination object + */ + 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); + + PDFDestination oldDestination = getDocument().findDestination(destination); + if (destination == oldDestination) { + destination = oldDestination; + } else { + getDocument().registerObject(destination); + getDocument().setHasDestinations(true); + } + + return destination; + } + + /** + * Make a the head object of the name dictionary (the /Dests object). + * + * @return the new PDFDests object + */ + public PDFDests makeDests(String limitsRef) { + PDFDests dests = new PDFDests(limitsRef); + getDocument().registerObject(dests); + + return dests; + } + + /** + * Make a the limits object of the name dictionary (the /Limits object). + * + * @return the new PDFLimits object + */ + public PDFLimits makeLimits(ArrayList destinationList) { + PDFLimits limits = new PDFLimits(destinationList); + getDocument().registerObject(limits); + + return limits; + } + /* ========================= links ===================================== */ /** @@ -888,7 +941,7 @@ public class PDFFactory { return link; } - private String getGoToReference(String destination, float yoffset) { + public String getGoToReference(String destination, float yoffset) { getDocument().getProfile().verifyActionAllowed(); String goToReference = null; PDFGoTo gt = new PDFGoTo(destination); @@ -1153,9 +1206,9 @@ public class PDFFactory { int value = 0; for (int i = 0, c = cidSubset.length(); i < c; i++) { int shift = i % 8; - boolean b = cidSubset.get(i); + boolean b = cidSubset.get(i); if (b) { - value |= 1 << 7 - shift; + value |= 1 << 7 - shift; } if (shift == 7) { baout.write(value); @@ -1335,7 +1388,7 @@ public class PDFFactory { public PDFICCBasedColorSpace makeICCBasedColorSpace(PDFResourceContext res, String explicitName, PDFICCStream iccStream) { PDFICCBasedColorSpace cs = new PDFICCBasedColorSpace(explicitName, iccStream); - + getDocument().registerObject(cs); if (res != null) { @@ -1343,7 +1396,7 @@ public class PDFFactory { } else { getDocument().getResources().addColorSpace(cs); } - + return cs; } diff --git a/src/java/org/apache/fop/pdf/PDFLimits.java b/src/java/org/apache/fop/pdf/PDFLimits.java new file mode 100644 index 000000000..ece7a3dfb --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFLimits.java @@ -0,0 +1,93 @@ +/* + * 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: PDFLink.java 426576 2006-07-28 15:44:37Z jeremias $ */ + +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/PDFRoot.java b/src/java/org/apache/fop/pdf/PDFRoot.java index ffe9611ba..60fe6b390 100644 --- a/src/java/org/apache/fop/pdf/PDFRoot.java +++ b/src/java/org/apache/fop/pdf/PDFRoot.java @@ -20,6 +20,7 @@ package org.apache.fop.pdf; import java.util.List; +import java.util.ArrayList; /** * class representing a Root (/Catalog) object @@ -62,6 +63,12 @@ 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; + private int pageMode = PAGEMODE_USENONE; /** @@ -125,6 +132,15 @@ public class PDFRoot extends PDFObject { return outline; } + /** + * Set the optional Metadata object. + * @param meta the Metadata object + * @since PDF 1.4 + */ + public void setNames(String referencePDF) { + this.namesReferencePDF = referencePDF; + } + /** * Set the optional Metadata object. * @param meta the Metadata object @@ -181,6 +197,9 @@ public class PDFRoot extends PDFObject { break; } } + if (getDocumentSafely().getHasDestinations() && namesReferencePDF != null) { + p.append(" /Names " + namesReferencePDF + "\n"); + } if (getMetadata() != null && getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) { p.append("/Metadata " + getMetadata().referencePDF() + "\n"); -- 2.39.5