Browse Source

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
tags/fop-0_92-beta
Jeremias Maerki 18 years ago
parent
commit
bb601dc8aa

+ 3
- 0
src/java/org/apache/fop/fo/extensions/xmp/XMPConstants.java View File

@@ -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/";
}

+ 71
- 0
src/java/org/apache/fop/pdf/PDFAMode.java View File

@@ -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;
}
}

+ 46
- 0
src/java/org/apache/fop/pdf/PDFConformanceException.java View File

@@ -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);
}
}

+ 45
- 9
src/java/org/apache/fop/pdf/PDFDocument.java View File

@@ -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;
@@ -107,6 +103,12 @@ public class PDFDocument {
/** Indicates what PDF version is active */
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
*/
@@ -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,

+ 6
- 1
src/java/org/apache/fop/pdf/PDFInfo.java View File

@@ -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;
}

+ 30
- 2
src/java/org/apache/fop/pdf/PDFMetadata.java View File

@@ -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;
}

+ 17
- 0
src/java/org/apache/fop/render/pdf/PDFRenderer.java View File

@@ -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());

Loading…
Cancel
Save