--- test/layoutengine/standard-testcases/basic-link_external-destination.xml (revision 642740) +++ test/layoutengine/standard-testcases/basic-link_external-destination.xml Sun Mar 30 16:49:53 CEST 2008 @@ -38,7 +38,7 @@ - - + + --- src/java/org/apache/fop/pdf/PDFGoToRemote.java (revision 642740) +++ src/java/org/apache/fop/pdf/PDFGoToRemote.java Sun Mar 30 15:37:05 CEST 2008 @@ -20,7 +20,7 @@ package org.apache.fop.pdf; /** - * class representing a /GoToR object. + * Class representing a /GoToR object. */ public class PDFGoToRemote extends PDFAction { @@ -30,17 +30,21 @@ private PDFFileSpec pdfFileSpec; private int pageReference = 0; private String destination = null; + private boolean newWindow = false; /** - * create an GoToR object. + * Create an GoToR object. * * @param pdfFileSpec the fileSpec associated with the action + * @param newWindow boolean indicating whether the target should be + * displayed in a new window */ - public PDFGoToRemote(PDFFileSpec pdfFileSpec) { + public PDFGoToRemote(PDFFileSpec pdfFileSpec, boolean newWindow) { /* generic creation of object */ super(); this.pdfFileSpec = pdfFileSpec; + this.newWindow = newWindow; } /** @@ -48,13 +52,16 @@ * * @param pdfFileSpec the fileSpec associated with the action * @param page a page reference within the remote document + * @param newWindow boolean indicating whether the target should be + * displayed in a new window */ - public PDFGoToRemote(PDFFileSpec pdfFileSpec, int page) { + public PDFGoToRemote(PDFFileSpec pdfFileSpec, int page, boolean newWindow) { /* generic creation of object */ super(); this.pdfFileSpec = pdfFileSpec; this.pageReference = page; + this.newWindow = newWindow; } /** @@ -62,13 +69,16 @@ * * @param pdfFileSpec the fileSpec associated with the action * @param dest a named destination within the remote document + * @param newWindow boolean indicating whether the target should be + * displayed in a new window */ - public PDFGoToRemote(PDFFileSpec pdfFileSpec, String dest) { + public PDFGoToRemote(PDFFileSpec pdfFileSpec, String dest, boolean newWindow) { /* generic creation of object */ super(); this.pdfFileSpec = pdfFileSpec; this.destination = dest; + this.newWindow = newWindow; } /** @@ -86,14 +96,20 @@ public String toPDFString() { StringBuffer sb = new StringBuffer(64); sb.append(getObjectID()); - sb.append("<<\n/S /GoToR\n/F " + pdfFileSpec.referencePDF() + "\n"); + sb.append("<<\n/S /GoToR\n/F "); + sb.append(pdfFileSpec.referencePDF()); + sb.append("\n"); if (destination != null) { - sb.append("/D (" + this.destination + ")"); + sb.append("/D (").append(this.destination).append(")"); } else { - sb.append("/D [ " + this.pageReference + " /XYZ null null null ]"); + sb.append("/D [ ").append(this.pageReference).append(" /XYZ null null null ]"); } + if (newWindow) { + sb.append("/NewWindow true"); + } + sb.append(" \n>>\nendobj\n"); return sb.toString(); @@ -142,7 +158,7 @@ } } - return true; + return (this.newWindow == remote.newWindow); } } --- src/java/org/apache/fop/fo/FOPropertyMapping.java (revision 642740) +++ src/java/org/apache/fop/fo/FOPropertyMapping.java Sun Mar 30 13:02:04 CEST 2008 @@ -258,7 +258,7 @@ /** * Return a (possibly cached) enum property based in the enum value. - * @param enum A enum value from Constants.java. + * @param enumValue A enum value from Constants.java. * @param text the text value by which this enum property is known * @return An EnumProperty instance. */ @@ -371,9 +371,8 @@ || ((id & Constants.PROPERTY_MASK) == 0)) { return (String) s_htPropIds.get(new Integer(id)); } else { - return (String) s_htPropIds.get(new Integer( - id & Constants.PROPERTY_MASK)) + "." + s_htPropIds.get( - new Integer(id & Constants.COMPOUND_MASK)); + return s_htPropIds.get(new Integer(id & Constants.PROPERTY_MASK)) + + "." + s_htPropIds.get(new Integer(id & Constants.COMPOUND_MASK)); } } @@ -2019,8 +2018,10 @@ addPropertyMaker("internal-destination", m); // show-destination - m = new ToBeImplementedProperty.Maker(PR_SHOW_DESTINATION); + m = new EnumProperty.Maker(PR_SHOW_DESTINATION); m.setInherited(false); + m.addEnum("new", getEnumProperty(EN_NEW, "NEW")); + m.addEnum("replace", getEnumProperty(EN_REPLACE, "REPLACE")); m.setDefault("replace"); addPropertyMaker("show-destination", m); --- src/java/org/apache/fop/fo/flow/BasicLink.java (revision 642740) +++ src/java/org/apache/fop/fo/flow/BasicLink.java Sun Mar 30 13:05:03 CEST 2008 @@ -27,19 +27,21 @@ import org.apache.fop.fo.ValidationException; /** - * Class modelling the fo:basic-link object. + * Class modelling the + * fo:basic-link object. * * This class contains the logic to determine the link represented by this FO, * and whether that link is external (uses a URI) or internal (an id * reference). */ public class BasicLink extends Inline { + // The value of properties relevant for fo:basic-link. // private ToBeImplementedProperty destinationPlacementOffset; private String externalDestination; // private ToBeImplementedProperty indicateDestination; private String internalDestination; - // private ToBeImplementedProperty showDestination; + private int showDestination; // private ToBeImplementedProperty targetProcessingContext; // private ToBeImplementedProperty targetPresentationContext; // private ToBeImplementedProperty targetStylesheet; @@ -51,22 +53,23 @@ private boolean blockOrInlineItemFound = false; /** - * @param parent FONode that is the parent of this object + * Construct a BasicLink instance with the given {@link FONode} + * as its parent. + * + * @param parent {@link FONode} that is the parent of this object */ public BasicLink(FONode parent) { super(parent); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); // destinationPlacementOffset = pList.get(PR_DESTINATION_PLACEMENT_OFFSET); externalDestination = pList.get(PR_EXTERNAL_DESTINATION).getString(); // indicateDestination = pList.get(PR_INDICATE_DESTINATION); internalDestination = pList.get(PR_INTERNAL_DESTINATION).getString(); - // showDestination = pList.get(PR_SHOW_DESTINATION); + showDestination = pList.get(PR_SHOW_DESTINATION).getEnum(); // targetProcessingContext = pList.get(PR_TARGET_PROCESSING_CONTEXT); // targetPresentationContext = pList.get(PR_TARGET_PRESENTATION_CONTEXT); // targetStylesheet = pList.get(PR_TARGET_STYLESHEET); @@ -81,26 +84,19 @@ } } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ protected void startOfNode() throws FOPException { super.startOfNode(); getFOEventHandler().startLink(this); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ protected void endOfNode() throws FOPException { super.endOfNode(); getFOEventHandler().endLink(); } - /** - * {@inheritDoc} String, String) - * XSL Content Model: marker* (#PCDATA|%inline;|%block;)* - */ + /** {@inheritDoc} */ protected void validateChildNode(Locator loc, String nsURI, String localName) throws ValidationException { if (FO_URI.equals(nsURI) && localName.equals("marker")) { @@ -115,33 +111,52 @@ } /** - * @return the "internal-destination" property. + * Get the value of the internal-destination property. + * + * @return the "internal-destination" property */ public String getInternalDestination() { return internalDestination; } /** - * @return the "external-destination" property. + * Get the value of the external-destination property. + * + * @return the "external-destination" property */ public String getExternalDestination() { return externalDestination; } /** - * @return whether or not this basic link has an internal destination or not + * Convenience method to check if this instance has an internal destination. + * + * @return true if this basic link has an internal destination; + * false otherwise */ public boolean hasInternalDestination() { return internalDestination != null && internalDestination.length() > 0; } /** - * @return whether or not this basic link has an external destination or not + * Convenience method to check if this instance has an external destination + * + * @return true if this basic link has an external destination; + * false otherwise */ public boolean hasExternalDestination() { return externalDestination != null && externalDestination.length() > 0; } + /** + * Get the value of the show-destination property. + * + * @return the "show-destination" property + */ + public int getShowDestination() { + return this.showDestination; + } + /** {@inheritDoc} */ public String getLocalName() { return "basic-link"; --- src/java/org/apache/fop/pdf/PDFFactory.java (revision 642740) +++ src/java/org/apache/fop/pdf/PDFFactory.java Sun Mar 30 16:32:15 CEST 2008 @@ -737,7 +737,7 @@ List theCone; PDFPattern myPattern; //PDFColorSpace theColorSpace; - double interpolation = (double)1.000; + double interpolation = 1.000; List theFunctions = new ArrayList(); int currentPosition; @@ -961,7 +961,7 @@ } /** - * make a link object + * Make a {@link PDFLink} object * * @param rect the clickable rectangle * @param destination the destination file @@ -976,7 +976,7 @@ PDFLink link = new PDFLink(rect); if (linkType == PDFLink.EXTERNAL) { - link.setAction(getExternalAction(destination)); + link.setAction(getExternalAction(destination, false)); } else { // linkType is internal String goToReference = getGoToReference(destination, yoffset); @@ -999,9 +999,11 @@ * * @param target The external target. This may be a PDF file name * (optionally with internal page number or destination) or any type of URI. + * @param newWindow boolean indicating whether the target should be + * displayed in a new window * @return the PDFAction thus created or found */ - public PDFAction getExternalAction(String target) { + public PDFAction getExternalAction(String target, boolean newWindow) { int index; String targetLo = target.toLowerCase(); // HTTP URL? @@ -1009,17 +1011,17 @@ return new PDFUri(target); // Bare PDF file name? } else if (targetLo.endsWith(".pdf")) { - return getGoToPDFAction(target, null, -1); + return getGoToPDFAction(target, null, -1, newWindow); // PDF file + page? } else if ((index = targetLo.indexOf(".pdf#page=")) > 0) { String filename = target.substring(0, index + 4); int page = Integer.parseInt(target.substring(index + 10)); - return getGoToPDFAction(filename, null, page); + return getGoToPDFAction(filename, null, page, newWindow); // PDF file + destination? } else if ((index = targetLo.indexOf(".pdf#dest=")) > 0) { String filename = target.substring(0, index + 4); String dest = target.substring(index + 10); - return getGoToPDFAction(filename, dest, -1); + return getGoToPDFAction(filename, dest, -1, newWindow); // None of the above? Default to URI: } else { return new PDFUri(target); @@ -1069,9 +1071,11 @@ * @param file the pdf file name * @param dest the remote name destination, may be null * @param page the remote page number, -1 means not specified + * @param newWindow boolean indicating whether the target should be + * displayed in a new window * @return the pdf goto remote object */ - private PDFGoToRemote getGoToPDFAction(String file, String dest, int page) { + private PDFGoToRemote getGoToPDFAction(String file, String dest, int page, boolean newWindow) { getDocument().getProfile().verifyActionAllowed(); PDFFileSpec fileSpec = new PDFFileSpec(file); PDFFileSpec oldspec = getDocument().findFileSpec(fileSpec); @@ -1083,11 +1087,11 @@ PDFGoToRemote remote; if (dest == null && page == -1) { - remote = new PDFGoToRemote(fileSpec); + remote = new PDFGoToRemote(fileSpec, newWindow); } else if (dest != null) { - remote = new PDFGoToRemote(fileSpec, dest); + remote = new PDFGoToRemote(fileSpec, dest, newWindow); } else { - remote = new PDFGoToRemote(fileSpec, page); + remote = new PDFGoToRemote(fileSpec, page, newWindow); } PDFGoToRemote oldremote = getDocument().findGoToRemote(remote); if (oldremote == null) { @@ -1458,6 +1462,7 @@ try { in = new java.net.URL(source.getSystemId()).openStream(); } catch (MalformedURLException e) { + //TODO: why construct a new exception when it is not thrown? new FileNotFoundException( "File not found. URL could not be resolved: " + e.getMessage()); @@ -1563,7 +1568,6 @@ /** * Create a PDFICCStream * @see PDFImageXObject - * @see org.apache.fop.image.JpegImage * @see org.apache.fop.pdf.PDFDeviceColorSpace * @return the new PDF ICC stream object */ --- src/java/org/apache/fop/fo/Constants.java (revision 642740) +++ src/java/org/apache/fop/fo/Constants.java Sun Mar 30 12:39:56 CEST 2008 @@ -1092,7 +1092,11 @@ /** Enumeration constant -- for instream-foreign-object and external-graphic, XSL 1.1 */ int EN_SCALE_DOWN_TO_FIT = 187; /** Enumeration constant -- for instream-foreign-object and external-graphic, XSL 1.1 */ - int EN_SCALE_UP_TO_FIT = 188; + int EN_SCALE_UP_TO_FIT = 188; + /** Enumeration constant -- for fo:basic-link show-destination */ + int EN_REPLACE = 189; + /** Enumeration constant -- for fo:basic-link show-destination */ + int EN_NEW = 190; /** Number of enumeration constants defined */ - int ENUM_COUNT = 188; + int ENUM_COUNT = 190; } --- src/java/org/apache/fop/area/Trait.java (revision 642740) +++ src/java/org/apache/fop/area/Trait.java Sun Mar 30 16:52:26 CEST 2008 @@ -194,7 +194,7 @@ public static final Integer OVERLINE_COLOR = new Integer(35); /** Trait for color of linethrough decorations when rendering inline parent. */ public static final Integer LINETHROUGH_COLOR = new Integer(36); - + /** Maximum value used by trait keys */ public static final int MAX_TRAIT_KEY = 36; @@ -226,7 +226,7 @@ // Create a hashmap mapping trait code to name for external representation //put(ID_LINK, new TraitInfo("id-link", String.class)); put(INTERNAL_LINK, new TraitInfo("internal-link", InternalLink.class)); - put(EXTERNAL_LINK, new TraitInfo("external-link", String.class)); + put(EXTERNAL_LINK, new TraitInfo("external-link", ExternalLink.class)); put(FONT, new TraitInfo("font", FontTriplet.class)); put(FONT_SIZE, new TraitInfo("font-size", Integer.class)); put(COLOR, new TraitInfo("color", Color.class)); @@ -277,7 +277,7 @@ new TraitInfo("is-reference-area", Boolean.class)); put(IS_VIEWPORT_AREA, new TraitInfo("is-viewport-area", Boolean.class)); - + } /** @@ -548,6 +548,51 @@ } /** + * External Link trait structure + */ + public static class ExternalLink implements Serializable { + + private String destination; + private boolean newWindow; + + /** + * Constructs an ExternalLink object with the given destination + * + * @param destination target of the link + * @param newWindow true if the target should be opened in a new window + */ + public ExternalLink(String destination, boolean newWindow) { + this.destination = destination; + this.newWindow = newWindow; + } + + /** + * Get the target/destination of the link + * @return the destination of the link + */ + public String getDestination() { + return this.destination; + } + + /** + * Check if the target has to be displayed in a new window + * @return true if the target has to be displayed in a new window + */ + public boolean newWindow() { + return this.newWindow; + } + + /** {@inheritDoc} */ + public String toString() { + StringBuffer sb = new StringBuffer(32); + sb.append(ExternalLink.class.getName()); + sb.append("[dest=").append(this.destination); + sb.append(";newWindow=").append(newWindow).append("]"); + return sb.toString(); + } + } + + /** * Background trait structure. * Used for storing back trait information which are related. */ --- src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java (revision 642740) +++ src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java Sun Mar 30 13:40:39 CEST 2008 @@ -68,8 +68,6 @@ */ private static Log log = LogFactory.getLog(InlineLayoutManager.class); - private InlineLevel fobj; - private CommonMarginInline inlineProps = null; private CommonBorderPaddingBackground borderProps = null; @@ -105,7 +103,6 @@ // The node should be FObjMixed public InlineLayoutManager(InlineLevel node) { super(node); - fobj = node; } private Inline getInlineFO() { @@ -114,6 +111,8 @@ /** {@inheritDoc} */ public void initialize() { + InlineLevel fobj = (InlineLevel) this.fobj; + int padding = 0; FontInfo fi = fobj.getFOEventHandler().getFontInfo(); FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi); @@ -557,7 +556,8 @@ if (returnList instanceof BlockKnuthSequence) { return; } - CommonBorderPaddingBackground borderAndPadding = fobj.getCommonBorderPaddingBackground(); + CommonBorderPaddingBackground borderAndPadding = + ((InlineLevel)fobj).getCommonBorderPaddingBackground(); if (borderAndPadding != null) { int ipStart = borderAndPadding.getBorderStartWidth(false) + borderAndPadding.getPaddingStart(false, this); @@ -581,7 +581,8 @@ if (returnList instanceof BlockKnuthSequence) { return; } - CommonBorderPaddingBackground borderAndPadding = fobj.getCommonBorderPaddingBackground(); + CommonBorderPaddingBackground borderAndPadding = + ((InlineLevel)fobj).getCommonBorderPaddingBackground(); if (borderAndPadding != null) { int ipEnd = borderAndPadding.getBorderEndWidth(false) + borderAndPadding.getPaddingEnd(false, this); --- src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java (revision 642740) +++ src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java Sun Mar 30 16:31:17 CEST 2008 @@ -21,7 +21,7 @@ import org.apache.fop.datatypes.URISpecification; import org.apache.fop.fo.flow.BasicLink; -import org.apache.fop.layoutmgr.LayoutManager; +import org.apache.fop.fo.Constants; import org.apache.fop.layoutmgr.PageSequenceLayoutManager; import org.apache.fop.area.inline.InlineArea; import org.apache.fop.area.Trait; @@ -31,7 +31,6 @@ * LayoutManager for the fo:basic-link formatting object */ public class BasicLinkLayoutManager extends InlineLayoutManager { - private BasicLink fobj; /** * Create an fo:basic-link layout manager. @@ -40,23 +39,22 @@ */ public BasicLinkLayoutManager(BasicLink node) { super(node); - fobj = node; } /** {@inheritDoc} */ protected InlineArea createArea(boolean bInlineParent) { InlineArea area = super.createArea(bInlineParent); - setupBasicLinkArea(parentLM, area); + setupBasicLinkArea(area); return area; } /* * Detect internal or external link and add it as an area trait * - * @param parentLM the parent LayoutManager * @param area the basic-link's area */ - private void setupBasicLinkArea(LayoutManager parentLM, InlineArea area) { + private void setupBasicLinkArea(InlineArea area) { + BasicLink fobj = (BasicLink) this.fobj; // internal destinations take precedence: if (fobj.hasInternalDestination()) { String idref = fobj.getInternalDestination(); @@ -70,8 +68,10 @@ } } else if (fobj.hasExternalDestination()) { String url = URISpecification.getURL(fobj.getExternalDestination()); + boolean newWindow = (fobj.getShowDestination() == Constants.EN_NEW); if (url.length() > 0) { - area.addTrait(Trait.EXTERNAL_LINK, url); + area.addTrait(Trait.EXTERNAL_LINK, + new Trait.ExternalLink(url, newWindow)); } } } --- src/java/org/apache/fop/render/pdf/PDFRenderer.java (revision 642740) +++ src/java/org/apache/fop/render/pdf/PDFRenderer.java Sun Mar 30 15:33:42 CEST 2008 @@ -1382,14 +1382,17 @@ // no INTERNAL_LINK, look for EXTERNAL_LINK if (!linkTraitFound) { - String extDest = (String) ip.getTrait(Trait.EXTERNAL_LINK); + Trait.ExternalLink extLink = (Trait.ExternalLink) ip.getTrait(Trait.EXTERNAL_LINK); + if (extLink != null) { + String extDest = extLink.getDestination(); - if (extDest != null && extDest.length() > 0) { - linkTraitFound = true; - if (annotsAllowed) { + if (extDest != null && extDest.length() > 0) { + linkTraitFound = true; + if (annotsAllowed) { - action = factory.getExternalAction(extDest); + action = factory.getExternalAction(extDest, extLink.newWindow()); - } - } - } + } + } + } + } // warn if link trait found but not allowed, else create link if (linkTraitFound) { @@ -1618,7 +1621,7 @@ * Adds a PDF XObject (a bitmap or form) to the PDF that will later be referenced. * @param uri URL of the bitmap * @param pos Position of the bitmap - * @deprecated Use {@link @putImage(String, Rectangle2D, Map)} instead. + * @deprecated Use {@link #putImage(String, Rectangle2D, Map)} instead. */ protected void putImage(String uri, Rectangle2D pos) { putImage(uri, pos, null);