diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/util/LogMessageId.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/util/LogMessageId.java
index 4cead2564..517798c05 100644
--- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/util/LogMessageId.java
+++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/util/LogMessageId.java
@@ -111,6 +111,7 @@ enum LogMessageId1Param implements LogMessageId {
LOAD_COULD_NOT_INSTANTIATE_CUSTOM_XML_READER(XRLog.LOAD, "Could not instantiate custom XMLReader class for XML parsing: {}. " +
"Please check classpath. Use value 'default' in FS configuration if necessary. Will now try JDK default."),
LOAD_UNABLE_TO_LOAD_CSS_FROM_URI(XRLog.LOAD, "Unable to load CSS from {}"),
+ LOAD_COULD_NOT_LOAD_EMBEDDED_FILE(XRLog.LOAD, "Was not able to load an embedded file for embedding with uri {}"),
LOAD_PARSE_STYLESHEETS_TIME(XRLog.LOAD, "TIME: parse stylesheets {}ms"),
LOAD_REQUESTING_STYLESHEET_AT_URI(XRLog.LOAD, "Requesting stylesheet: {}"),
LOAD_UNRECOGNIZED_IMAGE_FORMAT_FOR_URI(XRLog.LOAD, "Unrecognized image format for: {}"),
@@ -161,7 +162,8 @@ enum LogMessageId1Param implements LogMessageId {
EXCEPTION_COULD_NOT_LOAD_FONT_FACE(XRLog.EXCEPTION, "Could not load @font-face font: {}"),
EXCEPTION_COULD_NOT_LOAD_DEFAULT_CSS(XRLog.EXCEPTION, "Can't load default CSS from {}. This file must be on your CLASSPATH. Please check before continuing."),
EXCEPTION_DEFAULT_USERAGENT_IS_NOT_ABLE_TO_RESOLVE_BASE_URL_FOR(XRLog.EXCEPTION, "The default NaiveUserAgent doesn't know how to resolve the base URL for {}"),
- EXCEPTION_FAILED_TO_LOAD_BACKGROUND_IMAGE_AT_URI(XRLog.EXCEPTION, "Failed to load background image at uri {}");
+ EXCEPTION_FAILED_TO_LOAD_BACKGROUND_IMAGE_AT_URI(XRLog.EXCEPTION, "Failed to load background image at uri {}"),
+ EXCEPTION_COULD_NOT_LOAD_EMBEDDED_FILE(XRLog.EXCEPTION, "Was not able to create an embedded file for embedding with uri {}");
private final String where;
private final String messageFormat;
diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-508-file-embed.html b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-508-file-embed.html
new file mode 100644
index 000000000..5acad59d2
--- /dev/null
+++ b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-508-file-embed.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+Embedded text
document.
+
+
diff --git a/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/nonvisualregressiontests/NonVisualRegressionTest.java b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/nonvisualregressiontests/NonVisualRegressionTest.java
index 628e7a72b..cc0c5bf60 100644
--- a/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/nonvisualregressiontests/NonVisualRegressionTest.java
+++ b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/nonvisualregressiontests/NonVisualRegressionTest.java
@@ -18,12 +18,16 @@
import java.util.stream.IntStream;
import org.apache.commons.io.FileUtils;
+import org.apache.pdfbox.cos.COSDocument;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSObject;
import org.apache.pdfbox.io.IOUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionGoTo;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationFileAttachment;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageXYZDestination;
@@ -36,9 +40,9 @@
import org.apache.pdfbox.util.Charsets;
import org.hamcrest.CustomTypeSafeMatcher;
import org.junit.Assert;
-import org.junit.Ignore;
import org.junit.Test;
+import com.openhtmltopdf.outputdevice.helper.ExternalResourceControlPriority;
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import com.openhtmltopdf.testcases.TestcaseRunner;
import com.openhtmltopdf.util.Diagnostic;
@@ -1060,6 +1064,34 @@ public void testPr489DiagnosticConsumer() throws IOException {
.allMatch(diag -> !diag.getFormattedMessage().isEmpty()));
}
+ @Test
+ public void testIssue508FileEmbed() throws IOException {
+ try (PDDocument doc = run("issue-508-file-embed",
+ builder -> {
+ // File embeds are blocked by default, allow everything.
+ builder.useExternalResourceAccessControl((uri, type) -> true, ExternalResourceControlPriority.RUN_AFTER_RESOLVING_URI);
+ builder.useExternalResourceAccessControl((uri, type) -> true, ExternalResourceControlPriority.RUN_BEFORE_RESOLVING_URI);
+ })) {
+
+ // There should be multiple file attachment annotations because the link
+ // is broken into two boxes on multiple lines.
+ assertThat(doc.getPage(0).getAnnotations().size(), equalTo(2));
+
+ PDAnnotationFileAttachment fileAttach1 = (PDAnnotationFileAttachment) doc.getPage(0).getAnnotations().get(0);
+ assertThat(fileAttach1.getFile().getFile(), equalTo("basic.css"));
+
+ PDAnnotationFileAttachment fileAttach2 = (PDAnnotationFileAttachment) doc.getPage(0).getAnnotations().get(1);
+ assertThat(fileAttach2.getFile().getFile(), equalTo("basic.css"));
+
+ try (COSDocument cosDoc = doc.getDocument()) {
+ // Make sure the file is only embedded once.
+ List files = cosDoc.getObjectsByType(COSName.FILESPEC);
+ assertThat(files.size(), equalTo(1));
+ }
+
+ remove("issue-508-file-embed", doc);
+ }
+ }
// TODO:
// + More form controls.
diff --git a/openhtmltopdf-pdfa-testing/src/test/java/com/openhtmltopdf/pdfa/testing/PdfATester.java b/openhtmltopdf-pdfa-testing/src/test/java/com/openhtmltopdf/pdfa/testing/PdfATester.java
index 3a1461a6b..1ee2f2783 100644
--- a/openhtmltopdf-pdfa-testing/src/test/java/com/openhtmltopdf/pdfa/testing/PdfATester.java
+++ b/openhtmltopdf-pdfa-testing/src/test/java/com/openhtmltopdf/pdfa/testing/PdfATester.java
@@ -28,6 +28,7 @@
import org.verapdf.pdfa.results.TestAssertion;
import org.verapdf.pdfa.results.ValidationResult;
+import com.openhtmltopdf.outputdevice.helper.ExternalResourceControlPriority;
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder.PdfAConformance;
@@ -65,7 +66,11 @@ public boolean run(String resource, PDFAFlavour flavour, PdfAConformance conform
builder.usePdfAConformance(conform);
builder.useFont(new File("target/test/artefacts/Karla-Bold.ttf"), "TestFont");
builder.withHtmlContent(html, PdfATester.class.getResource("/html/").toString());
-
+
+ // File embeds are blocked by default, allow everything.
+ builder.useExternalResourceAccessControl((uri, type) -> true, ExternalResourceControlPriority.RUN_AFTER_RESOLVING_URI);
+ builder.useExternalResourceAccessControl((uri, type) -> true, ExternalResourceControlPriority.RUN_BEFORE_RESOLVING_URI);
+
try (InputStream colorProfile = PdfATester.class.getResourceAsStream("/colorspaces/sRGB.icc")) {
byte[] colorProfileBytes = IOUtils.toByteArray(colorProfile);
builder.useColorProfile(colorProfileBytes);
@@ -128,5 +133,12 @@ public void testAllInOnePdfA2a() throws Exception {
public void testAllInOnePdfA2u() throws Exception {
assertTrue(run("all-in-one", PDFAFlavour.PDFA_2_U, PdfAConformance.PDFA_2_U));
}
-
+
+ /**
+ * File embedding is allowed as of PDF/A3.
+ */
+ @Test
+ public void testFileEmbedA3b() throws Exception {
+ assertTrue(run("file-embed", PDFAFlavour.PDFA_3_B, PdfAConformance.PDFA_3_B));
+ }
}
diff --git a/openhtmltopdf-pdfa-testing/src/test/resources/html/file-embed.html b/openhtmltopdf-pdfa-testing/src/test/resources/html/file-embed.html
new file mode 100644
index 000000000..7e7147109
--- /dev/null
+++ b/openhtmltopdf-pdfa-testing/src/test/resources/html/file-embed.html
@@ -0,0 +1,36 @@
+
+
+ File embed testcase
+
+
+
+
+
+
+
+
+
+
+
+ File embed example
+
+
+
+
+ File embed
+
+
+
+
+
+
diff --git a/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/AnnotationContainer.java b/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/AnnotationContainer.java
new file mode 100644
index 000000000..d0ba6392e
--- /dev/null
+++ b/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/AnnotationContainer.java
@@ -0,0 +1,64 @@
+package com.openhtmltopdf.pdfboxout;
+
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationFileAttachment;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDBorderStyleDictionary;
+
+public abstract class AnnotationContainer {
+ public void setRectangle(PDRectangle rectangle) {
+ getPdAnnotation().setRectangle(rectangle);
+ }
+
+ public void setPrinted(boolean printed) {
+ getPdAnnotation().setPrinted(printed);
+ }
+
+ public void setQuadPoints(float[] quadPoints) {};
+
+ public abstract void setBorderStyle(PDBorderStyleDictionary styleDict);
+
+ public abstract PDAnnotation getPdAnnotation();
+
+ public static class PDAnnotationFileAttachmentContainer extends AnnotationContainer {
+ private final PDAnnotationFileAttachment pdAnnotationFileAttachment;
+
+ public PDAnnotationFileAttachmentContainer(PDAnnotationFileAttachment pdAnnotationFileAttachment) {
+ this.pdAnnotationFileAttachment = pdAnnotationFileAttachment;
+ }
+
+ @Override
+ public PDAnnotation getPdAnnotation() {
+ return pdAnnotationFileAttachment;
+ }
+
+ @Override
+ public void setBorderStyle(PDBorderStyleDictionary styleDict) {
+ pdAnnotationFileAttachment.setBorderStyle(styleDict);
+ }
+ }
+
+ public static class PDAnnotationLinkContainer extends AnnotationContainer {
+ private final PDAnnotationLink pdAnnotationLink;
+
+ public PDAnnotationLinkContainer(PDAnnotationLink pdAnnotationLink) {
+ this.pdAnnotationLink = pdAnnotationLink;
+ }
+
+ @Override
+ public PDAnnotation getPdAnnotation() {
+ return pdAnnotationLink;
+ }
+
+ @Override
+ public void setQuadPoints(float[] quadPoints) {
+ pdAnnotationLink.setQuadPoints(quadPoints);
+ }
+
+ @Override
+ public void setBorderStyle(PDBorderStyleDictionary styleDict) {
+ pdAnnotationLink.setBorderStyle(styleDict);
+ }
+ }
+}
diff --git a/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxAccessibilityHelper.java b/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxAccessibilityHelper.java
index 0ad3c4be9..6f3944b5f 100644
--- a/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxAccessibilityHelper.java
+++ b/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxAccessibilityHelper.java
@@ -1210,17 +1210,17 @@ private static class AnnotationWithStructureParent {
PDAnnotation annotation;
}
- public void addLink(Box anchor, Box target, PDAnnotationLink annotation, PDPage page) {
+ public void addLink(Box anchor, Box target, PDAnnotation pdAnnotation, PDPage page) {
PDStructureElement struct = getStructualElementForBox(anchor);
if (struct != null) {
// We have to append the link annotationobject reference as a kid of its associated structure element.
PDObjectReference ref = new PDObjectReference();
- ref.setReferencedObject(annotation);
+ ref.setReferencedObject(pdAnnotation);
struct.appendKid(ref);
// We also need to save the pair so we can add it to the number tree for reverse lookup.
AnnotationWithStructureParent annotStructParentPair = new AnnotationWithStructureParent();
- annotStructParentPair.annotation = annotation;
+ annotStructParentPair.annotation = pdAnnotation;
annotStructParentPair.structureParent = struct;
_pageItems._pageAnnotations.add(annotStructParentPair);
diff --git a/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxFastLinkManager.java b/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxFastLinkManager.java
index bde829b29..ce07f0bb3 100644
--- a/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxFastLinkManager.java
+++ b/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxFastLinkManager.java
@@ -3,6 +3,7 @@
import com.openhtmltopdf.extend.NamespaceHandler;
import com.openhtmltopdf.extend.ReplacedElement;
import com.openhtmltopdf.layout.SharedContext;
+import com.openhtmltopdf.outputdevice.helper.ExternalResourceType;
import com.openhtmltopdf.pdfboxout.PdfBoxLinkManager.IPdfBoxElementWithShapedLinks;
import com.openhtmltopdf.pdfboxout.quads.KongAlgo;
import com.openhtmltopdf.pdfboxout.quads.Triangle;
@@ -13,20 +14,29 @@
import com.openhtmltopdf.util.LogMessageId;
import com.openhtmltopdf.util.XRLog;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
+import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
import org.apache.pdfbox.pdmodel.interactive.action.PDAction;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionGoTo;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationFileAttachment;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDBorderStyleDictionary;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDPageXYZDestination;
import org.w3c.dom.Element;
import java.awt.*;
import java.awt.geom.*;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URI;
import java.util.*;
@@ -42,7 +52,19 @@ public class PdfBoxFastLinkManager {
private final Box _root;
private final PdfBoxFastOutputDevice _od;
private final List _links;
- private PdfBoxAccessibilityHelper _pdfUa;
+ private PdfBoxAccessibilityHelper _pdfUa;
+
+ /**
+ * A map from uri to embedded file, so we don't embed files twice
+ * in case of a split link (example, two link boxes are formed when
+ * a link breaks in the middle).
+ */
+ private final Map _embeddedFiles;
+
+ /**
+ * The lazily created appearance dict for emedded files.
+ */
+ private PDAppearanceDictionary _embeddedFileAppearance;
public PdfBoxFastLinkManager(SharedContext ctx, float dotsPerPoint, Box root, PdfBoxFastOutputDevice od) {
this._sharedContext = ctx;
@@ -51,6 +73,7 @@ public PdfBoxFastLinkManager(SharedContext ctx, float dotsPerPoint, Box root, Pd
this._od = od;
this._linkTargetAreas = new HashMap<>();
this._links = new ArrayList<>();
+ this._embeddedFiles = new HashMap<>();
}
private Rectangle2D calcTotalLinkArea(RenderingContext c, Box box, float pageHeight, AffineTransform transform) {
@@ -225,29 +248,152 @@ private void addUriAsLink(RenderingContext c, Box box, PDPage page, float pageHe
PDAnnotationLink annot = new PDAnnotationLink();
annot.setAction(action);
- if (!placeAnnotation(transform, linkShape, targetArea, annot))
+
+ AnnotationContainer annotContainer = new AnnotationContainer.PDAnnotationLinkContainer(annot);
+
+ if (!placeAnnotation(transform, linkShape, targetArea, annotContainer))
return;
- addLinkToPage(page, annot, box, target);
+ addLinkToPage(page, annotContainer, box, target);
} else {
XRLog.log(Level.WARNING, LogMessageId.LogMessageId1Param.GENERAL_PDF_COULD_NOT_FIND_VALID_TARGET_FOR_LINK, uri);
}
} else if (isURI(uri)) {
- PDActionURI uriAct = new PDActionURI();
- uriAct.setURI(uri);
-
- Rectangle2D targetArea = checkLinkArea(page, c, box, pageHeight, transform, linkShape);
- if (targetArea == null) {
- return;
- }
- PDAnnotationLink annot = new PDAnnotationLink();
- annot.setAction(uriAct);
- if (!placeAnnotation(transform, linkShape, targetArea, annot))
- return;
-
- addLinkToPage(page, annot, box, null);
- }
- }
+ AnnotationContainer annotContainer = null;
+
+ if (!elem.hasAttribute("download")) {
+ PDActionURI uriAct = new PDActionURI();
+ uriAct.setURI(uri);
+
+ PDAnnotationLink annot = new PDAnnotationLink();
+ annot.setAction(uriAct);
+
+ annotContainer = new AnnotationContainer.PDAnnotationLinkContainer(annot);
+ } else {
+ annotContainer = createFileEmbedLinkAnnotation(elem, uri);
+ }
+
+ if (annotContainer != null) {
+ Rectangle2D targetArea = checkLinkArea(page, c, box, pageHeight, transform, linkShape);
+
+ if (targetArea == null) {
+ return;
+ }
+
+ if (!placeAnnotation(transform, linkShape, targetArea, annotContainer)) {
+ return;
+ }
+
+ addLinkToPage(page, annotContainer, box, null);
+ }
+ }
+ }
+
+ /**
+ * Create a file attachment link, being careful not to embed the same
+ * file (as specified by uri) more than once.
+ *
+ * The element should have the following attributes:
+ * download="embedded-filename.ext",
+ * data-content-type="file-mime-type" which
+ * defaults to "application/octet-stream",
+ * relationship (required for PDF/A3), one of:
+ * "Source", "Supplement", "Data", "Alternative", "Unspecified",
+ * title="file description" (recommended for PDF/A3).
+ */
+ private AnnotationContainer createFileEmbedLinkAnnotation(
+ Element elem, String uri) {
+ PDComplexFileSpecification fs = _embeddedFiles.get(uri);
+
+ if (fs != null) {
+ PDAnnotationFileAttachment annotationFileAttachment = new PDAnnotationFileAttachment();
+
+ annotationFileAttachment.setFile(fs);
+ annotationFileAttachment.setAppearance(this._embeddedFileAppearance);
+
+ return new AnnotationContainer.PDAnnotationFileAttachmentContainer(annotationFileAttachment);
+ }
+
+ byte[] file = _sharedContext.getUserAgentCallback().getBinaryResource(uri, ExternalResourceType.FILE_EMBED);
+
+ if (file != null) {
+ try {
+ String contentType = elem.getAttribute("data-content-type").isEmpty() ?
+ "application/octet-stream" :
+ elem.getAttribute("data-content-type");
+
+ PDEmbeddedFile embeddedFile = new PDEmbeddedFile(_od.getWriter(), new ByteArrayInputStream(file));
+ embeddedFile.setSubtype(contentType);
+ embeddedFile.setSize(file.length);
+
+ // PDF/A3 requires a mod date for the file.
+ if (elem.hasAttribute("relationship")) {
+ // FIXME: Should we make this specifiable.
+ embeddedFile.setModDate(Calendar.getInstance());
+ }
+
+ String fileName = elem.getAttribute("download");
+
+ fs = new PDComplexFileSpecification();
+ fs.setEmbeddedFile(embeddedFile);
+ fs.setFile(fileName);
+ fs.setFileUnicode(fileName);
+
+ // The PDF/A3 standard requires one to specify the relationship
+ // this embedded file has to the link annotation.
+ if (elem.hasAttribute("relationship") &&
+ Arrays.asList("Source", "Supplement", "Data", "Alternative", "Unspecified")
+ .contains(elem.getAttribute("relationship"))) {
+ fs.getCOSObject().setItem(
+ COSName.getPDFName("AFRelationship"),
+ COSName.getPDFName(elem.getAttribute("relationship")));
+ }
+
+ if (elem.hasAttribute("title")) {
+ fs.setFileDescription(elem.getAttribute("title"));
+ }
+
+ this._embeddedFiles.put(uri, fs);
+
+ if (this._embeddedFileAppearance == null) {
+ this._embeddedFileAppearance = createFileEmbedLinkAppearance();
+ }
+
+ PDAnnotationFileAttachment annotationFileAttachment = new PDAnnotationFileAttachment();
+
+ annotationFileAttachment.setFile(fs);
+ annotationFileAttachment.setAppearance(this._embeddedFileAppearance);
+
+ // PDF/A3 requires we explicitly list this link as associated with file.
+ if (elem.hasAttribute("relationship")) {
+ COSArray fileRefArray = new COSArray();
+ fileRefArray.add(fs);
+
+ annotationFileAttachment.getCOSObject().setItem(COSName.getPDFName("AF"), fileRefArray);
+ }
+
+ return new AnnotationContainer.PDAnnotationFileAttachmentContainer(annotationFileAttachment);
+ } catch (IOException e) {
+ XRLog.log(Level.WARNING, LogMessageId.LogMessageId1Param.EXCEPTION_COULD_NOT_LOAD_EMBEDDED_FILE, uri, e);
+ }
+ } else {
+ XRLog.log(Level.WARNING, LogMessageId.LogMessageId1Param.LOAD_COULD_NOT_LOAD_EMBEDDED_FILE, uri);
+ }
+
+ return null;
+ }
+
+ /**
+ * Create an empty appearance stream to
+ * hide the pin icon used by various pdf reader for signaling an embedded file
+ */
+ private PDAppearanceDictionary createFileEmbedLinkAppearance() {
+ PDAppearanceDictionary appearanceDictionary = new PDAppearanceDictionary();
+ PDAppearanceStream appearanceStream = new PDAppearanceStream(_od.getWriter());
+ appearanceStream.setResources(new PDResources());
+ appearanceDictionary.setNormalAppearance(appearanceStream);
+ return appearanceDictionary;
+ }
private static boolean isURI(String uri) {
try {
@@ -260,7 +406,7 @@ private static boolean isURI(String uri) {
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean placeAnnotation(AffineTransform transform, Shape linkShape, Rectangle2D targetArea,
- PDAnnotationLink annot) {
+ AnnotationContainer annot) {
annot.setRectangle(new PDRectangle((float) targetArea.getMinX(), (float) targetArea.getMinY(),
(float) targetArea.getWidth(), (float) targetArea.getHeight()));
@@ -377,7 +523,8 @@ static QuadPointShape mapShapeToQuadPoints(AffineTransform transform, Shape link
return result;
}
- private void addLinkToPage(PDPage page, PDAnnotationLink annot, Box anchor, Box target) {
+ private void addLinkToPage(
+ PDPage page, AnnotationContainer annot, Box anchor, Box target) {
PDBorderStyleDictionary styleDict = new PDBorderStyleDictionary();
styleDict.setWidth(0);
styleDict.setStyle(PDBorderStyleDictionary.STYLE_SOLID);
@@ -391,10 +538,10 @@ private void addLinkToPage(PDPage page, PDAnnotationLink annot, Box anchor, Box
page.setAnnotations(annots);
}
- annots.add(annot);
+ annots.add(annot.getPdAnnotation());
if (_pdfUa != null) {
- _pdfUa.addLink(anchor, target, annot, page);
+ _pdfUa.addLink(anchor, target, annot.getPdAnnotation(), page);
}
} catch (IOException e) {
throw new PdfContentStreamAdapter.PdfException("processLink", e);