Skip to content

Commit

Permalink
Merge pull request #640 from danfickle/file_embeds_for_508
Browse files Browse the repository at this point in the history
Implementation of file-embed link. Fixes #508, fixes #636.
  • Loading branch information
danfickle authored Feb 1, 2021
2 parents d20825b + 9d890e0 commit c808b63
Show file tree
Hide file tree
Showing 8 changed files with 334 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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: {}"),
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<html>
<head>
<style>
@page {
size: 200px 200px;
}
</style>
</head>
<body>
Embedded <a href="stylesheets/basic.css" download="basic.css" data-content-type="text/plain">text <br/> document</a>.
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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<COSObject> files = cosDoc.getObjectsByType(COSName.FILESPEC);
assertThat(files.size(), equalTo(1));
}

remove("issue-508-file-embed", doc);
}
}

// TODO:
// + More form controls.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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));
}
}
36 changes: 36 additions & 0 deletions openhtmltopdf-pdfa-testing/src/test/resources/html/file-embed.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<html lang="EN-US">
<head>
<title>File embed testcase</title>
<meta name="subject" content="PDF/A3 file embed"/>
<meta name="author" content="openhtmltopdf.com team"/>
<meta name="description" content="An example for file embed"/>

<bookmarks>
<bookmark name="File embed" href="#file"/>
</bookmarks>

<style>
body {
margin: 0;
font-family: 'TestFont'; /* Font provided with builder. */
font-size: 15px;
}
</style>
</head>
<body>
<h1 id="file">File embed example</h1>

<p>
<a href="file-embed.html"
download="source.html"
data-content-type="text/html"
title="File embedded"
relationship="Source">

File embed

</a>
</p>

</body>
</html>
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit c808b63

Please # to comment.