Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cherry-pick from PR #143: pdf/a support #250

Merged
merged 1 commit into from
Jul 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
import com.openhtmltopdf.render.PageBox;
import com.openhtmltopdf.render.RenderingContext;
import com.openhtmltopdf.render.ViewportBox;
import com.openhtmltopdf.render.AbstractOutputDevice.ClipInfo;
import com.openhtmltopdf.render.displaylist.DisplayListCollector;
import com.openhtmltopdf.render.displaylist.DisplayListContainer;
import com.openhtmltopdf.render.displaylist.DisplayListPainter;
Expand All @@ -49,14 +48,20 @@
import com.openhtmltopdf.util.Configuration;
import com.openhtmltopdf.util.ThreadCtx;
import com.openhtmltopdf.util.XRLog;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
import org.apache.pdfbox.pdmodel.common.PDMetadata;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDMarkInfo;
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot;
import org.apache.pdfbox.pdmodel.encryption.PDEncryption;
import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent;
import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.schema.AdobePDFSchema;
import org.apache.xmpbox.schema.PDFAIdentificationSchema;
import org.apache.xmpbox.schema.XMPBasicSchema;
import org.apache.xmpbox.type.BadFieldValueException;
import org.apache.xmpbox.xml.XmpSerializer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
Expand Down Expand Up @@ -97,7 +102,11 @@ public class PdfBoxRenderer implements Closeable {

// Usually 1.7
private float _pdfVersion;


private String _pdfAConformance;

private byte[] _colorProfile;

private boolean _testMode;

private PDFCreationListener _listener;
Expand All @@ -122,8 +131,13 @@ public class PdfBoxRenderer implements Closeable {

_producer = state._producer;


_svgImpl = state._svgImpl;
_mathmlImpl = state._mathmlImpl;

_pdfAConformance = state._pdfAConformance;
_colorProfile = state._colorProfile;

_dotsPerPoint = DEFAULT_DOTS_PER_POINT;
_testMode = state._testMode;
_useFastMode = state._useFastRenderer;
Expand Down Expand Up @@ -621,7 +635,11 @@ private void writePDF(List<PageBox> pages, RenderingContext c, Rectangle2D first
c.setPageCount(pageCount);
firePreWrite(pageCount); // opportunity to adjust meta data
setDidValues(doc); // set PDF header fields from meta data


if (_pdfAConformance != null) {
addPdfASchema(doc, _pdfAConformance);
}

for (int i = 0; i < pageCount; i++) {
PageBox currentPage = pages.get(i);

Expand All @@ -643,10 +661,63 @@ private void writePDF(List<PageBox> pages, RenderingContext c, Rectangle2D first
_outputDevice.finish(c, _root);
}

private void addPdfASchema(PDDocument document, String conformance) {
PDDocumentInformation information = document.getDocumentInformation();
XMPMetadata metadata = XMPMetadata.createXMPMetadata();

try {
PDFAIdentificationSchema pdfaid = metadata.createAndAddPFAIdentificationSchema();
pdfaid.setConformance(conformance);
pdfaid.setPart(1);

AdobePDFSchema pdfSchema = metadata.createAndAddAdobePDFSchema();
pdfSchema.setProducer(information.getProducer());

XMPBasicSchema xmpBasicSchema = metadata.createAndAddXMPBasicSchema();
xmpBasicSchema.setCreateDate(information.getCreationDate());

PDMetadata metadataStream = new PDMetadata(document);
PDMarkInfo markInfo = new PDMarkInfo();
markInfo.setMarked(true);

// add to catalog
PDDocumentCatalog catalog = document.getDocumentCatalog();
catalog.setMetadata(metadataStream);
// for pdf/a-1 compliance, add the StructTreeRoot that https://www.pdf-online.com/osa/validate.aspx was
// complaining. Based on https://stackoverflow.com/a/46806392
catalog.setStructureTreeRoot(new PDStructureTreeRoot());
//

catalog.setMarkInfo(markInfo);

XmpSerializer serializer = new XmpSerializer();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
serializer.serialize(metadata, baos, true);
metadataStream.importXMPMetadata( baos.toByteArray() );


if (_colorProfile != null) {
ByteArrayInputStream colorProfile = new ByteArrayInputStream(_colorProfile);
PDOutputIntent oi = new PDOutputIntent(document, colorProfile);
oi.setInfo("sRGB IEC61966-2.1");
oi.setOutputCondition("sRGB IEC61966-2.1");
oi.setOutputConditionIdentifier("sRGB IEC61966-2.1");
oi.setRegistryName("http://www.color.org");
catalog.addOutputIntent(oi);
}
} catch (BadFieldValueException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (TransformerException e) {
throw new RuntimeException(e);
}
}

// Sets the document information dictionary values from html metadata
private void setDidValues(PDDocument doc) {
PDDocumentInformation info = new PDDocumentInformation();

info.setCreationDate(Calendar.getInstance());

if (_producer == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,30 @@ public PdfRendererBuilder usePdfVersion(float version) {
state._pdfVersion = version;
return this;
}

/**
* Set the PDF/A conformance, typically we use PDF/A-1
*
* @param pdfAConformance
* @return
*/
public PdfRendererBuilder usePdfAConformance(PdfAConformance pdfAConformance) {
this.state._pdfAConformance = pdfAConformance.value;
return this;
}

/**
* Sets the color profile, needed for PDF/A conformance.
*
* You can use the sRGB.icc from https://svn.apache.org/viewvc/pdfbox/trunk/examples/src/main/resources/org/apache/pdfbox/resources/pdfa/
*
* @param colorProfile
* @return
*/
public PdfRendererBuilder useColorProfile(byte[] colorProfile) {
this.state._colorProfile = colorProfile;
return this;
}

/**
* By default, this project creates an entirely in-memory <code>PDDocument</code>.
Expand Down Expand Up @@ -214,5 +238,21 @@ private AddedFont(FSSupplier<InputStream> supplier, File fontFile, Integer weigh
this.style = style;
}
}

/**
* Various level of PDF/A conformance:
*
* PDF/A-1, PDF/A-2 and PDF/A-3
*/
public enum PdfAConformance {

PDF_A_1("A"), PDF_A_2("B"), PDF_A_3("U");

PdfAConformance(String value) {
this.value = value;
}

private final String value;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ public class PdfRendererBuilderState extends BaseRendererBuilder.BaseRendererBui
public float _pdfVersion = 1.7f;
public String _producer;
public PDDocument pddocument;
public String _pdfAConformance;
public byte[] _colorProfile;
}