diff options
author | Simon Steiner <ssteiner@apache.org> | 2024-12-04 16:32:26 +0000 |
---|---|---|
committer | Simon Steiner <ssteiner@apache.org> | 2024-12-04 16:32:26 +0000 |
commit | fd084368f8c81c6a6b5aa4e94d443f4f0ad6e5ad (patch) | |
tree | 273d29aa28468a9bbcf9945a715848a85a015fcf | |
parent | 2f1858567a8d68bd9c09eb33955d2cbae2aa03ac (diff) | |
download | xmlgraphics-fop-fd084368f8c81c6a6b5aa4e94d443f4f0ad6e5ad.tar.gz xmlgraphics-fop-fd084368f8c81c6a6b5aa4e94d443f4f0ad6e5ad.zip |
FOP-3224: Add alt text to link dictionary
14 files changed, 112 insertions, 26 deletions
diff --git a/fop-core/src/main/java/org/apache/fop/area/Trait.java b/fop-core/src/main/java/org/apache/fop/area/Trait.java index 445beedd3..5f846f3b4 100644 --- a/fop-core/src/main/java/org/apache/fop/area/Trait.java +++ b/fop-core/src/main/java/org/apache/fop/area/Trait.java @@ -420,6 +420,7 @@ public final class Trait implements Serializable { private String destination; private boolean newWindow; + private String altText; /** * Constructs an ExternalLink object with the given destination @@ -427,9 +428,10 @@ public final class Trait implements Serializable { * @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) { + public ExternalLink(String destination, boolean newWindow, String altText) { this.destination = destination; this.newWindow = newWindow; + this.altText = altText; } /** @@ -452,7 +454,7 @@ public final class Trait implements Serializable { "Malformed trait value for Trait.ExternalLink: " + traitValue); } } - return new ExternalLink(dest, newWindow); + return new ExternalLink(dest, newWindow, null); } /** @@ -483,6 +485,10 @@ public final class Trait implements Serializable { sb.append(",dest=").append(this.destination); return sb.toString(); } + + public String getAltText() { + return altText; + } } /** diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java index 3a630f9be..76cda73e3 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java @@ -81,7 +81,7 @@ public class BasicLinkLayoutManager extends InlineLayoutManager { boolean newWindow = (fobj.getShowDestination() == Constants.EN_NEW); if (url.length() > 0) { area.addTrait(Trait.EXTERNAL_LINK, - new Trait.ExternalLink(url, newWindow)); + new Trait.ExternalLink(url, newWindow, fobj.getAltText())); } } } diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java index 12a6c3e09..16b1e80bc 100644 --- a/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java +++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFFactory.java @@ -556,7 +556,7 @@ public class PDFFactory { PDFLink link = new PDFLink(rect); if (linkType == PDFLink.EXTERNAL) { - link.setAction(getExternalAction(destination, false)); + link.setAction(getExternalAction(destination, false, null)); } else { // linkType is internal String goToReference = getGoToReference(destination, yoffset); @@ -583,7 +583,7 @@ public class PDFFactory { * displayed in a new window * @return the PDFAction thus created or found */ - public PDFAction getExternalAction(String target, boolean newWindow) { + public PDFAction getExternalAction(String target, boolean newWindow, String altText) { URI uri = getTargetUri(target); if (uri != null) { String scheme = uri.getScheme(); @@ -595,7 +595,7 @@ public class PDFFactory { scheme = "file"; } if (scheme == null) { - return new PDFUri(uri.toASCIIString()); + return new PDFUri(uri.toASCIIString(), altText); } else if (scheme.equalsIgnoreCase("embedded-file")) { return getActionForEmbeddedFile(filename, newWindow); } else if (scheme.equalsIgnoreCase("file")) { @@ -619,16 +619,16 @@ public class PDFFactory { return getGoToPDFAction(filename, dest, page, newWindow); } else { if (uri.getQuery() != null || uri.getFragment() != null) { - return new PDFUri(uri.toASCIIString()); + return new PDFUri(uri.toASCIIString(), altText); } else { return getLaunchAction(filename, newWindow); } } } else { - return new PDFUri(uri.toASCIIString()); + return new PDFUri(uri.toASCIIString(), altText); } } - return new PDFUri(target); + return new PDFUri(target, altText); } private URI getTargetUri(String target) { diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFLink.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFLink.java index 21e989999..e0426a454 100644 --- a/fop-core/src/main/java/org/apache/fop/pdf/PDFLink.java +++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFLink.java @@ -93,15 +93,21 @@ public class PDFLink extends PDFObject { f |= 1 << (5 - 1); //NoRotate, bit 5 fFlag = "/F " + f; } - String s = "<< /Type /Annot\n" + "/Subtype /Link\n" + "/Rect [ " + String dict = "<< /Type /Annot\n" + "/Subtype /Link\n" + "/Rect [ " + (ulx) + " " + (uly) + " " + (brx) + " " + (bry) + " ]\n" + "/C [ " + this.color + " ]\n" + "/Border [ 0 0 0 ]\n" + "/A " + this.action.getAction() + "\n" + "/H /I\n" + (this.structParent != null - ? "/StructParent " + this.structParent.toString() + "\n" : "") - + fFlag + "\n>>"; - return s; + ? "/StructParent " + this.structParent.toString() + "\n" : ""); + if (action instanceof PDFUri) { + String altText = ((PDFUri) action).getAltText(); + if (altText != null && !altText.isEmpty()) { + dict += "/Contents " + PDFText.escapeText(altText) + "\n"; + } + } + dict += fFlag + "\n>>"; + return dict; } /* diff --git a/fop-core/src/main/java/org/apache/fop/pdf/PDFUri.java b/fop-core/src/main/java/org/apache/fop/pdf/PDFUri.java index a4c75ee10..686ab1d84 100644 --- a/fop-core/src/main/java/org/apache/fop/pdf/PDFUri.java +++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFUri.java @@ -25,14 +25,16 @@ package org.apache.fop.pdf; public class PDFUri extends PDFAction { private String uri; + private String altText; /** * create a Uri instance. * * @param uri the uri to which the link should point */ - public PDFUri(String uri) { + public PDFUri(String uri, String altText) { this.uri = uri; + this.altText = altText; } /** @@ -57,4 +59,8 @@ public class PDFUri extends PDFAction { //TODO Convert this class into a dictionary return getDictString(); } + + public String getAltText() { + return altText; + } } diff --git a/fop-core/src/main/java/org/apache/fop/render/intermediate/IFRenderer.java b/fop-core/src/main/java/org/apache/fop/render/intermediate/IFRenderer.java index 84e4e3a2c..d4a25ad0b 100644 --- a/fop-core/src/main/java/org/apache/fop/render/intermediate/IFRenderer.java +++ b/fop-core/src/main/java/org/apache/fop/render/intermediate/IFRenderer.java @@ -965,7 +965,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { String extDest = extLink.getDestination(); if (extDest != null && extDest.length() > 0) { linkTraitFound = true; - action = new URIAction(extDest, extLink.newWindow()); + action = new URIAction(extDest, extLink.newWindow(), extLink.getAltText()); action = actionSet.put(action); } } diff --git a/fop-core/src/main/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java b/fop-core/src/main/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java index f654ef37e..0abcd8acd 100644 --- a/fop-core/src/main/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java +++ b/fop-core/src/main/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java @@ -165,8 +165,9 @@ public class DocumentNavigationHandler extends DefaultHandler String id = attributes.getValue("id"); String gotoURI = attributes.getValue("uri"); String showDestination = attributes.getValue("show-destination"); + String altText = attributes.getValue("alt-text"); boolean newWindow = "new".equals(showDestination); - URIAction action = new URIAction(gotoURI, newWindow); + URIAction action = new URIAction(gotoURI, newWindow, altText); if (id != null) { action.setID(id); } diff --git a/fop-core/src/main/java/org/apache/fop/render/intermediate/extensions/URIAction.java b/fop-core/src/main/java/org/apache/fop/render/intermediate/extensions/URIAction.java index 774f27b43..9a4960bb9 100644 --- a/fop-core/src/main/java/org/apache/fop/render/intermediate/extensions/URIAction.java +++ b/fop-core/src/main/java/org/apache/fop/render/intermediate/extensions/URIAction.java @@ -33,18 +33,20 @@ public class URIAction extends AbstractAction implements DocumentNavigationExten private String uri; private boolean newWindow; + private String altText; /** * Creates a new instance. * @param uri the target URI * @param newWindow true if the link should be opened in a new window */ - public URIAction(String uri, boolean newWindow) { + public URIAction(String uri, boolean newWindow, String altText) { if (uri == null) { throw new NullPointerException("uri must not be null"); } this.uri = uri; this.newWindow = newWindow; + this.altText = altText; setID(getIDPrefix() + (uri + newWindow).hashCode()); } @@ -97,6 +99,9 @@ public class URIAction extends AbstractAction implements DocumentNavigationExten atts.addAttribute("", "id", "id", XMLUtil.CDATA, getID()); } atts.addAttribute("", "uri", "uri", XMLUtil.CDATA, getURI()); + if (altText != null && !altText.isEmpty()) { + atts.addAttribute("", "alt-text", "alt-text", XMLUtil.CDATA, altText); + } if (isNewWindow()) { atts.addAttribute("", "show-destination", "show-destination", XMLUtil.CDATA, "new"); } @@ -106,4 +111,7 @@ public class URIAction extends AbstractAction implements DocumentNavigationExten GOTO_URI.getLocalName(), GOTO_URI.getQName()); } + public String getAltText() { + return altText; + } } diff --git a/fop-core/src/main/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java b/fop-core/src/main/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java index 552139980..657d81899 100644 --- a/fop-core/src/main/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java +++ b/fop-core/src/main/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java @@ -171,7 +171,7 @@ public class PDFDocumentNavigationHandler implements IFDocumentNavigationHandler assert u.isComplete(); String uri = u.getURI(); PDFFactory factory = getPDFDoc().getFactory(); - pdfAction = factory.getExternalAction(uri, u.isNewWindow()); + pdfAction = factory.getExternalAction(uri, u.isNewWindow(), u.getAltText()); if (!pdfAction.hasObjectNumber()) { //Some PDF actions are pooled getPDFDoc().registerObject(pdfAction); diff --git a/fop-core/src/test/java/org/apache/fop/pdf/PDFAttachmentTestCase.java b/fop-core/src/test/java/org/apache/fop/pdf/PDFAttachmentTestCase.java index 2d7e659e3..a690a9d84 100644 --- a/fop-core/src/test/java/org/apache/fop/pdf/PDFAttachmentTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFAttachmentTestCase.java @@ -53,7 +53,7 @@ public class PDFAttachmentTestCase { docHandler.startPage(0, "", "", new Dimension()); docHandler.handleExtensionObject(new PDFEmbeddedFileAttachment("filename", "src", "desc")); docHandler.getDocumentNavigationHandler().renderLink(new Link( - new URIAction("embedded-file:filename", false), new Rectangle())); + new URIAction("embedded-file:filename", false, null), new Rectangle())); docHandler.endDocument(); Assert.assertTrue(out.toString().contains( "<<\n /Type /Filespec\n /F (filename)\n /UF (filename)\n /AFRelationship /Data")); @@ -76,7 +76,7 @@ public class PDFAttachmentTestCase { "src", "desc"); docHandler.handleExtensionObject(fileAtt); docHandler.getDocumentNavigationHandler().renderLink(new Link( - new URIAction("embedded-file:" + unicodeFilename, false), new Rectangle())); + new URIAction("embedded-file:" + unicodeFilename, false, null), new Rectangle())); docHandler.endDocument(); Assert.assertTrue(out.toString().contains( "<<\n /Type /Filespec\n /F (" + fileAtt.getFilename() + ")\n /UF " @@ -99,7 +99,7 @@ public class PDFAttachmentTestCase { "src", "desc"); docHandler.handleExtensionObject(fileAtt); docHandler.getDocumentNavigationHandler().renderLink(new Link( - new URIAction("embedded-file:" + unicodeFilename, false), new Rectangle())); + new URIAction("embedded-file:" + unicodeFilename, false, null), new Rectangle())); docHandler.endDocument(); Assert.assertTrue(out.toString().contains( "<<\n /Type /Filespec\n /F (t\\(st)\n /UF (t\\(st)\n /AFRelationship /Data")); diff --git a/fop-core/src/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java b/fop-core/src/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java index 14745cdee..9aeef7807 100644 --- a/fop-core/src/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java @@ -259,7 +259,7 @@ public class PDFFactoryTestCase { PDFFactory pdfFactory = new PDFFactory(doc); String target = "embedded-file:" + unicodeFilename; PDFJavaScriptLaunchAction pdfAction = (PDFJavaScriptLaunchAction) - pdfFactory.getExternalAction(target, false); + pdfFactory.getExternalAction(target, false, null); String expectedString = "<<\n/S /JavaScript\n/JS (this.exportDataObject\\({cName:\"" + fileSpec.getFilename() + "\", nLaunch:2}\\);)\n>>"; @@ -281,4 +281,16 @@ public class PDFFactoryTestCase { assertEquals(expectedString, link.toPDFString()); } + + @Test + public void testLinkAltText() throws IOException { + PDFDocument doc = new PDFDocument(""); + PDFFactory pdfFactory = new PDFFactory(doc); + PDFAction action = pdfFactory.getExternalAction("a", false, "b"); + PDFLink link = pdfFactory.makeLink(new Rectangle(), "a", 0, 0); + link.setAction(action); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + link.output(bos); + assertTrue(bos.toString().contains("/Contents (b)")); + } } diff --git a/fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java b/fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java index 988a14668..2d2e8110c 100644 --- a/fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/pdf/PDFLinkTestCase.java @@ -413,7 +413,7 @@ public class PDFLinkTestCase { docHandler.startDocument(); docHandler.startPage(0, "", "", new Dimension()); docHandler.getDocumentNavigationHandler().renderLink(new Link( - new URIAction(target, false), new Rectangle())); + new URIAction(target, false, null), new Rectangle())); docHandler.endDocument(); // Normalize spaces between word for easier testing diff --git a/fop-core/src/test/java/org/apache/fop/render/intermediate/URIActionTestCase.java b/fop-core/src/test/java/org/apache/fop/render/intermediate/URIActionTestCase.java index d6c30d4c7..42675f8a0 100644 --- a/fop-core/src/test/java/org/apache/fop/render/intermediate/URIActionTestCase.java +++ b/fop-core/src/test/java/org/apache/fop/render/intermediate/URIActionTestCase.java @@ -26,9 +26,9 @@ import org.apache.fop.render.intermediate.extensions.URIAction; public class URIActionTestCase { @Test public void testID() { - URIAction action = new URIAction("uri", true); - URIAction action2 = new URIAction("uri", true); - URIAction action3 = new URIAction("uri2", true); + URIAction action = new URIAction("uri", true, null); + URIAction action2 = new URIAction("uri", true, null); + URIAction action3 = new URIAction("uri2", true, null); Assert.assertEquals(action.getID(), action2.getID()); Assert.assertFalse(action.getID().equals(action3.getID())); } diff --git a/fop/test/intermediate/basic-link_alttext.xml b/fop/test/intermediate/basic-link_alttext.xml new file mode 100644 index 000000000..2ae776db6 --- /dev/null +++ b/fop/test/intermediate/basic-link_alttext.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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$ --> +<testcase> + <info> + <p> + This test checks a fo:basic-link in a span + </p> + </info> + <cfg> + <accessibility>true</accessibility> + </cfg> + <fo> + <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> + <fo:layout-master-set> + <fo:simple-page-master page-width="8.5in" page-height="11in" master-name="Page"> + <fo:region-body region-name="Body"/> + </fo:simple-page-master> + </fo:layout-master-set> + <fo:page-sequence master-reference="Page"> + <fo:flow flow-name="Body"> + <fo:block> + <fo:basic-link external-destination="www.a.com" fox:alt-text="xxx">www.a.com</fo:basic-link> + </fo:block> + </fo:flow> + </fo:page-sequence> + </fo:root> + </fo> + <if-checks xmlns:nav="http://xmlgraphics.apache.org/fop/intermediate/document-navigation"> + <eval expected="xxx" xpath="//nav:link/nav:goto-uri/@alt-text"/> + </if-checks> +</testcase> |