diff options
author | Jeremias Maerki <jeremias@apache.org> | 2006-04-05 14:21:39 +0000 |
---|---|---|
committer | Jeremias Maerki <jeremias@apache.org> | 2006-04-05 14:21:39 +0000 |
commit | d1c0afa0a3a277b02c7301c37fd6b9fb34f0d2ba (patch) | |
tree | 50175cf4652eb077bb84db07cfc363cd498dac79 /src | |
parent | 05ff967d64a3050a5ce08cc435bd91844efcf89d (diff) | |
download | xmlgraphics-fop-d1c0afa0a3a277b02c7301c37fd6b9fb34f0d2ba.tar.gz xmlgraphics-fop-d1c0afa0a3a277b02c7301c37fd6b9fb34f0d2ba.zip |
Improved JUnit report creation.
Added support for OutputIntent objects in PDF. When PDF/A-1b is activated OutputIntents are created and the sRGB color space is used by default (hardcoded for the moment for lack of better color infrastructure in FOP).
The sRGB color profile from HP (covering sRGB IEC61966-2.1) is now embedded in fop.jar as a resource so the PDF library can embed it. The sRGB profile from the Sun JRE is much bigger. That's why it's not used.
The Gladiator TrueType font (glb12.ttf) has been copied over from Batik and is used to verify PDF/A-1b's conformance checks.
CMYK JPEG image added to test resources so PDF/A-1b color space checks can be performed.
With the color space checks, support for PDF/A-1b is complete to the degree that FOP supports the creation of elements described in ISO 19005-1, except for the case where an embedded XMP packet is used in the fo:declarations element. In this case the metadata is not synchronized with the values in the Info PDF object which could lead to validation errors when checking for PDF/A-1b conformance.
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@391624 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFDocument.java | 2 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFFactory.java | 10 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFOutputIntent.java | 173 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFRoot.java | 29 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm | bin | 0 -> 3144 bytes | |||
-rw-r--r-- | src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm.LICENSE.txt | 14 | ||||
-rw-r--r-- | src/java/org/apache/fop/render/pdf/FopPDFImage.java | 14 | ||||
-rw-r--r-- | src/java/org/apache/fop/render/pdf/PDFRenderer.java | 58 | ||||
-rw-r--r-- | src/java/org/apache/fop/svg/PDFGraphics2D.java | 7 |
9 files changed, 305 insertions, 2 deletions
diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java index 79b7e52e1..f97c498b6 100644 --- a/src/java/org/apache/fop/pdf/PDFDocument.java +++ b/src/java/org/apache/fop/pdf/PDFDocument.java @@ -285,7 +285,7 @@ public class PDFDocument { if (mode == PDFAMode.PDFA_1A) { throw new UnsupportedOperationException("PDF/A-1a is not implemented, yet"); } else if (mode == PDFAMode.PDFA_1B) { - log.warn("Please note: PDF/A-1b is 'WORK IN PROGRESS' and not fully supported, yet!"); + //you got the green light! } this.pdfAMode = mode; } diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index 6222cf1f0..4fddd3e35 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -150,6 +150,16 @@ public class PDFFactory { } /** + * Make a OutputIntent dictionary. + * @return the newly created OutputIntent dictionary + */ + public PDFOutputIntent makeOutputIntent() { + PDFOutputIntent outputIntent = new PDFOutputIntent(); + getDocument().registerObject(outputIntent); + return outputIntent; + } + + /** * Make a /Page object. The page is assigned an object number immediately * so references can already be made. The page must be added to the * PDFDocument later using addObject(). diff --git a/src/java/org/apache/fop/pdf/PDFOutputIntent.java b/src/java/org/apache/fop/pdf/PDFOutputIntent.java new file mode 100644 index 000000000..a18f0e351 --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFOutputIntent.java @@ -0,0 +1,173 @@ +/* + * 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; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Represents the OutputIntent dictionary. + * @since PDF 1.4 + */ +public class PDFOutputIntent extends PDFObject { + + /** Subtype for PDF/X output intents */ + public static final String GTS_PDFX = "GTS_PDFX"; + /** Subtype for PDF/A-1 output intents */ + public static final String GTS_PDFA1 = "GTS_PDFA1"; + + private String subtype; //S in the PDF spec + private String outputCondition; + private String outputConditionIdentifier; + private String registryName; + private String info; + private PDFICCStream destOutputProfile; + + + /** @return the output intent subtype. */ + public String getSubtype() { + return subtype; + } + + /** + * Sets the output intent subtype. + * @param subtype the subtype (usually "GTS_PDFX") + */ + public void setSubtype(String subtype) { + this.subtype = subtype; + } + + /** @return the OutputCondition field */ + public String getOutputCondition() { + return outputCondition; + } + + /** + * Sets the human-readable form of the output condition. + * @param outputCondition A text string concisely identifying the intended output + * device or production condition in human-readable form. + */ + public void setOutputCondition(String outputCondition) { + this.outputCondition = outputCondition; + } + + /** @return the OutputConditionIdentifier field */ + public String getOutputConditionIdentifier() { + return outputConditionIdentifier; + } + + /** + * Sets the identifier for the output condition. + * @param outputConditionIdentifier A string identifying the intended output device or + * production condition in human- or machine-readable form. + */ + public void setOutputConditionIdentifier(String outputConditionIdentifier) { + this.outputConditionIdentifier = outputConditionIdentifier; + } + + /** @return the RegistryName field */ + public String getRegistryName() { + return registryName; + } + + /** + * Sets the registry name. + * @param registryName A string (conventionally a uniform resource identifier, + * or URI) identifying the registry in which the condition designated + * by OutputConditionIdentifier is defined. + */ + public void setRegistryName(String registryName) { + this.registryName = registryName; + } + + /** @return the Info field */ + public String getInfo() { + return info; + } + + /** + * Sets the Info field. + * @param info A human-readable text string containing additional information or comments about + * the intended target device or production condition. + */ + public void setInfo(String info) { + this.info = info; + } + + /** @return the DestOutputProfile */ + public PDFICCStream getDestOutputProfile() { + return destOutputProfile; + } + + /** + * Sets the destination ICC profile. + * @param destOutputProfile An ICC profile stream defining the transformation from the PDF + * document’s source colors to output device colorants. + */ + public void setDestOutputProfile(PDFICCStream destOutputProfile) { + this.destOutputProfile = destOutputProfile; + } + + /** @see org.apache.fop.pdf.PDFObject#toPDF() */ + public byte[] toPDF() { + ByteArrayOutputStream bout = new ByteArrayOutputStream(128); + try { + bout.write(encode(getObjectID())); + bout.write(encode("<<\n")); + bout.write(encode("/Type /OutputIntent\n")); + + bout.write(encode("/S /")); + bout.write(encode(this.subtype)); + bout.write(encode("\n")); + + if (outputCondition != null) { + bout.write(encode("/OutputCondition ")); + bout.write(encodeText(this.outputCondition)); + bout.write(encode("\n")); + } + + bout.write(encode("/OutputConditionIdentifier ")); + bout.write(encodeText(this.outputConditionIdentifier)); + bout.write(encode("\n")); + + if (registryName != null) { + bout.write(encode("/RegistryName ")); + bout.write(encodeText(this.registryName)); + bout.write(encode("\n")); + } + + if (info != null) { + bout.write(encode("/Info ")); + bout.write(encodeText(this.info)); + bout.write(encode("\n")); + } + + if (destOutputProfile != null) { + bout.write(encode("/DestOutputProfile " + destOutputProfile.referencePDF() + "\n")); + } + + bout.write(encode(">>\nendobj\n")); + } catch (IOException ioe) { + log.error("Ignored I/O exception", ioe); + } + return bout.toByteArray(); + } + + +} diff --git a/src/java/org/apache/fop/pdf/PDFRoot.java b/src/java/org/apache/fop/pdf/PDFRoot.java index 3d483f587..9ac4e1b0f 100644 --- a/src/java/org/apache/fop/pdf/PDFRoot.java +++ b/src/java/org/apache/fop/pdf/PDFRoot.java @@ -18,6 +18,8 @@ package org.apache.fop.pdf; +import java.util.List; + /** * class representing a Root (/Catalog) object */ @@ -56,6 +58,9 @@ public class PDFRoot extends PDFObject { /** Optional Metadata object */ private PDFMetadata metadata; + /** The array of OutputIntents */ + private List outputIntents; + private int pageMode = PAGEMODE_USENONE; /** @@ -137,6 +142,17 @@ public class PDFRoot extends PDFObject { } /** + * Adds an OutputIntent to the PDF + * @param outputIntent the OutputIntent dictionary + */ + public void addOutputIntent(PDFOutputIntent outputIntent) { + if (this.outputIntents == null) { + this.outputIntents = new java.util.ArrayList(); + } + this.outputIntents.add(outputIntent); + } + + /** * @see org.apache.fop.pdf.PDFObject#toPDFString() */ public String toPDFString() { @@ -168,6 +184,19 @@ public class PDFRoot extends PDFObject { && getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) { p.append("/Metadata " + getMetadata().referencePDF() + "\n"); } + if (this.outputIntents != null + && this.outputIntents.size() > 0 + && getDocumentSafely().getPDFVersion() >= PDFDocument.PDF_VERSION_1_4) { + p.append("/OutputIntents ["); + for (int i = 0, c = this.outputIntents.size(); i < c; i++) { + PDFOutputIntent outputIntent = (PDFOutputIntent)this.outputIntents.get(i); + if (i > 0) { + p.append(" "); + } + p.append(outputIntent.referencePDF()); + } + p.append("]\n"); + } p.append(">>\nendobj\n"); return p.toString(); } diff --git a/src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm b/src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm Binary files differnew file mode 100644 index 000000000..7f9d18d09 --- /dev/null +++ b/src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm diff --git a/src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm.LICENSE.txt b/src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm.LICENSE.txt new file mode 100644 index 000000000..9b817e339 --- /dev/null +++ b/src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm.LICENSE.txt @@ -0,0 +1,14 @@ +Obtained from: http://www.srgb.com/usingsrgb.html + +The file "sRGB Color Space Profile.icm" is: +Copyright (c) 1998 Hewlett-Packard Company + +To anyone who acknowledges that the file "sRGB Color Space Profile.icm" +is provided "AS IS" WITH NO EXPRESS OR IMPLIED WARRANTY: +permission to use, copy and distribute this file for any purpose is hereby +granted without fee, provided that the file is not changed including the HP +copyright notice tag, and that the name of Hewlett-Packard Company not be +used in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. Hewlett-Packard Company makes +no representations about the suitability of this software for any purpose. + diff --git a/src/java/org/apache/fop/render/pdf/FopPDFImage.java b/src/java/org/apache/fop/render/pdf/FopPDFImage.java index d1beb0961..2ecdcbcec 100644 --- a/src/java/org/apache/fop/render/pdf/FopPDFImage.java +++ b/src/java/org/apache/fop/render/pdf/FopPDFImage.java @@ -17,6 +17,7 @@ /* $Id$ */ package org.apache.fop.render.pdf; +import org.apache.fop.pdf.PDFConformanceException; import org.apache.fop.pdf.PDFFilterList; import org.apache.fop.pdf.PDFImage; import org.apache.fop.pdf.PDFFilter; @@ -24,6 +25,7 @@ import org.apache.fop.pdf.PDFICCStream; import org.apache.fop.pdf.PDFColor; import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.DCTFilter; +import org.apache.fop.pdf.CCFFilter; import org.apache.fop.pdf.PDFColorSpace; import org.apache.fop.pdf.PDFXObject; import org.apache.fop.pdf.BitmapImage; @@ -36,7 +38,6 @@ import java.io.IOException; import java.io.OutputStream; import java.awt.color.ColorSpace; import java.awt.color.ICC_Profile; -import org.apache.fop.pdf.CCFFilter; /** * PDFImage implementation for the PDF renderer. @@ -129,6 +130,17 @@ public class FopPDFImage implements PDFImage { PDFXObject xobj = doc.addImage(null, fopimg); softMaskRef = xobj.referencePDF(); } + if (doc.getPDFAMode().isPDFA1LevelB()) { + if (pdfCS != null + && pdfCS.getColorSpace() != PDFColorSpace.DEVICE_RGB + && pdfCS.getColorSpace() != PDFColorSpace.DEVICE_GRAY + && prof == null) { + //See PDF/A-1, ISO 19005:1:2005(E), 6.2.3.3 + //FOP is currently restricted to DeviceRGB if PDF/A-1 is active. + throw new PDFConformanceException( + "PDF/A-1 does not allow mixing DeviceRGB and DeviceCMYK."); + } + } } /** diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java index 68e9aa022..48eef1750 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java @@ -20,8 +20,12 @@ package org.apache.fop.render.pdf; // Java import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; +import java.io.UnsupportedEncodingException; import java.awt.Color; +import java.awt.color.ColorSpace; +import java.awt.color.ICC_Profile; import java.awt.geom.Rectangle2D; import java.awt.geom.AffineTransform; import java.util.Iterator; @@ -34,6 +38,7 @@ import org.w3c.dom.Document; // Avalon import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.commons.io.IOUtils; // FOP import org.apache.fop.apps.FOPException; @@ -69,11 +74,13 @@ import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.PDFEncryptionManager; import org.apache.fop.pdf.PDFEncryptionParams; import org.apache.fop.pdf.PDFFilterList; +import org.apache.fop.pdf.PDFICCStream; import org.apache.fop.pdf.PDFInfo; import org.apache.fop.pdf.PDFLink; import org.apache.fop.pdf.PDFMetadata; import org.apache.fop.pdf.PDFNumber; import org.apache.fop.pdf.PDFOutline; +import org.apache.fop.pdf.PDFOutputIntent; import org.apache.fop.pdf.PDFPage; import org.apache.fop.pdf.PDFResourceContext; import org.apache.fop.pdf.PDFResources; @@ -352,11 +359,62 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { this.pdfDoc.setFilterMap(filterMap); this.pdfDoc.outputHeader(stream); + if (pdfAMode.isPDFA1LevelB()) { + log.debug("PDF/A is active. Conformance Level: " + pdfAMode); + addPDFA1OutputIntent(); + } + //Setup encryption if necessary PDFEncryptionManager.setupPDFEncryption(encryptionParams, this.pdfDoc); } /** + * Adds an OutputIntent to the PDF as mandated by PDF/A-1 when uncalibrated color spaces + * are used (which is true if we use DeviceRGB to represent sRGB colors). + * @throws IOException in case of an I/O problem + */ + private void addPDFA1OutputIntent() throws IOException { + PDFOutputIntent outputIntent = pdfDoc.getFactory().makeOutputIntent(); + outputIntent.setSubtype(PDFOutputIntent.GTS_PDFA1); + PDFICCStream icc = pdfDoc.getFactory().makePDFICCStream(); + ICC_Profile profile; + InputStream in = PDFDocument.class.getResourceAsStream("sRGB Color Space Profile.icm"); + if (in != null) { + try { + profile = ICC_Profile.getInstance(in); + } finally { + IOUtils.closeQuietly(in); + } + } else { + //Fallback: Use the sRGB profile from the JRE (about 140KB) + profile = ICC_Profile.getInstance(ColorSpace.CS_sRGB); + } + String desc = getICCProfileDescription(profile); + + icc.setColorSpace(profile, null); + outputIntent.setDestOutputProfile(icc); + outputIntent.setOutputConditionIdentifier(desc); + outputIntent.setInfo(outputIntent.getOutputConditionIdentifier()); + pdfDoc.getRoot().addOutputIntent(outputIntent); + } + + private String getICCProfileDescription(ICC_Profile profile) { + byte[] data = profile.getData(ICC_Profile.icSigProfileDescriptionTag); + if (data == null) { + return null; + } else { + //Info on the data format: http://www.color.org/ICC-1_1998-09.PDF + int length = (data[8] << 3 * 8) | (data[9] << 2 * 8) | (data[10] << 8) | data[11]; + length--; //Remove trailing NUL character + try { + return new String(data, 12, length, "US-ASCII"); + } catch (UnsupportedEncodingException e) { + throw new UnsupportedOperationException("Incompatible VM"); + } + } + } + + /** * @see org.apache.fop.render.Renderer#stopRenderer() */ public void stopRenderer() throws IOException { diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java index 59cb6612d..31f0567a2 100644 --- a/src/java/org/apache/fop/svg/PDFGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java @@ -18,6 +18,7 @@ package org.apache.fop.svg; +import org.apache.fop.pdf.PDFConformanceException; import org.apache.fop.pdf.PDFResourceContext; import org.apache.fop.pdf.PDFResources; import org.apache.fop.pdf.PDFGState; @@ -812,6 +813,12 @@ public class PDFGraphics2D extends AbstractGraphics2D { currentStream.write(currentColour.getColorSpaceOut(fill)); } else if (c.getColorSpace().getType() == ColorSpace.TYPE_CMYK) { + if (pdfDoc.getPDFAMode().isPDFA1LevelB()) { + //See PDF/A-1, ISO 19005:1:2005(E), 6.2.3.3 + //FOP is currently restricted to DeviceRGB if PDF/A-1 is active. + throw new PDFConformanceException( + "PDF/A-1 does not allow mixing DeviceRGB and DeviceCMYK."); + } float[] cComps = c.getColorComponents(new float[3]); double[] cmyk = new double[3]; for (int i = 0; i < 3; i++) { |