aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache
diff options
context:
space:
mode:
authorJeremias Maerki <jeremias@apache.org>2006-02-17 16:18:04 +0000
committerJeremias Maerki <jeremias@apache.org>2006-02-17 16:18:04 +0000
commitbb601dc8aa70faa53cdfac917161f7d2ef7fc1a3 (patch)
tree7ed71ff48b867f840939cb4f9a8dbe2eda5bb89e /src/java/org/apache
parent7d3e991871bb74e8e2ce7eb77f2e5748e7d84d63 (diff)
downloadxmlgraphics-fop-bb601dc8aa70faa53cdfac917161f7d2ef7fc1a3.tar.gz
xmlgraphics-fop-bb601dc8aa70faa53cdfac917161f7d2ef7fc1a3.zip
XMP metadata generation from the PDFInfo object now conforms to PDF/A-1 (6.7.3 in ISO 19005-1:2005(E)).
On PDFDocument, it is now possible to set the PDF-A mode which triggers conformance checks where necessary. The first PDF/A-1b conformance checks are now in the code. When conformance problems are detected, the new PDFConformanceException (subclass of RuntimeException since this would be a programmer error) is thrown. PDF Renderer got a setting (through rendering options and XML configuration) to set the PDF/A-1 mode. Default is "disabled". git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@378560 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache')
-rw-r--r--src/java/org/apache/fop/fo/extensions/xmp/XMPConstants.java3
-rw-r--r--src/java/org/apache/fop/pdf/PDFAMode.java71
-rw-r--r--src/java/org/apache/fop/pdf/PDFConformanceException.java46
-rw-r--r--src/java/org/apache/fop/pdf/PDFDocument.java54
-rw-r--r--src/java/org/apache/fop/pdf/PDFInfo.java7
-rw-r--r--src/java/org/apache/fop/pdf/PDFMetadata.java32
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderer.java17
7 files changed, 218 insertions, 12 deletions
diff --git a/src/java/org/apache/fop/fo/extensions/xmp/XMPConstants.java b/src/java/org/apache/fop/fo/extensions/xmp/XMPConstants.java
index b8b29988c..ed189aa38 100644
--- a/src/java/org/apache/fop/fo/extensions/xmp/XMPConstants.java
+++ b/src/java/org/apache/fop/fo/extensions/xmp/XMPConstants.java
@@ -35,4 +35,7 @@ public interface XMPConstants {
/** Namespace URI for the XMP Basic Schema */
String XMP_BASIC_NAMESPACE = "http://ns.adobe.com/xap/1.0/";
+ /** Namespace URI for the Adobe PDF Schema */
+ String ADOBE_PDF_NAMESPACE = "http://ns.adobe.com/pdf/1.3/";
+
}
diff --git a/src/java/org/apache/fop/pdf/PDFAMode.java b/src/java/org/apache/fop/pdf/PDFAMode.java
new file mode 100644
index 000000000..52e1f02f5
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFAMode.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+/** Enum class for PDF/A modes. */
+public final class PDFAMode {
+
+ /** PDF/A disabled */
+ public static final PDFAMode DISABLED = new PDFAMode("PDF/A disabled");
+ /** PDF/A-1a enabled */
+ public static final PDFAMode PDFA_1A = new PDFAMode("PDF/A-1a");
+ /** PDF/A-1b enabled */
+ public static final PDFAMode PDFA_1B = new PDFAMode("PDF/A-1b");
+
+ private String name;
+
+ /**
+ * Constructor to add a new named item.
+ * @param name Name of the item.
+ */
+ private PDFAMode(String name) {
+ this.name = name;
+ }
+
+ /** @return the name of the enum */
+ public String getName() {
+ return this.name;
+ }
+
+ /** @return true if this mode obey the restrictions established by PDF/A-1b. */
+ public boolean isPDFA1LevelB() {
+ return (this != DISABLED);
+ }
+
+ /**
+ * Returns the mode enum object given a String.
+ * @param s the string
+ * @return the PDFAMode enum object (DISABLED will be returned if no match is found)
+ */
+ public static PDFAMode valueOf(String s) {
+ if (PDFA_1A.getName().equalsIgnoreCase(s)) {
+ return PDFA_1A;
+ } else if (PDFA_1B.getName().equalsIgnoreCase(s)) {
+ return PDFA_1B;
+ } else {
+ return DISABLED;
+ }
+ }
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ return name;
+ }
+
+}
diff --git a/src/java/org/apache/fop/pdf/PDFConformanceException.java b/src/java/org/apache/fop/pdf/PDFConformanceException.java
new file mode 100644
index 000000000..b8f8e3015
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFConformanceException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+/**
+ * RuntimeException descendant indicating a conformance problem during PDF generation. This
+ * exception will be throws, for example, when PDF/A-1 Level B conformance is activated but the
+ * PDF version produced is not PDF 1.4 as mandated by ISO 19005-1:2005(E).
+ */
+public class PDFConformanceException extends RuntimeException {
+
+ /**
+ * Constructs an PDFConformanceException with no detail message.
+ * A detail message is a String that describes this particular exception.
+ */
+ public PDFConformanceException() {
+ super();
+ }
+
+ /**
+ * Constructs an PDFConformanceException with the specified detail
+ * message. A detail message is a String that describes this particular
+ * exception.
+ * @param message the String that contains a detailed message
+ */
+ public PDFConformanceException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java
index 572e3be43..916f19ca8 100644
--- a/src/java/org/apache/fop/pdf/PDFDocument.java
+++ b/src/java/org/apache/fop/pdf/PDFDocument.java
@@ -58,10 +58,6 @@ import org.apache.commons.logging.LogFactory;
public class PDFDocument {
private static final Integer LOCATION_PLACEHOLDER = new Integer(0);
- /**
- * the version of PDF supported which is 1.4
- */
- protected static final String PDF_VERSION = "1.4";
/** Integer constant to represent PDF 1.3 */
public static final int PDF_VERSION_1_3 = 3;
@@ -108,6 +104,12 @@ public class PDFDocument {
protected int pdfVersion = PDF_VERSION_1_4;
/**
+ * Indicates the PDF/A-1 mode currently active. Defaults to "no restrictions", i.e.
+ * PDF/A-1 not active.
+ */
+ protected PDFAMode pdfAMode = PDFAMode.DISABLED;
+
+ /**
* the /Root object
*/
protected PDFRoot root;
@@ -250,6 +252,35 @@ public class PDFDocument {
return this.pdfVersion;
}
+ /** @return the String representing the active PDF version */
+ public String getPDFVersionString() {
+ switch (getPDFVersion()) {
+ case PDF_VERSION_1_3:
+ return "1.3";
+ case PDF_VERSION_1_4:
+ return "1.4";
+ default:
+ throw new IllegalStateException("Unsupported PDF version selected");
+ }
+ }
+
+ /** @return the PDF/A mode currently active. */
+ public PDFAMode getPDFAMode() {
+ return this.pdfAMode;
+ }
+
+ /**
+ * Sets the active PDF/A mode. This must be set immediately after calling the constructor so
+ * the checks will be activated.
+ * @param mode one of the PDFAMode constants
+ */
+ public void setPDFAMode(PDFAMode mode) {
+ if (mode == null) {
+ throw new NullPointerException("mode must not be null");
+ }
+ this.pdfAMode = mode;
+ }
+
/**
* Returns the factory for PDF objects.
* @return PDFFactory the factory
@@ -474,8 +505,10 @@ public class PDFDocument {
* @param params The encryption parameters for the pdf file
*/
public void setEncryption(PDFEncryptionParams params) {
- this.encryption =
- PDFEncryptionManager.newInstance(++this.objectcount, params);
+ if (getPDFAMode().isPDFA1LevelB()) {
+ throw new PDFConformanceException("PDF/A-1 doesn't allow encrypted PDFs");
+ }
+ this.encryption = PDFEncryptionManager.newInstance(++this.objectcount, params);
((PDFObject)this.encryption).setDocument(this);
if (encryption != null) {
/**@todo this cast is ugly. PDFObject should be transformed to an interface. */
@@ -824,13 +857,16 @@ public class PDFDocument {
public void outputHeader(OutputStream stream) throws IOException {
this.position = 0;
- byte[] pdf = ("%PDF-" + PDF_VERSION + "\n").getBytes();
+ if (getPDFAMode().isPDFA1LevelB() && getPDFVersion() != PDF_VERSION_1_4) {
+ throw new PDFConformanceException("PDF version must be 1.4 for " + getPDFAMode());
+ }
+
+ byte[] pdf = ("%PDF-" + getPDFVersionString() + "\n").getBytes();
stream.write(pdf);
this.position += pdf.length;
// output a binary comment as recommended by the PDF spec (3.4.1)
- byte[] bin =
- {
+ byte[] bin = {
(byte)'%',
(byte)0xAA,
(byte)0xAB,
diff --git a/src/java/org/apache/fop/pdf/PDFInfo.java b/src/java/org/apache/fop/pdf/PDFInfo.java
index 7bf346753..109d791bd 100644
--- a/src/java/org/apache/fop/pdf/PDFInfo.java
+++ b/src/java/org/apache/fop/pdf/PDFInfo.java
@@ -44,6 +44,11 @@ public class PDFInfo extends PDFObject {
*/
private String creator;
+ /** @return the producer of the document or null if not set */
+ public String getProducer() {
+ return this.producer;
+ }
+
/**
* set the producer string
*
@@ -110,7 +115,7 @@ public class PDFInfo extends PDFObject {
}
/** @return the keywords for the document or null if not set */
- public String getKeyword() {
+ public String getKeywords() {
return this.keywords;
}
diff --git a/src/java/org/apache/fop/pdf/PDFMetadata.java b/src/java/org/apache/fop/pdf/PDFMetadata.java
index d162ba032..9af42e8b6 100644
--- a/src/java/org/apache/fop/pdf/PDFMetadata.java
+++ b/src/java/org/apache/fop/pdf/PDFMetadata.java
@@ -23,6 +23,7 @@ import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.TimeZone;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
@@ -32,7 +33,6 @@ import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
-import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.ElementMapping;
import org.apache.fop.fo.extensions.xmp.XMPConstants;
import org.w3c.dom.DOMImplementation;
@@ -127,6 +127,11 @@ public class PDFMetadata extends PDFStream {
/** @see org.apache.fop.pdf.AbstractPDFStream#buildStreamDict(String) */
protected String buildStreamDict(String lengthEntry) {
final String filterEntry = getFilterList().buildFilterDictEntries();
+ if (getDocumentSafely().getPDFAMode().isPDFA1LevelB()
+ && filterEntry != null && filterEntry.length() > 0) {
+ throw new PDFConformanceException(
+ "The Filter key is prohibited when PDF/A-1 is active");
+ }
final StringBuffer sb = new StringBuffer(128);
sb.append(getObjectID());
sb.append("<< ");
@@ -153,7 +158,8 @@ public class PDFMetadata extends PDFStream {
Element desc, el;
PDFInfo info = pdfDoc.getInfo();
DateFormat pseudoISO8601DateFormat = new SimpleDateFormat(
- "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS");
+ "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'");
+ pseudoISO8601DateFormat.setTimeZone(TimeZone.getTimeZone("GMT+00"));
//Set creation date if not available, yet
if (info.getCreationDate() == null) {
@@ -161,9 +167,12 @@ public class PDFMetadata extends PDFStream {
info.setCreationDate(d);
}
+ final String xmlns = "http://www.w3.org/2000/xmlns/";
+
//Dublin Core
desc = doc.createElementNS(XMPConstants.RDF_NAMESPACE, "rdf:Description");
desc.setAttribute("about", "");
+ desc.setAttributeNS(xmlns, "xmlns:dc", XMPConstants.DUBLIN_CORE_NAMESPACE);
rdf.appendChild(desc);
if (info.getAuthor() != null) {
el = doc.createElementNS(XMPConstants.DUBLIN_CORE_NAMESPACE, "dc:creator");
@@ -187,6 +196,7 @@ public class PDFMetadata extends PDFStream {
//XMP Basic Schema
desc = doc.createElementNS(XMPConstants.RDF_NAMESPACE, "rdf:Description");
desc.setAttribute("about", "");
+ desc.setAttributeNS(xmlns, "xmlns:xmp", XMPConstants.XMP_BASIC_NAMESPACE);
rdf.appendChild(desc);
el = doc.createElementNS(XMPConstants.XMP_BASIC_NAMESPACE, "xmp:createDate");
desc.appendChild(el);
@@ -197,6 +207,24 @@ public class PDFMetadata extends PDFStream {
el.appendChild(doc.createTextNode(info.getCreator()));
}
+ //Adobe PDF Schema
+ desc = doc.createElementNS(XMPConstants.RDF_NAMESPACE, "rdf:Description");
+ desc.setAttribute("about", "");
+ desc.setAttributeNS(xmlns, "xmlns:pdf", XMPConstants.ADOBE_PDF_NAMESPACE);
+ rdf.appendChild(desc);
+ if (info.getKeywords() != null) {
+ el = doc.createElementNS(XMPConstants.ADOBE_PDF_NAMESPACE, "pdf:Keywords");
+ desc.appendChild(el);
+ el.appendChild(doc.createTextNode(info.getKeywords()));
+ }
+ if (info.getProducer() != null) {
+ el = doc.createElementNS(XMPConstants.ADOBE_PDF_NAMESPACE, "pdf:Producer");
+ desc.appendChild(el);
+ el.appendChild(doc.createTextNode(info.getProducer()));
+ }
+ el = doc.createElementNS(XMPConstants.ADOBE_PDF_NAMESPACE, "pdf:PDFVersion");
+ desc.appendChild(el);
+ el.appendChild(doc.createTextNode(pdfDoc.getPDFVersionString()));
return doc;
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
index d0f65d16f..33602792d 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
@@ -62,6 +62,7 @@ import org.apache.fop.fonts.FontSetup;
import org.apache.fop.image.FopImage;
import org.apache.fop.image.ImageFactory;
import org.apache.fop.image.XMLImage;
+import org.apache.fop.pdf.PDFAMode;
import org.apache.fop.pdf.PDFAnnotList;
import org.apache.fop.pdf.PDFColor;
import org.apache.fop.pdf.PDFDocument;
@@ -109,6 +110,9 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
*/
public static final String MIME_TYPE = MimeConstants.MIME_PDF;
+ /** Rendering Options key for the PDF/A mode. */
+ public static final String PDF_A_MODE = "pdf-a-mode";
+
/** Controls whether comments are written to the PDF stream. */
protected static final boolean WRITE_COMMENTS = true;
@@ -117,6 +121,9 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
*/
protected PDFDocument pdfDoc;
+ /** the PDF/A mode (Default: disabled) */
+ protected PDFAMode pdfAMode = PDFAMode.DISABLED;
+
/**
* Map of pages using the PageViewport as the key
* this is used for prepared pages that cannot be immediately
@@ -230,6 +237,11 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
} else {
this.fontList.addAll(cfgFonts);
}
+
+ String s = cfg.getChild(PDF_A_MODE, true).getValue(null);
+ if (s != null) {
+ this.pdfAMode = PDFAMode.valueOf(s);
+ }
}
/**
@@ -237,6 +249,10 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
*/
public void setUserAgent(FOUserAgent agent) {
super.setUserAgent(agent);
+ String s = (String)agent.getRendererOptions().get(PDF_A_MODE);
+ if (s != null) {
+ this.pdfAMode = PDFAMode.valueOf(s);
+ }
}
/**
@@ -249,6 +265,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
ostream = stream;
this.pdfDoc = new PDFDocument(
userAgent.getProducer() != null ? userAgent.getProducer() : "");
+ this.pdfDoc.setPDFAMode(this.pdfAMode);
this.pdfDoc.setCreator(userAgent.getCreator());
this.pdfDoc.setCreationDate(userAgent.getCreationDate());
this.pdfDoc.getInfo().setAuthor(userAgent.getAuthor());