diff options
author | Simon Steiner <ssteiner@apache.org> | 2015-03-12 10:13:48 +0000 |
---|---|---|
committer | Simon Steiner <ssteiner@apache.org> | 2015-03-12 10:13:48 +0000 |
commit | de3950df5e27c9e8a7901a3377891bfab9bb4b1c (patch) | |
tree | d2fa421d4740b3ae727eb0b6aec499230ece6bd8 | |
parent | b28c99c038ebe6ed1c9c8690a68a7c6deab97ff7 (diff) | |
download | xmlgraphics-fop-de3950df5e27c9e8a7901a3377891bfab9bb4b1c.tar.gz xmlgraphics-fop-de3950df5e27c9e8a7901a3377891bfab9bb4b1c.zip |
FOP-2456: PDF VT and Page Piece support
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1666117 13f79535-47bb-0310-9956-ffa450edef68
27 files changed, 601 insertions, 17 deletions
diff --git a/lib/xmlgraphics-commons-svn-trunk.jar b/lib/xmlgraphics-commons-svn-trunk.jar Binary files differindex 262c55b31..f0c861d6e 100644 --- a/lib/xmlgraphics-commons-svn-trunk.jar +++ b/lib/xmlgraphics-commons-svn-trunk.jar diff --git a/src/foschema/fop-configuration.xsd b/src/foschema/fop-configuration.xsd index 2201016d1..d3c0b3885 100644 --- a/src/foschema/fop-configuration.xsd +++ b/src/foschema/fop-configuration.xsd @@ -94,6 +94,9 @@ </xsd:annotation> <xsd:sequence minOccurs="0" maxOccurs="unbounded"> <xsd:choice> + <xsd:element name="pdf-a-mode" type="xsd:string" minOccurs="0"/> + <xsd:element name="pdf-x-mode" type="xsd:string" minOccurs="0"/> + <xsd:element name="pdf-vt-mode" type="xsd:string" minOccurs="0"/> <xsd:element name="version" type="xsd:float" minOccurs="0"/> <xsd:element name="endianness" type="xsd:string" minOccurs="0"> <xsd:annotation> @@ -271,6 +274,7 @@ </xsd:element> <xsd:element name="resource-group-file" type="xsd:string" minOccurs="0"/> <xsd:element name="default-resource-levels" minOccurs="0"/> + <xsd:element name="merge-fonts" minOccurs="0"/> </xsd:choice> </xsd:sequence> <xsd:attribute name="mime" type="MimeConstants" use="required"/> diff --git a/src/java/org/apache/fop/pdf/PDFDPart.java b/src/java/org/apache/fop/pdf/PDFDPart.java new file mode 100644 index 000000000..bd7262713 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFDPart.java @@ -0,0 +1,47 @@ +/* + * 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$ */ +package org.apache.fop.pdf; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +public class PDFDPart extends PDFDictionary { + private List<PDFPage> pages = new ArrayList<PDFPage>(); + private PDFDictionary parent; + public PDFDPart(PDFDictionary parent) { + this.parent = parent; + put("Type", new PDFName("DPart")); + } + + public void addPage(PDFPage p) { + pages.add(p); + } + + @Override + public int output(OutputStream stream) throws IOException { + put("Parent", parent.makeReference()); + if (!pages.isEmpty()) { + put("Start", pages.get(0).makeReference()); + put("End", pages.get(pages.size() - 1).makeReference()); + } + return super.output(stream); + } +} diff --git a/src/java/org/apache/fop/pdf/PDFDPartRoot.java b/src/java/org/apache/fop/pdf/PDFDPartRoot.java new file mode 100644 index 000000000..cd1d3b446 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFDPartRoot.java @@ -0,0 +1,42 @@ +/* + * 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$ */ +package org.apache.fop.pdf; + +public class PDFDPartRoot extends PDFDictionary { + private PDFArray parts = new PDFArray(); + protected PDFDPart dpart; + + public PDFDPartRoot(PDFDocument document) { + put("Type", new PDFName("DPartRoot")); + dpart = new PDFDPart(this); + document.registerTrailerObject(dpart); + PDFArray dparts = new PDFArray(); + dparts.add(parts); + dpart.put("DParts", dparts); + put("DPartRootNode", dpart.makeReference()); + PDFArray nodeNameList = new PDFArray(); + nodeNameList.add(new PDFName("root")); + nodeNameList.add(new PDFName("record")); + put("NodeNameList", nodeNameList); + } + + public void add(PDFDPart part) { + parts.add(part); + } +} diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index 86a480780..b1152df16 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -28,6 +28,7 @@ import java.io.InputStream; import java.text.DecimalFormat; import java.util.Arrays; import java.util.BitSet; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -76,6 +77,7 @@ public class PDFFactory { private Log log = LogFactory.getLog(PDFFactory.class); private int subsetFontCounter = -1; + private Map<String, PDFDPart> dparts = new HashMap<String, PDFDPart>(); /** * Creates a new PDFFactory. @@ -1487,4 +1489,24 @@ public class PDFFactory { return navigator; } + public void makeDPart(PDFPage page, String pageMasterName) { + PDFDPartRoot root = getDocument().getRoot().getDPartRoot(); + PDFDPart dPart; + if (dparts.containsKey(pageMasterName)) { + dPart = dparts.get(pageMasterName); + } else { + dPart = new PDFDPart(root.dpart); + root.add(dPart); + getDocument().registerTrailerObject(dPart); + dparts.put(pageMasterName, dPart); + } + dPart.addPage(page); + page.put("DPart", dPart); + } + + public PDFDPartRoot makeDPartRoot() { + PDFDPartRoot pdfdPartRoot = new PDFDPartRoot(getDocument()); + getDocument().registerTrailerObject(pdfdPartRoot); + return pdfdPartRoot; + } } diff --git a/src/java/org/apache/fop/pdf/PDFImageXObject.java b/src/java/org/apache/fop/pdf/PDFImageXObject.java index e472efbea..bbee663da 100644 --- a/src/java/org/apache/fop/pdf/PDFImageXObject.java +++ b/src/java/org/apache/fop/pdf/PDFImageXObject.java @@ -20,9 +20,11 @@ package org.apache.fop.pdf; // Java +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Set; +import java.util.UUID; /* modified by JKT to integrate with 0.12.0 */ /* modified by Eric SCHAEFFER to integrate with 0.13.0 */ @@ -63,6 +65,11 @@ public class PDFImageXObject extends PDFXObject { * @return the length of the data written */ public int output(OutputStream stream) throws IOException { + if (getDocument().getProfile().isPDFVTActive()) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + pdfimage.outputContents(baos); + put("GTS_XID", "uuid:" + UUID.nameUUIDFromBytes(baos.toByteArray())); + } int length = super.output(stream); // let it gc diff --git a/src/java/org/apache/fop/pdf/PDFMetadata.java b/src/java/org/apache/fop/pdf/PDFMetadata.java index 203429147..370c0017a 100644 --- a/src/java/org/apache/fop/pdf/PDFMetadata.java +++ b/src/java/org/apache/fop/pdf/PDFMetadata.java @@ -22,6 +22,7 @@ package org.apache.fop.pdf; import java.io.IOException; import java.io.OutputStream; import java.util.Date; +import java.util.UUID; import javax.xml.transform.TransformerConfigurationException; @@ -37,6 +38,12 @@ import org.apache.xmlgraphics.xmp.schemas.pdf.AdobePDFAdapter; import org.apache.xmlgraphics.xmp.schemas.pdf.AdobePDFSchema; import org.apache.xmlgraphics.xmp.schemas.pdf.PDFAAdapter; import org.apache.xmlgraphics.xmp.schemas.pdf.PDFAXMPSchema; +import org.apache.xmlgraphics.xmp.schemas.pdf.PDFVTAdapter; +import org.apache.xmlgraphics.xmp.schemas.pdf.PDFVTXMPSchema; +import org.apache.xmlgraphics.xmp.schemas.pdf.PDFXAdapter; +import org.apache.xmlgraphics.xmp.schemas.pdf.PDFXXMPSchema; +import org.apache.xmlgraphics.xmp.schemas.pdf.XAPMMAdapter; +import org.apache.xmlgraphics.xmp.schemas.pdf.XAPMMXMPSchema; /** * Special PDFStream for Metadata. @@ -161,11 +168,35 @@ public class PDFMetadata extends PDFStream { pdfa.setPart(pdfaMode.getPart()); pdfa.setConformance(String.valueOf(pdfaMode.getConformanceLevel())); } + AdobePDFAdapter adobePDF = AdobePDFSchema.getAdapter(meta); + PDFXMode pdfxMode = pdfDoc.getProfile().getPDFXMode(); + if (pdfxMode != PDFXMode.DISABLED) { + PDFXAdapter pdfx = PDFXXMPSchema.getAdapter(meta); + pdfx.setVersion(pdfxMode.getName()); + + XAPMMAdapter xapmm = XAPMMXMPSchema.getAdapter(meta); + xapmm.setVersion("1"); + xapmm.setDocumentID("uuid:" + UUID.randomUUID().toString()); + xapmm.setInstanceID("uuid:" + UUID.randomUUID().toString()); + xapmm.setRenditionClass("default"); + adobePDF.setTrapped("False"); + } + PDFProfile profile = pdfDoc.getProfile(); + PDFVTMode pdfvtMode = profile.getPDFVTMode(); + if (pdfvtMode != PDFVTMode.DISABLED) { + PDFVTAdapter pdfvt = PDFVTXMPSchema.getAdapter(meta); + pdfvt.setVersion("PDF/VT-1"); + if (info.getModDate() != null) { + pdfvt.setModifyDate(info.getModDate()); + } else if (profile.isModDateRequired()) { + //if modify date is needed but none is in the Info object, use creation date + pdfvt.setModifyDate(info.getCreationDate()); + } + } //XMP Basic Schema XMPBasicAdapter xmpBasic = XMPBasicSchema.getAdapter(meta); xmpBasic.setCreateDate(info.getCreationDate()); - PDFProfile profile = pdfDoc.getProfile(); if (info.getModDate() != null) { xmpBasic.setModifyDate(info.getModDate()); } else if (profile.isModDateRequired()) { @@ -176,7 +207,7 @@ public class PDFMetadata extends PDFStream { xmpBasic.setCreatorTool(info.getCreator()); } - AdobePDFAdapter adobePDF = AdobePDFSchema.getAdapter(meta); + if (info.getKeywords() != null) { adobePDF.setKeywords(info.getKeywords()); } diff --git a/src/java/org/apache/fop/pdf/PDFProfile.java b/src/java/org/apache/fop/pdf/PDFProfile.java index 4289d0ee8..219623842 100644 --- a/src/java/org/apache/fop/pdf/PDFProfile.java +++ b/src/java/org/apache/fop/pdf/PDFProfile.java @@ -43,6 +43,8 @@ public class PDFProfile { */ protected PDFXMode pdfXMode = PDFXMode.DISABLED; + protected PDFVTMode pdfVTMode = PDFVTMode.DISABLED; + private PDFDocument doc; /** @@ -59,12 +61,15 @@ public class PDFProfile { protected void validateProfileCombination() { if (pdfAMode != PDFAMode.DISABLED) { if (pdfAMode == PDFAMode.PDFA_1B) { - if (pdfXMode != PDFXMode.DISABLED && pdfXMode != PDFXMode.PDFX_3_2003) { + if (pdfXMode != PDFXMode.DISABLED && pdfXMode != PDFXMode.PDFX_3_2003 && pdfXMode != PDFXMode.PDFX_4) { throw new PDFConformanceException( pdfAMode + " and " + pdfXMode + " are not compatible!"); } } } + if (pdfVTMode != PDFVTMode.DISABLED && pdfXMode != PDFXMode.PDFX_4) { + throw new PDFConformanceException(pdfVTMode.name() + " requires " + PDFXMode.PDFX_4.getName() + " enabled"); + } } /** @return the PDFDocument this profile is attached to */ @@ -99,11 +104,19 @@ public class PDFProfile { return this.pdfXMode; } + public PDFVTMode getPDFVTMode() { + return this.pdfVTMode; + } + /** @return true if any PDF/X mode is active */ public boolean isPDFXActive() { return getPDFXMode() != PDFXMode.DISABLED; } + public boolean isPDFVTActive() { + return getPDFVTMode() != PDFVTMode.DISABLED; + } + /** * Sets the PDF/X mode * @param mode the PDF/X mode @@ -116,6 +129,18 @@ public class PDFProfile { validateProfileCombination(); } + /** + * Sets the PDF/X mode + * @param mode the PDF/X mode + */ + public void setPDFVTMode(PDFVTMode mode) { + if (mode == null) { + mode = PDFVTMode.DISABLED; + } + this.pdfVTMode = mode; + validateProfileCombination(); + } + /** {@inheritDoc} */ public String toString() { StringBuffer sb = new StringBuffer(); @@ -186,7 +211,7 @@ public class PDFProfile { if (pdfAMode.isPart1()) { return getPDFAMode(); } - if (isPDFXActive()) { + if (getPDFXMode() == PDFXMode.PDFX_3_2003) { return getPDFXMode(); } return null; @@ -194,7 +219,7 @@ public class PDFProfile { /** Checks if the right PDF version is set. */ public void verifyPDFVersion() { - final String err = "PDF version must be 1.4 for {0}"; + String err = "PDF version must be 1.4 for {0}"; if (getPDFAMode().isPart1() && !Version.V1_4.equals(getDocument().getPDFVersion())) { throw new PDFConformanceException(format(err, getPDFAMode())); @@ -252,12 +277,12 @@ public class PDFProfile { /** @return true if the ModDate Info entry must be present. */ public boolean isModDateRequired() { - return getPDFXMode() == PDFXMode.PDFX_3_2003; + return getPDFXMode() != PDFXMode.DISABLED; } /** @return true if the Trapped Info entry must be present. */ public boolean isTrappedEntryRequired() { - return getPDFXMode() == PDFXMode.PDFX_3_2003; + return getPDFXMode() != PDFXMode.DISABLED; } /** @return true if annotations are allowed */ diff --git a/src/java/org/apache/fop/pdf/PDFRoot.java b/src/java/org/apache/fop/pdf/PDFRoot.java index 0a0f03d6d..66e58c5f0 100644 --- a/src/java/org/apache/fop/pdf/PDFRoot.java +++ b/src/java/org/apache/fop/pdf/PDFRoot.java @@ -52,6 +52,8 @@ public class PDFRoot extends PDFDictionary { private final PDFDocument document; + private PDFDPartRoot dPartRoot; + private static final PDFName[] PAGEMODE_NAMES = new PDFName[] { new PDFName("UseNone"), @@ -318,4 +320,12 @@ public class PDFRoot extends PDFDictionary { public PDFDictionary getMarkInfo() { return (PDFDictionary)get("MarkInfo"); } + + public PDFDPartRoot getDPartRoot() { + if (dPartRoot == null) { + dPartRoot = getDocument().getFactory().makeDPartRoot(); + put("DPartRoot", dPartRoot.makeReference()); + } + return dPartRoot; + } } diff --git a/src/java/org/apache/fop/pdf/PDFVTMode.java b/src/java/org/apache/fop/pdf/PDFVTMode.java new file mode 100644 index 000000000..0dbb65873 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFVTMode.java @@ -0,0 +1,45 @@ +/* + * 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$ */ +package org.apache.fop.pdf; + +public enum PDFVTMode { + DISABLED("PDF/VT disabled"), + PDFVT_1("PDF/VT-1"); + + private String name; + + PDFVTMode(String s) { + name = s; + } + + /** + * Returns the mode enum object given a String. + * + * @param s the string + * @return the PDFVTMode enum object (DISABLED will be returned if no match is found) + */ + public static PDFVTMode getValueOf(String s) { + for (PDFVTMode mode : values()) { + if (mode.name.equalsIgnoreCase(s)) { + return mode; + } + } + return DISABLED; + } +} diff --git a/src/java/org/apache/fop/pdf/PDFXMode.java b/src/java/org/apache/fop/pdf/PDFXMode.java index be0312891..84e7f785a 100644 --- a/src/java/org/apache/fop/pdf/PDFXMode.java +++ b/src/java/org/apache/fop/pdf/PDFXMode.java @@ -25,7 +25,8 @@ public enum PDFXMode { /** PDF/X disabled */ DISABLED("PDF/X disabled"), /** PDF/X-3:2003 enabled */ - PDFX_3_2003("PDF/X-3:2003"); + PDFX_3_2003("PDF/X-3:2003"), + PDFX_4("PDF/X-4"); private String name; @@ -48,11 +49,12 @@ public enum PDFXMode { * @return the PDFAMode enum object (DISABLED will be returned if no match is found) */ public static PDFXMode getValueOf(String s) { - if (PDFX_3_2003.getName().equalsIgnoreCase(s)) { - return PDFX_3_2003; - } else { - return DISABLED; + for (PDFXMode mode : values()) { + if (mode.name.equalsIgnoreCase(s)) { + return mode; + } } + return DISABLED; } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java index 05e9cb580..e5896b803 100644 --- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java @@ -243,6 +243,9 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { toPDFCoordSystem(cropBox, boxTransform), toPDFCoordSystem(bleedBox, boxTransform), toPDFCoordSystem(trimBox, boxTransform)); + if (pdfDoc.getProfile().isPDFVTActive()) { + pdfDoc.getFactory().makeDPart(currentPage, pageMasterName); + } if (accessEnabled) { logicalStructureHandler.startPage(currentPage); } diff --git a/src/java/org/apache/fop/render/pdf/PDFRendererConfig.java b/src/java/org/apache/fop/render/pdf/PDFRendererConfig.java index f25872c4c..bbfb8a9b3 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRendererConfig.java +++ b/src/java/org/apache/fop/render/pdf/PDFRendererConfig.java @@ -60,6 +60,7 @@ import static org.apache.fop.render.pdf.PDFRendererOption.LINEARIZATION; import static org.apache.fop.render.pdf.PDFRendererOption.MERGE_FONTS; import static org.apache.fop.render.pdf.PDFRendererOption.OUTPUT_PROFILE; import static org.apache.fop.render.pdf.PDFRendererOption.PDF_A_MODE; +import static org.apache.fop.render.pdf.PDFRendererOption.PDF_VT_MODE; import static org.apache.fop.render.pdf.PDFRendererOption.PDF_X_MODE; import static org.apache.fop.render.pdf.PDFRendererOption.VERSION; @@ -134,6 +135,7 @@ public final class PDFRendererConfig implements RendererConfig { buildFilterMapFromConfiguration(cfg); parseAndPut(PDF_A_MODE, cfg); parseAndPut(PDF_X_MODE, cfg); + parseAndPut(PDF_VT_MODE, cfg); configureEncryptionParams(cfg, userAgent, strict); parseAndPut(OUTPUT_PROFILE, cfg); parseAndPut(DISABLE_SRGB_COLORSPACE, cfg); diff --git a/src/java/org/apache/fop/render/pdf/PDFRendererOption.java b/src/java/org/apache/fop/render/pdf/PDFRendererOption.java index cfad76b81..8874f566d 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRendererOption.java +++ b/src/java/org/apache/fop/render/pdf/PDFRendererOption.java @@ -24,6 +24,7 @@ import java.net.URISyntaxException; import org.apache.fop.apps.io.InternalResourceResolver; import org.apache.fop.pdf.PDFAMode; +import org.apache.fop.pdf.PDFVTMode; import org.apache.fop.pdf.PDFXMode; import org.apache.fop.pdf.Version; import org.apache.fop.render.RendererConfigOption; @@ -49,6 +50,13 @@ public enum PDFRendererOption implements RendererConfigOption { return PDFXMode.getValueOf(value); } }, + /** Rendering Options key for the PDF/VT mode, default: {@link PDFVTMode#DISABLED} */ + PDF_VT_MODE("pdf-vt-mode", PDFVTMode.DISABLED) { + @Override + PDFVTMode deserialize(String value) { + return PDFVTMode.getValueOf(value); + } + }, /** PDF version entry: specify the version of the PDF document created, datatype: String */ VERSION("version") { @Override diff --git a/src/java/org/apache/fop/render/pdf/PDFRendererOptionsConfig.java b/src/java/org/apache/fop/render/pdf/PDFRendererOptionsConfig.java index 8124db9f5..e4cf2480b 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRendererOptionsConfig.java +++ b/src/java/org/apache/fop/render/pdf/PDFRendererOptionsConfig.java @@ -26,6 +26,7 @@ import java.util.Map; import org.apache.fop.pdf.PDFAMode; import org.apache.fop.pdf.PDFEncryptionParams; +import org.apache.fop.pdf.PDFVTMode; import org.apache.fop.pdf.PDFXMode; import org.apache.fop.pdf.Version; @@ -35,6 +36,7 @@ import static org.apache.fop.render.pdf.PDFRendererOption.LINEARIZATION; import static org.apache.fop.render.pdf.PDFRendererOption.MERGE_FONTS; import static org.apache.fop.render.pdf.PDFRendererOption.OUTPUT_PROFILE; import static org.apache.fop.render.pdf.PDFRendererOption.PDF_A_MODE; +import static org.apache.fop.render.pdf.PDFRendererOption.PDF_VT_MODE; import static org.apache.fop.render.pdf.PDFRendererOption.PDF_X_MODE; import static org.apache.fop.render.pdf.PDFRendererOption.VERSION; @@ -107,6 +109,10 @@ public final class PDFRendererOptionsConfig { return (PDFXMode) properties.get(PDF_X_MODE); } + public PDFVTMode getPDFVTMode() { + return (PDFVTMode) properties.get(PDF_VT_MODE); + } + public PDFEncryptionParams getEncryptionParameters() { return encryptionConfig; } diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java index f93628331..e8da29b7d 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java @@ -27,15 +27,18 @@ import java.io.OutputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.Date; import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.TimeZone; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.java2d.color.profile.ColorProfileUtil; +import org.apache.xmlgraphics.util.DateFormatUtil; import org.apache.xmlgraphics.xmp.Metadata; import org.apache.xmlgraphics.xmp.schemas.DublinCoreSchema; import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter; @@ -162,6 +165,7 @@ class PDFRenderingUtil { private void updatePDFProfiles() { pdfDoc.getProfile().setPDFAMode(rendererConfig.getPDFAMode()); pdfDoc.getProfile().setPDFXMode(rendererConfig.getPDFXMode()); + pdfDoc.getProfile().setPDFVTMode(rendererConfig.getPDFVTMode()); } private void addsRGBColorSpace() throws IOException { @@ -466,6 +470,18 @@ class PDFRenderingUtil { for (PDFCollectionEntryExtension entry : extension.getEntries()) { info.put(entry.getKey(), entry.getValueAsString()); } + } else if (type == PDFDictionaryType.VT) { + if (currentPage.get("DPart") != null) { + augmentDictionary((PDFDictionary)currentPage.get("DPart"), extension); + } + } else if (type == PDFDictionaryType.PagePiece) { + String date = DateFormatUtil.formatPDFDate(new Date(), TimeZone.getDefault()); + if (currentPage.get("PieceInfo") == null) { + currentPage.put("PieceInfo", new PDFDictionary()); + currentPage.put("LastModified", date); + } + PDFDictionary d = augmentDictionary((PDFDictionary)currentPage.get("PieceInfo"), extension); + d.put("LastModified", date); } else { throw new IllegalStateException(); } @@ -474,8 +490,20 @@ class PDFRenderingUtil { private PDFDictionary augmentDictionary(PDFDictionary dictionary, PDFDictionaryExtension extension) { for (PDFCollectionEntryExtension entry : extension.getEntries()) { if (entry instanceof PDFDictionaryExtension) { - dictionary.put(entry.getKey(), - augmentDictionary(new PDFDictionary(dictionary), (PDFDictionaryExtension) entry)); + String[] keys = entry.getKey().split("/"); + for (int i = 0; i < keys.length; i++) { + if (keys[i].isEmpty()) { + throw new IllegalStateException("pdf:dictionary key: " + entry.getKey() + " not valid"); + } + if (i == keys.length - 1) { + dictionary.put(keys[i], + augmentDictionary(new PDFDictionary(dictionary), (PDFDictionaryExtension) entry)); + } else { + PDFDictionary d = new PDFDictionary(); + dictionary.put(keys[i], d); + dictionary = d; + } + } } else if (entry instanceof PDFArrayExtension) { dictionary.put(entry.getKey(), augmentArray(new PDFArray(dictionary), (PDFArrayExtension) entry)); } else { diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java index 26b03d5bc..3da224290 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java @@ -107,6 +107,10 @@ public class PDFDictionaryElement extends PDFCollectionEntryElement { // handled in PDFPageElement subclass } else if (localName.equals("info")) { // handled in PDFDocumentInformationElement subclass + } else if (localName.equals("vt")) { + // handled in PDFVTElement subclass + } else if (localName.equals("pagepiece")) { + // handled in PDFPagePieceElement subclass } else if (localName.equals("dictionary")) { if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName()) && !PDFObjectType.Array.elementName().equals(parent.getLocalName())) { invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null); diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java index 50b6f3a83..d1367413e 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java @@ -71,7 +71,7 @@ public class PDFDictionaryExtension extends PDFCollectionExtension { @Override public void addEntry(PDFCollectionEntryExtension entry) { if ((entry.getKey() == null) || (entry.getKey().length() == 0)) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("pdf:dictionary key is empty"); } else { entries.add(entry); } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java index 1a3387aa0..4cefc2bad 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java @@ -32,7 +32,9 @@ public enum PDFDictionaryType { Navigator("navigator", true), // navigation node dictionary element Page("page"), // page dictionary element /** Document Information Dictionary */ - Info("info"); + Info("info"), + VT("vt"), + PagePiece("pagepiece"); private String elementName; private boolean usesIDAttribute; diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java b/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java index 5831b1f2a..836b53656 100644 --- a/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java @@ -67,6 +67,10 @@ public class PDFElementMapping extends ElementMapping { foObjs.put(PDFObjectType.String.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.String)); // pdf:info foObjs.put(PDFDictionaryType.Info.elementName(), new PDFDocumentInformationElementMaker()); + // pdf:vt + foObjs.put(PDFDictionaryType.VT.elementName(), new PDFVTElementMaker()); + // pdf:pagepiece + foObjs.put(PDFDictionaryType.PagePiece.elementName(), new PDFPagePieceElementMaker()); } } @@ -140,4 +144,15 @@ public class PDFElementMapping extends ElementMapping { } } + static class PDFVTElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFVTElement(parent); + } + } + + static class PDFPagePieceElementMaker extends ElementMapping.Maker { + public FONode make(FONode parent) { + return new PDFPagePieceElement(parent); + } + } } diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFVTElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFVTElement.java new file mode 100644 index 000000000..24fb50b6c --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/extensions/PDFVTElement.java @@ -0,0 +1,28 @@ +/* + * 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$ */ +package org.apache.fop.render.pdf.extensions; + +import org.apache.fop.fo.FONode; + +public class PDFVTElement extends PDFDictionaryElement { + + PDFVTElement(FONode parent) { + super(parent, PDFDictionaryType.VT); + } +} diff --git a/test/java/org/apache/fop/pdf/PDFLinearizationTestCase.java b/test/java/org/apache/fop/pdf/PDFLinearizationTestCase.java index 7b78c1f5b..3df497e0f 100644 --- a/test/java/org/apache/fop/pdf/PDFLinearizationTestCase.java +++ b/test/java/org/apache/fop/pdf/PDFLinearizationTestCase.java @@ -288,7 +288,7 @@ public class PDFLinearizationTestCase { } } - private Map<String, StringBuilder> readObjs(InputStream inputStream) throws IOException { + public static Map<String, StringBuilder> readObjs(InputStream inputStream) throws IOException { Map<String, StringBuilder> objs = new LinkedHashMap<String, StringBuilder>(); StringBuilder sb = new StringBuilder(); String key = null; diff --git a/test/java/org/apache/fop/pdf/PDFPagePieceTestCase.java b/test/java/org/apache/fop/pdf/PDFPagePieceTestCase.java new file mode 100644 index 000000000..4ee98b665 --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFPagePieceTestCase.java @@ -0,0 +1,67 @@ +/* + * 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$ */ +package org.apache.fop.pdf; + +import java.awt.Dimension; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.util.Arrays; + +import javax.xml.transform.stream.StreamResult; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.FopFactory; +import org.apache.fop.render.intermediate.IFContext; +import org.apache.fop.render.intermediate.IFException; +import org.apache.fop.render.pdf.PDFDocumentHandler; +import org.apache.fop.render.pdf.extensions.PDFCollectionEntryExtension; +import org.apache.fop.render.pdf.extensions.PDFDictionaryAttachment; +import org.apache.fop.render.pdf.extensions.PDFDictionaryExtension; +import org.apache.fop.render.pdf.extensions.PDFDictionaryType; + +import junit.framework.Assert; + +public class PDFPagePieceTestCase { + @Test + public void testPDF() throws IFException { + FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI()); + FOUserAgent userAgent = fopFactory.newFOUserAgent(); + PDFDocumentHandler documentHandler = new PDFDocumentHandler(new IFContext(userAgent)); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + documentHandler.setResult(new StreamResult(bos)); + documentHandler.startDocument(); + documentHandler.startPage(0, "", "", new Dimension()); + + PDFDictionaryExtension dictionaryExtension = mock(PDFDictionaryExtension.class); + when(dictionaryExtension.getDictionaryType()).thenReturn(PDFDictionaryType.PagePiece); + + PDFDictionaryExtension child = mock(PDFDictionaryExtension.class); + when(child.getKey()).thenReturn("a"); + when(dictionaryExtension.getEntries()).thenReturn(Arrays.<PDFCollectionEntryExtension>asList(child)); + documentHandler.handleExtensionObject(new PDFDictionaryAttachment(dictionaryExtension)); + + documentHandler.endPage(); + Assert.assertTrue(bos.toString(), bos.toString().contains("/PieceInfo << /a << >> /LastModified (D:")); + } +} diff --git a/test/java/org/apache/fop/pdf/PDFVT.fo b/test/java/org/apache/fop/pdf/PDFVT.fo new file mode 100644 index 000000000..6919afcf6 --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFVT.fo @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-family="Arial" xmlns:pdf="http://xmlgraphics.apache.org/fop/extensions/pdf"> + <fo:layout-master-set> + <fo:simple-page-master master-name="simple" page-height="27.9cm" page-width="21.6cm"> + <fo:region-body /> + <pdf:vt> + <pdf:dictionary key="DPM/CIP4_Root/CIP4_Production/CIP4_Part"> + <pdf:string key="CIP4_ProductType">frontpages</pdf:string> + </pdf:dictionary> + </pdf:vt> + </fo:simple-page-master> + </fo:layout-master-set> + <fo:declarations> + <x:xmpmeta xmlns:x="adobe:ns:meta/"> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> + <rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"> + <dc:title>Document title</dc:title> + <dc:creator>Document author</dc:creator> + <dc:description>Document subject</dc:description> + </rdf:Description> + </rdf:RDF> + </x:xmpmeta> + </fo:declarations> + <fo:page-sequence format="1" id="th_default_sequence1" master-reference="simple"> + <fo:flow flow-name="xsl-region-body"> + <fo:block>page 1</fo:block> + </fo:flow> + </fo:page-sequence> +</fo:root> diff --git a/test/java/org/apache/fop/pdf/PDFVT.xconf b/test/java/org/apache/fop/pdf/PDFVT.xconf new file mode 100644 index 000000000..52588f0b0 --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFVT.xconf @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<fop version="1.0"> + <renderers> + <renderer mime="application/pdf"> + <pdf-x-mode>PDF/X-4</pdf-x-mode> + <pdf-vt-mode>PDF/VT-1</pdf-vt-mode> + <output-profile>test/resources/color/ISOcoated_v2_300_bas.icc</output-profile> + <fonts> + <font kerning="yes" embed-url="test/resources/fonts/ttf/DejaVuLGCSerif.ttf"> + <font-triplet name="Arial" style="normal" weight="normal"/> + </font> + </fonts> + </renderer> + </renderers> +</fop> diff --git a/test/java/org/apache/fop/pdf/PDFVTTestCase.java b/test/java/org/apache/fop/pdf/PDFVTTestCase.java new file mode 100644 index 000000000..e3dcf2cf6 --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFVTTestCase.java @@ -0,0 +1,142 @@ +/* + * 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$ */ +package org.apache.fop.pdf; + +import java.awt.geom.Rectangle2D; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.stream.StreamSource; + +import org.junit.Assert; +import org.junit.Test; + +import org.xml.sax.SAXException; + +import org.apache.xmlgraphics.util.QName; +import org.apache.xmlgraphics.xmp.Metadata; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.Fop; +import org.apache.fop.apps.FopFactory; +import org.apache.fop.apps.MimeConstants; +import org.apache.fop.render.pdf.PDFContentGenerator; + +public class PDFVTTestCase { + @Test + public void testXMP() throws IOException { + PDFDocument doc = new PDFDocument(""); + doc.getProfile().setPDFXMode(PDFXMode.PDFX_4); + doc.getProfile().setPDFVTMode(PDFVTMode.PDFVT_1); + Metadata metadata = PDFMetadata.createXMPFromPDFDocument(doc); + StringBuilder sb = new StringBuilder(); + Iterator i = metadata.iterator(); + while (i.hasNext()) { + QName k = (QName) i.next(); + sb.append(k + ": " + metadata.getProperty(k).getValue() + "\n"); + } + String s = sb.toString(); + Assert.assertTrue(s.contains("pdfxid:GTS_PDFXVersion: PDF/X-4")); + Assert.assertTrue(s.contains("xmpMM:VersionID: 1")); + Assert.assertTrue(s.contains("pdf:Trapped: False")); + Assert.assertTrue(s.contains("xmpMM:RenditionClass: default")); + Assert.assertTrue(s.contains("pdf:PDFVersion: 1.4")); + Assert.assertTrue(s.contains("pdfvtid:GTS_PDFVTVersion: PDF/VT-1")); + } + + @Test + public void testPDF() throws IOException { + PDFDocument doc = new PDFDocument(""); + doc.getInfo().setTitle("title"); + doc.getProfile().setPDFXMode(PDFXMode.PDFX_4); + doc.getProfile().setPDFVTMode(PDFVTMode.PDFVT_1); + PDFResources resources = new PDFResources(doc); + doc.addObject(resources); + PDFResourceContext context = new PDFResourceContext(resources); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + PDFContentGenerator gen = new PDFContentGenerator(doc, out, context); + Rectangle2D.Float f = new Rectangle2D.Float(); + PDFPage page = new PDFPage(resources, 0, f, f, f, f); + doc.addImage(context, new BitmapImage("", 1, 1, new byte[0], null)); + doc.registerObject(page); + doc.getFactory().makeDPart(page, "master"); + gen.flushPDFDoc(); + doc.outputTrailer(out); + + Collection<StringBuilder> objs = PDFLinearizationTestCase.readObjs( + new ByteArrayInputStream(out.toByteArray())).values(); + Assert.assertTrue(getObj(objs, "/Type /Catalog").contains("/DPartRoot ")); + Assert.assertTrue(getObj(objs, "/Type /DPartRoot").contains("/NodeNameList [/root /record]")); + Assert.assertTrue( + getObj(objs, "/Subtype /Image").contains("/GTS_XID (uuid:d41d8cd9-8f00-3204-a980-0998ecf8427e)")); + } + + @Test + public void textFO() throws IOException, SAXException, TransformerException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI(), + new FileInputStream("test/java/org/apache/fop/pdf/PDFVT.xconf")); + FOUserAgent userAgent = fopFactory.newFOUserAgent(); + + Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, userAgent, out); + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + Source src = new StreamSource(new FileInputStream("test/java/org/apache/fop/pdf/PDFVT.fo")); + Result res = new SAXResult(fop.getDefaultHandler()); + transformer.transform(src, res); + + Map<String, StringBuilder> objs = + PDFLinearizationTestCase.readObjs(new ByteArrayInputStream(out.toByteArray())); + String dpart = getObj(objs.values(), "/DParts"); + int v = getValue("/DParts", dpart); + String dpm = objs.get(v + " 0 obj").toString(); + Assert.assertTrue(dpm.contains( + "/DPM << /CIP4_Root << /CIP4_Production << /CIP4_Part << /CIP4_ProductType (frontpages) >>")); + } + + private int getValue(String name, String firstObj) throws IOException { + String[] split = firstObj.split(" "); + for (int i = 0; i < split.length; i++) { + if (split[i].equals(name)) { + return Integer.valueOf(split[i + 1].replace("[[", "")); + } + } + throw new IOException(name + " not found " + firstObj); + } + + private String getObj(Collection<StringBuilder> objs, String x) { + for (StringBuilder s : objs) { + if (s.toString().contains(x)) { + return s.toString(); + } + } + return null; + } +} diff --git a/test/resources/color/ISOcoated_v2_300_bas.icc b/test/resources/color/ISOcoated_v2_300_bas.icc Binary files differnew file mode 100644 index 000000000..71faa91f9 --- /dev/null +++ b/test/resources/color/ISOcoated_v2_300_bas.icc |