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 85b1b005a..ca6a81899 100644
--- a/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxAccessibilityHelper.java
+++ b/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxAccessibilityHelper.java
@@ -1214,7 +1214,7 @@ 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 annotation, 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.
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 df1ce8c87..a6b5fa39f 100644
--- a/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxFastLinkManager.java
+++ b/openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxFastLinkManager.java
@@ -13,21 +13,24 @@
import com.openhtmltopdf.util.XRLog;
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.PDAnnotationLink;
-import org.apache.pdfbox.pdmodel.interactive.annotation.PDBorderStyleDictionary;
+import org.apache.pdfbox.pdmodel.interactive.annotation.*;
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.nio.file.Paths;
import java.util.*;
import java.util.List;
import java.util.Map.Entry;
@@ -224,23 +227,58 @@ private void addUriAsLink(RenderingContext c, Box box, PDPage page, float pageHe
PDAnnotationLink annot = new PDAnnotationLink();
annot.setAction(action);
- if (!placeAnnotation(transform, linkShape, targetArea, annot))
+ if (!placeAnnotation(transform, linkShape, targetArea, new PDAnnotationLinkContainer(annot)))
return;
- addLinkToPage(page, annot, box, target);
+ addLinkToPage(page, new PDAnnotationLinkContainer(annot), box, target);
} else {
XRLog.general(Level.WARNING, "Could not find valid target for link. Link href = " + 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);
+
+ PDAnnotationLink annotationLink = new PDAnnotationLink();
+ PDActionURI uriAct = new PDActionURI();
+ uriAct.setURI(uri);
+ annotationLink.setAction(uriAct);
+ PDAnnotationContainer annot = new PDAnnotationLinkContainer(annotationLink);
+
+ if ("true".equals(elem.getAttribute("data-embed-file"))) {
+ byte[] file = _sharedContext.getUserAgentCallback().getBinaryResource(uri);
+ if (file != null) {
+ try {
+ PDComplexFileSpecification fs = new PDComplexFileSpecification();
+ PDEmbeddedFile embeddedFile = new PDEmbeddedFile(_od.getWriter(), new ByteArrayInputStream(file));
+ String contentType = "".equals(elem.getAttribute("data-content-type")) ? "application/octet-stream" : elem.getAttribute("data-content-type");
+ embeddedFile.setSubtype(contentType);
+ fs.setEmbeddedFile(embeddedFile);
+ String fileName = Paths.get(uri).getFileName().toString();
+ fs.setFile(fileName);
+ fs.setFileUnicode(fileName);
+ PDAnnotationFileAttachment annotationFileAttachment = new PDAnnotationFileAttachment();
+ annotationFileAttachment.setFile(fs);
+
+ // hide the pin icon used by various pdf reader for signaling an embedded file
+ PDAppearanceDictionary appearanceDictionary = new PDAppearanceDictionary();
+ PDAppearanceStream appearanceStream = new PDAppearanceStream(_od.getWriter());
+ appearanceStream.setResources(new PDResources());
+ appearanceDictionary.setNormalAppearance(appearanceStream);
+ annotationFileAttachment.setAppearance(appearanceDictionary);
+ //
+
+ annot = new PDAnnotationFileAttachmentContainer(annotationFileAttachment);
+ } catch (IOException e) {
+ XRLog.exception("Was not able to create an embedded file for embedding with uri " + uri, e);
+ }
+ } else {
+ XRLog.general("Was not able to load file from uri for embedding" + uri);
+ }
+ }
+
if (!placeAnnotation(transform, linkShape, targetArea, annot))
return;
@@ -257,9 +295,60 @@ private static boolean isURI(String uri) {
}
}
+ private interface PDAnnotationContainer {
+ default void setRectangle(PDRectangle rectangle) {getPdAnnotation().setRectangle(rectangle);};
+ default void setPrinted(boolean printed) {getPdAnnotation().setPrinted(printed);};
+ default void setQuadPoints(float[] quadPoints) {};
+
+ void setBorderStyle(PDBorderStyleDictionary styleDict);
+
+ PDAnnotation getPdAnnotation();
+ }
+
+ private static class PDAnnotationFileAttachmentContainer implements PDAnnotationContainer {
+ private final PDAnnotationFileAttachment pdAnnotationFileAttachment;
+
+ PDAnnotationFileAttachmentContainer(PDAnnotationFileAttachment pdAnnotationFileAttachment) {
+ this.pdAnnotationFileAttachment = pdAnnotationFileAttachment;
+ }
+
+ @Override
+ public PDAnnotation getPdAnnotation() {
+ return pdAnnotationFileAttachment;
+ }
+
+ @Override
+ public void setBorderStyle(PDBorderStyleDictionary styleDict) {
+ pdAnnotationFileAttachment.setBorderStyle(styleDict);
+ }
+ }
+
+ private static class PDAnnotationLinkContainer implements PDAnnotationContainer {
+ private final PDAnnotationLink pdAnnotationLink;
+
+ private 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);
+ }
+ }
+
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean placeAnnotation(AffineTransform transform, Shape linkShape, Rectangle2D targetArea,
- PDAnnotationLink annot) {
+ PDAnnotationContainer annot) {
annot.setRectangle(new PDRectangle((float) targetArea.getMinX(), (float) targetArea.getMinY(),
(float) targetArea.getWidth(), (float) targetArea.getHeight()));
@@ -376,7 +465,7 @@ 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, PDAnnotationContainer annot, Box anchor, Box target) {
PDBorderStyleDictionary styleDict = new PDBorderStyleDictionary();
styleDict.setWidth(0);
styleDict.setStyle(PDBorderStyleDictionary.STYLE_SOLID);
@@ -386,14 +475,14 @@ private void addLinkToPage(PDPage page, PDAnnotationLink annot, Box anchor, Box
List annots = page.getAnnotations();
if (annots == null) {
- annots = new ArrayList();
+ annots = new ArrayList<>();
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);