aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README21
-rw-r--r--build.xml58
-rw-r--r--src/java/org/apache/fop/pdf/PDFDocument.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFFactory.java10
-rw-r--r--src/java/org/apache/fop/pdf/PDFOutputIntent.java173
-rw-r--r--src/java/org/apache/fop/pdf/PDFRoot.java29
-rw-r--r--src/java/org/apache/fop/pdf/sRGB Color Space Profile.icmbin0 -> 3144 bytes
-rw-r--r--src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm.LICENSE.txt14
-rw-r--r--src/java/org/apache/fop/render/pdf/FopPDFImage.java14
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderer.java58
-rw-r--r--src/java/org/apache/fop/svg/PDFGraphics2D.java7
-rw-r--r--test/java/org/apache/fop/StandardTestSuite.java45
-rw-r--r--test/java/org/apache/fop/render/pdf/PDFAConformanceTestCase.java157
-rw-r--r--test/resources/fonts/glb12.README.txt9
-rwxr-xr-xtest/resources/fonts/glb12.ttfbin0 -> 53460 bytes
-rw-r--r--test/resources/fonts/glb12.ttf.ansi.xml282
-rw-r--r--test/resources/fonts/glb12.ttf.xml397
-rw-r--r--test/resources/images/cmyk.jpgbin0 -> 1785 bytes
-rw-r--r--test/test.xconf18
-rw-r--r--test/xml/pdf-a/base14-font.fo13
-rw-r--r--test/xml/pdf-a/minimal-pdf-a.fo13
-rw-r--r--test/xml/pdf-a/with-cmyk-images.fo14
-rw-r--r--test/xml/pdf-a/with-eps.fo14
-rw-r--r--test/xml/pdf-a/with-rgb-images.fo14
24 files changed, 1319 insertions, 43 deletions
diff --git a/README b/README
index fc6ae1bea..bd51f057d 100644
--- a/README
+++ b/README
@@ -27,6 +27,27 @@ Apache FOP is part of Apache's XML Graphics project. The homepage of Apache
FOP is http://xmlgraphics.apache.org/fop/.
+Legal information
+---------------------
+
+Apache FOP is published under the Apache License version 2.0. For the license
+text, please see the following files:
+- LICENSE
+- NOTICE
+
+Legal information on libraries used by Apache FOP can be found in the
+"lib/README.txt" file.
+
+Here is a list of files included in Apache FOP but not published under Apache
+License version 2.0:
+- sRGB IEC61966-2.1 color profile
+ File: src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm
+ Info: src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm.LICENSE.txt
+- K3 3 of 9 barcode font (TrueType)
+ File: examples/fo/advanced/K3.TTF
+ Info: example/fo/advanced/K3.README
+
+
Where to get help?
---------------------
diff --git a/build.xml b/build.xml
index e89fad850..5cb3c81a6 100644
--- a/build.xml
+++ b/build.xml
@@ -397,6 +397,8 @@ list of possible build targets.
<copy todir="${build.classes.dir}">
<fileset dir="${src.java.dir}">
<include name="META-INF/**"/>
+ <include name="**/*.icm"/>
+ <include name="**/*.LICENSE.txt"/>
</fileset>
</copy>
<mkdir dir="${build.viewer.resources.dir}"/>
@@ -670,6 +672,7 @@ list of possible build targets.
</target>
<target name="junit-compile" depends="package, transcoder-pkg, junit-with-xmlunit, junit-without-xmlunit" description="Runs FOP's JUnit tests" if="junit.present">
<mkdir dir="${build.dir}/test-classes"/>
+ <mkdir dir="${build.dir}/test-reports"/>
<javac destdir="${build.dir}/test-classes" fork="${javac.fork}"
debug="${javac.debug}" deprecation="${javac.deprecation}"
optimize="${javac.optimize}" source="${javac.source}"
@@ -687,11 +690,11 @@ list of possible build targets.
<target name="junit-transcoder" depends="junit-compile" description="Runs FOP's JUnit transcoder tests" if="junit.present">
<echo message="Running basic functionality tests for fop-transcoder.jar"/>
- <mkdir dir="${build.dir}/test-reports/fop-transcoder"/>
<junit haltonerror="no" fork="${junit.fork}">
<sysproperty key="basedir" value="${basedir}"/>
<sysproperty key="jawa.awt.headless" value="true"/>
<formatter type="brief" usefile="false"/>
+ <formatter type="plain" usefile="true"/>
<classpath>
<pathelement location="${build.dir}/test-classes"/>
<path refid="libs-build-classpath"/>
@@ -699,11 +702,7 @@ list of possible build targets.
<include name="fop-transcoder.jar"/>
</fileset>
</classpath>
- <batchtest todir="${build.dir}/test-reports/fop-transcoder">
- <fileset dir="${build.dir}/test-classes">
- <include name="org/apache/fop/BasicTranscoderTestSuite.class"/>
- </fileset>
- </batchtest>
+ <test name="org.apache.fop.BasicTranscoderTestSuite" todir="${build.dir}/test-reports" outfile="TEST-transcoder"/>
</junit>
<echo message="Running basic functionality tests for fop-transcoder-allinone.jar"/>
<!-- These are the same tests as in the block above but testing the "allinone" JAR
@@ -712,11 +711,11 @@ list of possible build targets.
previous test block succeeded it indicates that the packaging of the allinone
JAR needs to be updated.
-->
- <mkdir dir="${build.dir}/test-reports/fop-transcoder-allinone"/>
<junit haltonerror="no" fork="${junit.fork}" errorproperty="fop.junit.error" failureproperty="fop.junit.failure">
<sysproperty key="basedir" value="${basedir}"/>
<sysproperty key="jawa.awt.headless" value="true"/>
<formatter type="brief" usefile="false"/>
+ <formatter type="plain" usefile="true"/>
<classpath>
<pathelement location="${build.dir}/test-classes"/>
<fileset dir="build">
@@ -728,21 +727,17 @@ list of possible build targets.
<include name="batik*.jar"/>
</fileset>
</classpath>
- <batchtest todir="${build.dir}/test-reports/fop-transcoder-allinone">
- <fileset dir="${build.dir}/test-classes">
- <include name="org/apache/fop/BasicTranscoderTestSuite.class"/>
- </fileset>
- </batchtest>
+ <test name="org.apache.fop.BasicTranscoderTestSuite" todir="${build.dir}/test-reports" outfile="TEST-transcoder-allinone"/>
</junit>
</target>
<target name="junit-basic" depends="junit-compile" description="Runs FOP's JUnit basic tests" if="junit.present">
<echo message="Running basic functionality tests for fop.jar"/>
- <mkdir dir="${build.dir}/test-reports/fop"/>
<junit haltonerror="no" fork="${junit.fork}" errorproperty="fop.junit.error" failureproperty="fop.junit.failure">
<sysproperty key="basedir" value="${basedir}"/>
<sysproperty key="jawa.awt.headless" value="true"/>
<formatter type="brief" usefile="false"/>
+ <formatter type="plain" usefile="true"/>
<classpath>
<pathelement location="${build.dir}/test-classes"/>
<path refid="libs-build-classpath"/>
@@ -750,12 +745,7 @@ list of possible build targets.
<include name="fop.jar"/>
</fileset>
</classpath>
- <batchtest todir="${build.dir}/test-reports/fop">
- <fileset dir="${build.dir}/test-classes">
- <include name="org/apache/fop/BasicDriverTestSuite.class"/>
- <include name="org/apache/fop/UtilityCodeTestSuite.class"/>
- </fileset>
- </batchtest>
+ <test name="org.apache.fop.StandardTestSuite" todir="${build.dir}/test-reports"/>
</junit>
</target>
@@ -779,43 +769,35 @@ list of possible build targets.
<target name="junit-layout-standard" depends="junit-compile" if="junit.present" description="Runs FOP's standard JUnit layout tests">
<echo message="Running standard layout engine tests"/>
- <mkdir dir="${build.dir}/test-reports/fop"/>
<junit haltonerror="no" fork="${junit.fork}" errorproperty="fop.junit.error" failureproperty="fop.junit.failure">
<sysproperty key="basedir" value="${basedir}"/>
<sysproperty key="jawa.awt.headless" value="true"/>
<sysproperty key="fop.layoutengine.disabled" value="${layoutengine.disabled}"/>
<sysproperty key="fop.layoutengine.testset" value="standard"/>
<formatter type="brief" usefile="false"/>
+ <formatter type="plain" usefile="true"/>
<classpath>
<pathelement location="${build.dir}/test-classes"/>
<path refid="libs-run-classpath"/>
</classpath>
- <batchtest todir="${build.dir}/test-reports/fop">
- <fileset dir="${build.dir}/test-classes">
- <include name="org/apache/fop/layoutengine/LayoutEngineTestSuite.class"/>
- </fileset>
- </batchtest>
+ <test name="org.apache.fop.layoutengine.LayoutEngineTestSuite" todir="${build.dir}/test-reports" outfile="TEST-layoutengine-standard"/>
</junit>
</target>
<target name="junit-layout-hyphenation" depends="hyphenation-present, junit-compile" if="hyphenation.present" description="Runs FOP's JUnit hyphenation layout tests">
<echo message="Running hyphenation layout engine tests"/>
- <mkdir dir="${build.dir}/test-reports/fop"/>
<junit haltonerror="no" fork="${junit.fork}" errorproperty="fop.junit.error" failureproperty="fop.junit.failure">
<sysproperty key="basedir" value="${basedir}"/>
<sysproperty key="jawa.awt.headless" value="true"/>
<sysproperty key="fop.layoutengine.disabled" value="${layoutengine.disabled}"/>
<sysproperty key="fop.layoutengine.testset" value="hyphenation"/>
<formatter type="brief" usefile="false"/>
+ <formatter type="plain" usefile="true"/>
<classpath>
<pathelement location="${build.dir}/test-classes"/>
<path refid="libs-run-classpath"/>
</classpath>
- <batchtest todir="${build.dir}/test-reports/fop">
- <fileset dir="${build.dir}/test-classes">
- <include name="org/apache/fop/layoutengine/LayoutEngineTestSuite.class"/>
- </fileset>
- </batchtest>
+ <test name="org.apache.fop.layoutengine.LayoutEngineTestSuite" todir="${build.dir}/test-reports" outfile="TEST-layoutengine-hyphenation"/>
</junit>
</target>
@@ -828,6 +810,7 @@ list of possible build targets.
<sysproperty key="jawa.awt.headless" value="true"/>
<sysproperty key="fop.layoutengine.disabled" value="${fotree.disabled}"/>
<formatter type="brief" usefile="false"/>
+ <formatter type="plain" usefile="true"/>
<classpath>
<pathelement location="${build.dir}/test-classes"/>
<path refid="libs-build-classpath"/>
@@ -835,11 +818,7 @@ list of possible build targets.
<include name="fop.jar"/>
</fileset>
</classpath>
- <batchtest todir="${build.dir}/test-reports/fop">
- <fileset dir="${build.dir}/test-classes">
- <include name="org/apache/fop/fotreetest/FOTreeTestSuite.class"/>
- </fileset>
- </batchtest>
+ <test name="org.apache.fop.fotreetest.FOTreeTestSuite" todir="${build.dir}/test-reports" outfile="TEST-FO-tree"/>
</junit>
</target>
@@ -851,6 +830,7 @@ list of possible build targets.
<sysproperty key="fop.layoutengine.disabled" value="${layoutengine.disabled}"/>
<sysproperty key="fop.layoutengine.testset" value="standard"/>
<formatter type="brief" usefile="false"/>
+ <formatter type="plain" usefile="true"/>
<classpath>
<pathelement location="${build.dir}/test-classes"/>
<path refid="libs-build-classpath"/>
@@ -858,11 +838,7 @@ list of possible build targets.
<include name="fop.jar"/>
</fileset>
</classpath>
- <batchtest todir="${build.dir}/test-reports/fop">
- <fileset dir="${build.dir}/test-classes">
- <include name="org/apache/fop/intermediate/IntermediateFormatTestSuite.class"/>
- </fileset>
- </batchtest>
+ <test name="org.apache.fop.intermediate.IntermediateFormatTestSuite" todir="${build.dir}/test-reports" outfile="TEST-intermediate-format"/>
</junit>
</target>
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
new file mode 100644
index 000000000..7f9d18d09
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/sRGB Color Space Profile.icm
Binary files differ
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++) {
diff --git a/test/java/org/apache/fop/StandardTestSuite.java b/test/java/org/apache/fop/StandardTestSuite.java
new file mode 100644
index 000000000..ee3c5add8
--- /dev/null
+++ b/test/java/org/apache/fop/StandardTestSuite.java
@@ -0,0 +1,45 @@
+/*
+ * 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: BasicDriverTestSuite.java 231325 2005-08-10 21:05:39Z jeremias $ */
+
+package org.apache.fop;
+
+import org.apache.fop.render.pdf.PDFAConformanceTestCase;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Test suite for basic functionality of FOP.
+ */
+public class StandardTestSuite {
+
+ /**
+ * Builds the test suite
+ * @return the test suite
+ */
+ public static Test suite() {
+ TestSuite suite = new TestSuite(
+ "Basic functionality test suite for FOP");
+ //$JUnit-BEGIN$
+ suite.addTest(BasicDriverTestSuite.suite());
+ suite.addTest(UtilityCodeTestSuite.suite());
+ suite.addTest(new TestSuite(PDFAConformanceTestCase.class));
+ //$JUnit-END$
+ return suite;
+ }
+}
diff --git a/test/java/org/apache/fop/render/pdf/PDFAConformanceTestCase.java b/test/java/org/apache/fop/render/pdf/PDFAConformanceTestCase.java
new file mode 100644
index 000000000..365ca51db
--- /dev/null
+++ b/test/java/org/apache/fop/render/pdf/PDFAConformanceTestCase.java
@@ -0,0 +1,157 @@
+/*
+ * 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.render.pdf;
+
+import java.io.File;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.output.ByteArrayOutputStream;
+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.pdf.PDFConformanceException;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests PDF/A-1 functionality.
+ */
+public class PDFAConformanceTestCase extends TestCase {
+
+ private TransformerFactory tFactory = TransformerFactory.newInstance();
+ private FopFactory fopFactory = FopFactory.newInstance();
+ private File foBaseDir = new File("test/xml/pdf-a");
+
+ /**
+ * Main constructor
+ * @param name the name of the test case
+ */
+ public PDFAConformanceTestCase(String name) {
+ super(name);
+ try {
+ fopFactory.setUserConfig(new File("test/test.xconf"));
+ } catch (Exception e) {
+ throw new RuntimeException("Configuring the FopFactory failed: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Convert the test file
+ * @param foFile the FO file
+ * @param ua the preconfigured user agent
+ * @throws Exception if the conversion fails
+ */
+ protected void convertFO(File foFile, FOUserAgent ua) throws Exception {
+ ua.getRendererOptions().put("pdf-a-mode", "PDF/A-1b");
+ File outFile = null;
+ ByteArrayOutputStream baout = new ByteArrayOutputStream();
+ Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, ua, baout);
+ Transformer transformer = tFactory.newTransformer();
+ Source src = new StreamSource(foFile);
+ SAXResult res = new SAXResult(fop.getDefaultHandler());
+ transformer.transform(src, res);
+ if (true) {
+ //Write to file for debugging
+ outFile = new File(foFile.getParentFile(), foFile.getName() + ".pdf");
+ FileUtils.writeByteArrayToFile(outFile, baout.toByteArray());
+ }
+ }
+
+ /**
+ * Test exception when PDF/A-1 is enabled and everything is as it should.
+ * @throws Exception if the test fails
+ */
+ public void testAllOk() throws Exception {
+ FOUserAgent ua = fopFactory.newFOUserAgent();
+ File foFile = new File(foBaseDir, "minimal-pdf-a.fo");
+ convertFO(foFile, ua);
+ }
+
+ /**
+ * Test exception when PDF/A-1 is enabled together with encryption.
+ * @throws Exception if the test fails
+ */
+ public void testNoEncryption() throws Exception {
+ FOUserAgent ua = fopFactory.newFOUserAgent();
+ ua.getRendererOptions().put("owner-password", "mypassword"); //To enabled encryption
+ File foFile = new File(foBaseDir, "minimal-pdf-a.fo");
+ try {
+ convertFO(foFile, ua);
+ fail("Expected PDFConformanceException. PDF/A-1 and PDF encryption don't go together.");
+ } catch (PDFConformanceException e) {
+ //Good!
+ }
+ }
+
+ /**
+ * Test exception when PDF/A-1 is enabled and a font is used which is not embedded.
+ * @throws Exception if the test fails
+ */
+ public void testFontNotEmbedded() throws Exception {
+ FOUserAgent ua = fopFactory.newFOUserAgent();
+ File foFile = new File(foBaseDir, "base14-font.fo");
+ try {
+ convertFO(foFile, ua);
+ fail("Expected PDFConformanceException. PDF/A-1 wants all fonts embedded.");
+ } catch (PDFConformanceException e) {
+ //Good!
+ }
+ }
+
+ /**
+ * Test exception when PDF/A-1 is enabled and an EPS is used.
+ * @throws Exception if the test fails
+ */
+ public void testEPSUsage() throws Exception {
+ FOUserAgent ua = fopFactory.newFOUserAgent();
+ File foFile = new File(foBaseDir, "with-eps.fo");
+ try {
+ convertFO(foFile, ua);
+ fail("Expected PDFConformanceException. PDF/A-1 does not allow PostScript XObjects.");
+ } catch (PDFConformanceException e) {
+ //Good!
+ }
+ }
+
+ /**
+ * Test exception when PDF/A-1 is enabled and images.
+ * @throws Exception if the test fails
+ */
+ public void testImages() throws Exception {
+ FOUserAgent ua = fopFactory.newFOUserAgent();
+ File foFile = new File(foBaseDir, "with-rgb-images.fo");
+ convertFO(foFile, ua);
+
+ foFile = new File(foBaseDir, "with-cmyk-images.fo");
+ try {
+ convertFO(foFile, ua);
+ fail("Expected PDFConformanceException. PDF/A-1 does not allow PostScript XObjects.");
+ } catch (PDFConformanceException e) {
+ //Good!
+ }
+ }
+
+}
diff --git a/test/resources/fonts/glb12.README.txt b/test/resources/fonts/glb12.README.txt
new file mode 100644
index 000000000..100046ad7
--- /dev/null
+++ b/test/resources/fonts/glb12.README.txt
@@ -0,0 +1,9 @@
+This Gladiator Bold font has been copied from Apache Batik. Origin:
+http://svn.apache.org/repos/asf/xmlgraphics/batik/trunk/samples/tests/resources/ttf/glb12.ttf
+
+It was added to that repository in July 2003:
+http://mail-archives.apache.org/mod_mbox/xmlgraphics-batik-dev/200307.mbox/%3c20030715155519.37236.qmail@icarus.apache.org%3e
+
+More information on that contribution can be found here:
+http://svn.apache.org/repos/asf/xmlgraphics/batik/trunk/contrib/fonts/gladiator/
+http://mail-archives.apache.org/mod_mbox/xmlgraphics-batik-dev/200306.mbox/ajax/%3c3EE07AA6.5080507@sun.com%3e \ No newline at end of file
diff --git a/test/resources/fonts/glb12.ttf b/test/resources/fonts/glb12.ttf
new file mode 100755
index 000000000..77ff2b490
--- /dev/null
+++ b/test/resources/fonts/glb12.ttf
Binary files differ
diff --git a/test/resources/fonts/glb12.ttf.ansi.xml b/test/resources/fonts/glb12.ttf.ansi.xml
new file mode 100644
index 000000000..18cd2650e
--- /dev/null
+++ b/test/resources/fonts/glb12.ttf.ansi.xml
@@ -0,0 +1,282 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<font-metrics type="TRUETYPE">
+ <font-name>Gladiator,Bold</font-name>
+ <embed/>
+ <cap-height>691</cap-height>
+ <x-height>429</x-height>
+ <ascender>765</ascender>
+ <descender>-234</descender>
+ <bbox>
+ <left>-157</left>
+ <bottom>-250</bottom>
+ <right>1181</right>
+ <top>1071</top>
+ </bbox>
+ <flags>33</flags>
+ <stemv>0</stemv>
+ <italicangle>0</italicangle>
+ <subtype>TRUETYPE</subtype>
+ <singlebyte-extras>
+ <encoding>WinAnsiEncoding</encoding>
+ <first-char>0</first-char>
+ <last-char>255</last-char>
+ <widths>
+ <char idx="0" wdt="432"/>
+ <char idx="1" wdt="432"/>
+ <char idx="2" wdt="432"/>
+ <char idx="3" wdt="432"/>
+ <char idx="4" wdt="432"/>
+ <char idx="5" wdt="432"/>
+ <char idx="6" wdt="432"/>
+ <char idx="7" wdt="432"/>
+ <char idx="8" wdt="432"/>
+ <char idx="9" wdt="432"/>
+ <char idx="10" wdt="432"/>
+ <char idx="11" wdt="432"/>
+ <char idx="12" wdt="432"/>
+ <char idx="13" wdt="432"/>
+ <char idx="14" wdt="432"/>
+ <char idx="15" wdt="432"/>
+ <char idx="16" wdt="432"/>
+ <char idx="17" wdt="432"/>
+ <char idx="18" wdt="432"/>
+ <char idx="19" wdt="432"/>
+ <char idx="20" wdt="432"/>
+ <char idx="21" wdt="432"/>
+ <char idx="22" wdt="432"/>
+ <char idx="23" wdt="432"/>
+ <char idx="24" wdt="432"/>
+ <char idx="25" wdt="432"/>
+ <char idx="26" wdt="432"/>
+ <char idx="27" wdt="432"/>
+ <char idx="28" wdt="432"/>
+ <char idx="29" wdt="432"/>
+ <char idx="30" wdt="432"/>
+ <char idx="31" wdt="432"/>
+ <char idx="32" wdt="332"/>
+ <char idx="33" wdt="376"/>
+ <char idx="34" wdt="525"/>
+ <char idx="35" wdt="980"/>
+ <char idx="36" wdt="625"/>
+ <char idx="37" wdt="754"/>
+ <char idx="38" wdt="886"/>
+ <char idx="39" wdt="311"/>
+ <char idx="40" wdt="398"/>
+ <char idx="41" wdt="398"/>
+ <char idx="42" wdt="601"/>
+ <char idx="43" wdt="652"/>
+ <char idx="44" wdt="311"/>
+ <char idx="45" wdt="331"/>
+ <char idx="46" wdt="311"/>
+ <char idx="47" wdt="500"/>
+ <char idx="48" wdt="541"/>
+ <char idx="49" wdt="541"/>
+ <char idx="50" wdt="541"/>
+ <char idx="51" wdt="541"/>
+ <char idx="52" wdt="541"/>
+ <char idx="53" wdt="541"/>
+ <char idx="54" wdt="541"/>
+ <char idx="55" wdt="541"/>
+ <char idx="56" wdt="541"/>
+ <char idx="57" wdt="541"/>
+ <char idx="58" wdt="311"/>
+ <char idx="59" wdt="311"/>
+ <char idx="60" wdt="500"/>
+ <char idx="61" wdt="805"/>
+ <char idx="62" wdt="500"/>
+ <char idx="63" wdt="569"/>
+ <char idx="64" wdt="805"/>
+ <char idx="65" wdt="729"/>
+ <char idx="66" wdt="708"/>
+ <char idx="67" wdt="725"/>
+ <char idx="68" wdt="748"/>
+ <char idx="69" wdt="683"/>
+ <char idx="70" wdt="665"/>
+ <char idx="71" wdt="769"/>
+ <char idx="72" wdt="808"/>
+ <char idx="73" wdt="347"/>
+ <char idx="74" wdt="347"/>
+ <char idx="75" wdt="769"/>
+ <char idx="76" wdt="644"/>
+ <char idx="77" wdt="1109"/>
+ <char idx="78" wdt="767"/>
+ <char idx="79" wdt="821"/>
+ <char idx="80" wdt="689"/>
+ <char idx="81" wdt="821"/>
+ <char idx="82" wdt="708"/>
+ <char idx="83" wdt="689"/>
+ <char idx="84" wdt="685"/>
+ <char idx="85" wdt="767"/>
+ <char idx="86" wdt="689"/>
+ <char idx="87" wdt="1181"/>
+ <char idx="88" wdt="729"/>
+ <char idx="89" wdt="729"/>
+ <char idx="90" wdt="644"/>
+ <char idx="91" wdt="542"/>
+ <char idx="92" wdt="500"/>
+ <char idx="93" wdt="542"/>
+ <char idx="94" wdt="253"/>
+ <char idx="95" wdt="432"/>
+ <char idx="96" wdt="311"/>
+ <char idx="97" wdt="515"/>
+ <char idx="98" wdt="556"/>
+ <char idx="99" wdt="492"/>
+ <char idx="100" wdt="556"/>
+ <char idx="101" wdt="517"/>
+ <char idx="102" wdt="311"/>
+ <char idx="103" wdt="556"/>
+ <char idx="104" wdt="575"/>
+ <char idx="105" wdt="311"/>
+ <char idx="106" wdt="311"/>
+ <char idx="107" wdt="556"/>
+ <char idx="108" wdt="311"/>
+ <char idx="109" wdt="839"/>
+ <char idx="110" wdt="575"/>
+ <char idx="111" wdt="547"/>
+ <char idx="112" wdt="556"/>
+ <char idx="113" wdt="556"/>
+ <char idx="114" wdt="440"/>
+ <char idx="115" wdt="492"/>
+ <char idx="116" wdt="359"/>
+ <char idx="117" wdt="575"/>
+ <char idx="118" wdt="476"/>
+ <char idx="119" wdt="711"/>
+ <char idx="120" wdt="496"/>
+ <char idx="121" wdt="476"/>
+ <char idx="122" wdt="496"/>
+ <char idx="123" wdt="432"/>
+ <char idx="124" wdt="311"/>
+ <char idx="125" wdt="432"/>
+ <char idx="126" wdt="253"/>
+ <char idx="127" wdt="432"/>
+ <char idx="128" wdt="432"/>
+ <char idx="129" wdt="432"/>
+ <char idx="130" wdt="432"/>
+ <char idx="131" wdt="432"/>
+ <char idx="132" wdt="432"/>
+ <char idx="133" wdt="432"/>
+ <char idx="134" wdt="432"/>
+ <char idx="135" wdt="432"/>
+ <char idx="136" wdt="253"/>
+ <char idx="137" wdt="432"/>
+ <char idx="138" wdt="0"/>
+ <char idx="139" wdt="432"/>
+ <char idx="140" wdt="0"/>
+ <char idx="141" wdt="432"/>
+ <char idx="142" wdt="0"/>
+ <char idx="143" wdt="432"/>
+ <char idx="144" wdt="432"/>
+ <char idx="145" wdt="311"/>
+ <char idx="146" wdt="311"/>
+ <char idx="147" wdt="525"/>
+ <char idx="148" wdt="525"/>
+ <char idx="149" wdt="432"/>
+ <char idx="150" wdt="855"/>
+ <char idx="151" wdt="1109"/>
+ <char idx="152" wdt="253"/>
+ <char idx="153" wdt="432"/>
+ <char idx="154" wdt="0"/>
+ <char idx="155" wdt="432"/>
+ <char idx="156" wdt="0"/>
+ <char idx="157" wdt="432"/>
+ <char idx="158" wdt="0"/>
+ <char idx="159" wdt="0"/>
+ <char idx="160" wdt="332"/>
+ <char idx="161" wdt="376"/>
+ <char idx="162" wdt="432"/>
+ <char idx="163" wdt="0"/>
+ <char idx="164" wdt="432"/>
+ <char idx="165" wdt="432"/>
+ <char idx="166" wdt="432"/>
+ <char idx="167" wdt="0"/>
+ <char idx="168" wdt="395"/>
+ <char idx="169" wdt="432"/>
+ <char idx="170" wdt="432"/>
+ <char idx="171" wdt="657"/>
+ <char idx="172" wdt="432"/>
+ <char idx="173" wdt="432"/>
+ <char idx="174" wdt="432"/>
+ <char idx="175" wdt="253"/>
+ <char idx="176" wdt="432"/>
+ <char idx="177" wdt="432"/>
+ <char idx="178" wdt="432"/>
+ <char idx="179" wdt="432"/>
+ <char idx="180" wdt="304"/>
+ <char idx="181" wdt="432"/>
+ <char idx="182" wdt="432"/>
+ <char idx="183" wdt="432"/>
+ <char idx="184" wdt="151"/>
+ <char idx="185" wdt="432"/>
+ <char idx="186" wdt="432"/>
+ <char idx="187" wdt="657"/>
+ <char idx="188" wdt="432"/>
+ <char idx="189" wdt="432"/>
+ <char idx="190" wdt="432"/>
+ <char idx="191" wdt="569"/>
+ <char idx="192" wdt="547"/>
+ <char idx="193" wdt="729"/>
+ <char idx="194" wdt="729"/>
+ <char idx="195" wdt="729"/>
+ <char idx="196" wdt="729"/>
+ <char idx="197" wdt="729"/>
+ <char idx="198" wdt="432"/>
+ <char idx="199" wdt="725"/>
+ <char idx="200" wdt="683"/>
+ <char idx="201" wdt="683"/>
+ <char idx="202" wdt="683"/>
+ <char idx="203" wdt="683"/>
+ <char idx="204" wdt="347"/>
+ <char idx="205" wdt="347"/>
+ <char idx="206" wdt="347"/>
+ <char idx="207" wdt="347"/>
+ <char idx="208" wdt="0"/>
+ <char idx="209" wdt="767"/>
+ <char idx="210" wdt="821"/>
+ <char idx="211" wdt="821"/>
+ <char idx="212" wdt="821"/>
+ <char idx="213" wdt="821"/>
+ <char idx="214" wdt="821"/>
+ <char idx="215" wdt="432"/>
+ <char idx="216" wdt="0"/>
+ <char idx="217" wdt="767"/>
+ <char idx="218" wdt="767"/>
+ <char idx="219" wdt="767"/>
+ <char idx="220" wdt="767"/>
+ <char idx="221" wdt="729"/>
+ <char idx="222" wdt="0"/>
+ <char idx="223" wdt="432"/>
+ <char idx="224" wdt="515"/>
+ <char idx="225" wdt="515"/>
+ <char idx="226" wdt="515"/>
+ <char idx="227" wdt="515"/>
+ <char idx="228" wdt="515"/>
+ <char idx="229" wdt="515"/>
+ <char idx="230" wdt="432"/>
+ <char idx="231" wdt="492"/>
+ <char idx="232" wdt="517"/>
+ <char idx="233" wdt="517"/>
+ <char idx="234" wdt="517"/>
+ <char idx="235" wdt="517"/>
+ <char idx="236" wdt="311"/>
+ <char idx="237" wdt="311"/>
+ <char idx="238" wdt="311"/>
+ <char idx="239" wdt="311"/>
+ <char idx="240" wdt="0"/>
+ <char idx="241" wdt="575"/>
+ <char idx="242" wdt="547"/>
+ <char idx="243" wdt="547"/>
+ <char idx="244" wdt="547"/>
+ <char idx="245" wdt="547"/>
+ <char idx="246" wdt="547"/>
+ <char idx="247" wdt="432"/>
+ <char idx="248" wdt="0"/>
+ <char idx="249" wdt="575"/>
+ <char idx="250" wdt="575"/>
+ <char idx="251" wdt="575"/>
+ <char idx="252" wdt="575"/>
+ <char idx="253" wdt="476"/>
+ <char idx="254" wdt="0"/>
+ <char idx="255" wdt="476"/>
+ </widths>
+ </singlebyte-extras>
+</font-metrics>
diff --git a/test/resources/fonts/glb12.ttf.xml b/test/resources/fonts/glb12.ttf.xml
new file mode 100644
index 000000000..b4dff29c2
--- /dev/null
+++ b/test/resources/fonts/glb12.ttf.xml
@@ -0,0 +1,397 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<!--
+
+Generated from glb12.ttf by Apache FOP's TTFReader
+
+-->
+<font-metrics type="TYPE0">
+ <font-name>Gladiator,Bold</font-name>
+ <embed/>
+ <cap-height>691</cap-height>
+ <x-height>0</x-height>
+ <ascender>765</ascender>
+ <descender>-234</descender>
+ <bbox>
+ <left>-157</left>
+ <bottom>-250</bottom>
+ <right>1181</right>
+ <top>1071</top>
+ </bbox>
+ <flags>33</flags>
+ <stemv>0</stemv>
+ <italicangle>0</italicangle>
+ <subtype>TYPE0</subtype>
+ <multibyte-extras>
+ <cid-type>CIDFontType2</cid-type>
+ <default-width>0</default-width>
+ <bfranges>
+ <bf gi="33" ue="33" us="32"/>
+ <bf gi="6" ue="34" us="34"/>
+ <bf gi="36" ue="38" us="35"/>
+ <bf gi="5" ue="39" us="39"/>
+ <bf gi="41" ue="91" us="40"/>
+ <bf gi="4" ue="92" us="92"/>
+ <bf gi="94" ue="93" us="93"/>
+ <bf gi="11" ue="94" us="94"/>
+ <bf gi="21" ue="96" us="96"/>
+ <bf gi="98" ue="122" us="97"/>
+ <bf gi="12" ue="124" us="124"/>
+ <bf gi="29" ue="126" us="126"/>
+ <bf gi="9" ue="161" us="161"/>
+ <bf gi="185" ue="163" us="163"/>
+ <bf gi="158" ue="167" us="167"/>
+ <bf gi="128" ue="168" us="168"/>
+ <bf gi="93" ue="171" us="171"/>
+ <bf gi="25" ue="175" us="175"/>
+ <bf gi="22" ue="180" us="180"/>
+ <bf gi="27" ue="184" us="184"/>
+ <bf gi="35" ue="187" us="187"/>
+ <bf gi="10" ue="191" us="191"/>
+ <bf gi="186" ue="197" us="192"/>
+ <bf gi="192" ue="214" us="199"/>
+ <bf gi="209" ue="222" us="216"/>
+ <bf gi="216" ue="229" us="224"/>
+ <bf gi="222" ue="246" us="231"/>
+ <bf gi="239" ue="255" us="248"/>
+ <bf gi="129" ue="258" us="258"/>
+ <bf gi="159" ue="259" us="259"/>
+ <bf gi="130" ue="260" us="260"/>
+ <bf gi="160" ue="261" us="261"/>
+ <bf gi="131" ue="262" us="262"/>
+ <bf gi="161" ue="263" us="263"/>
+ <bf gi="132" ue="268" us="268"/>
+ <bf gi="162" ue="269" us="269"/>
+ <bf gi="133" ue="270" us="270"/>
+ <bf gi="163" ue="271" us="271"/>
+ <bf gi="157" ue="273" us="273"/>
+ <bf gi="135" ue="280" us="280"/>
+ <bf gi="165" ue="281" us="281"/>
+ <bf gi="134" ue="282" us="282"/>
+ <bf gi="164" ue="283" us="283"/>
+ <bf gi="136" ue="286" us="286"/>
+ <bf gi="166" ue="287" us="287"/>
+ <bf gi="156" ue="304" us="304"/>
+ <bf gi="19" ue="305" us="305"/>
+ <bf gi="32" ue="307" us="307"/>
+ <bf gi="137" ue="313" us="313"/>
+ <bf gi="167" ue="314" us="314"/>
+ <bf gi="138" ue="317" us="317"/>
+ <bf gi="168" ue="318" us="318"/>
+ <bf gi="139" ue="321" us="321"/>
+ <bf gi="169" ue="322" us="322"/>
+ <bf gi="140" ue="323" us="323"/>
+ <bf gi="170" ue="324" us="324"/>
+ <bf gi="141" ue="327" us="327"/>
+ <bf gi="171" ue="328" us="328"/>
+ <bf gi="142" ue="336" us="336"/>
+ <bf gi="172" ue="337" us="337"/>
+ <bf gi="208" ue="338" us="338"/>
+ <bf gi="238" ue="339" us="339"/>
+ <bf gi="143" ue="340" us="340"/>
+ <bf gi="173" ue="341" us="341"/>
+ <bf gi="144" ue="344" us="344"/>
+ <bf gi="174" ue="345" us="345"/>
+ <bf gi="145" ue="346" us="346"/>
+ <bf gi="175" ue="347" us="347"/>
+ <bf gi="147" ue="350" us="350"/>
+ <bf gi="177" ue="351" us="351"/>
+ <bf gi="146" ue="352" us="352"/>
+ <bf gi="176" ue="353" us="353"/>
+ <bf gi="149" ue="354" us="354"/>
+ <bf gi="179" ue="355" us="355"/>
+ <bf gi="148" ue="356" us="356"/>
+ <bf gi="151" ue="366" us="366"/>
+ <bf gi="181" ue="367" us="367"/>
+ <bf gi="150" ue="368" us="368"/>
+ <bf gi="180" ue="369" us="369"/>
+ <bf gi="152" ue="377" us="376"/>
+ <bf gi="182" ue="378" us="378"/>
+ <bf gi="155" ue="379" us="379"/>
+ <bf gi="184" ue="380" us="380"/>
+ <bf gi="154" ue="381" us="381"/>
+ <bf gi="183" ue="382" us="382"/>
+ <bf gi="95" ue="710" us="710"/>
+ <bf gi="23" ue="711" us="711"/>
+ <bf gi="24" ue="728" us="728"/>
+ <bf gi="96" ue="729" us="729"/>
+ <bf gi="26" ue="730" us="730"/>
+ <bf gi="30" ue="731" us="731"/>
+ <bf gi="127" ue="732" us="732"/>
+ <bf gi="126" ue="733" us="733"/>
+ <bf gi="28" ue="823" us="823"/>
+ <bf gi="3" ue="915" us="915"/>
+ <bf gi="124" ue="8212" us="8211"/>
+ <bf gi="97" ue="8216" us="8216"/>
+ <bf gi="40" ue="8217" us="8217"/>
+ <bf gi="8" ue="8220" us="8220"/>
+ <bf gi="7" ue="8221" us="8221"/>
+ <bf gi="20" ue="63166" us="63166"/>
+ <bf gi="14" ue="64260" us="64256"/>
+ <bf gi="0" ue="65535" us="65535"/>
+ </bfranges>
+ <cid-widths start-index="0">
+ <wx w="432"/>
+ <wx w="0"/>
+ <wx w="333"/>
+ <wx w="665"/>
+ <wx w="500"/>
+ <wx w="311"/>
+ <wx w="525"/>
+ <wx w="525"/>
+ <wx w="525"/>
+ <wx w="376"/>
+ <wx w="569"/>
+ <wx w="253"/>
+ <wx w="311"/>
+ <wx w="0"/>
+ <wx w="623"/>
+ <wx w="623"/>
+ <wx w="623"/>
+ <wx w="934"/>
+ <wx w="934"/>
+ <wx w="311"/>
+ <wx w="311"/>
+ <wx w="304"/>
+ <wx w="304"/>
+ <wx w="253"/>
+ <wx w="253"/>
+ <wx w="253"/>
+ <wx w="151"/>
+ <wx w="151"/>
+ <wx w="235"/>
+ <wx w="253"/>
+ <wx w="151"/>
+ <wx w="623"/>
+ <wx w="575"/>
+ <wx w="332"/>
+ <wx w="376"/>
+ <wx w="657"/>
+ <wx w="980"/>
+ <wx w="625"/>
+ <wx w="754"/>
+ <wx w="886"/>
+ <wx w="311"/>
+ <wx w="398"/>
+ <wx w="398"/>
+ <wx w="601"/>
+ <wx w="652"/>
+ <wx w="311"/>
+ <wx w="331"/>
+ <wx w="311"/>
+ <wx w="500"/>
+ <wx w="541"/>
+ <wx w="541"/>
+ <wx w="541"/>
+ <wx w="541"/>
+ <wx w="541"/>
+ <wx w="541"/>
+ <wx w="541"/>
+ <wx w="541"/>
+ <wx w="541"/>
+ <wx w="541"/>
+ <wx w="311"/>
+ <wx w="311"/>
+ <wx w="500"/>
+ <wx w="805"/>
+ <wx w="500"/>
+ <wx w="569"/>
+ <wx w="805"/>
+ <wx w="729"/>
+ <wx w="708"/>
+ <wx w="725"/>
+ <wx w="748"/>
+ <wx w="683"/>
+ <wx w="665"/>
+ <wx w="769"/>
+ <wx w="808"/>
+ <wx w="347"/>
+ <wx w="347"/>
+ <wx w="769"/>
+ <wx w="644"/>
+ <wx w="1109"/>
+ <wx w="767"/>
+ <wx w="821"/>
+ <wx w="689"/>
+ <wx w="821"/>
+ <wx w="708"/>
+ <wx w="689"/>
+ <wx w="685"/>
+ <wx w="767"/>
+ <wx w="689"/>
+ <wx w="1181"/>
+ <wx w="729"/>
+ <wx w="729"/>
+ <wx w="644"/>
+ <wx w="542"/>
+ <wx w="657"/>
+ <wx w="542"/>
+ <wx w="253"/>
+ <wx w="115"/>
+ <wx w="311"/>
+ <wx w="515"/>
+ <wx w="556"/>
+ <wx w="492"/>
+ <wx w="556"/>
+ <wx w="517"/>
+ <wx w="311"/>
+ <wx w="556"/>
+ <wx w="575"/>
+ <wx w="311"/>
+ <wx w="311"/>
+ <wx w="556"/>
+ <wx w="311"/>
+ <wx w="839"/>
+ <wx w="575"/>
+ <wx w="547"/>
+ <wx w="556"/>
+ <wx w="556"/>
+ <wx w="440"/>
+ <wx w="492"/>
+ <wx w="359"/>
+ <wx w="575"/>
+ <wx w="476"/>
+ <wx w="711"/>
+ <wx w="496"/>
+ <wx w="476"/>
+ <wx w="496"/>
+ <wx w="855"/>
+ <wx w="1109"/>
+ <wx w="253"/>
+ <wx w="253"/>
+ <wx w="395"/>
+ <wx w="729"/>
+ <wx w="729"/>
+ <wx w="725"/>
+ <wx w="725"/>
+ <wx w="748"/>
+ <wx w="683"/>
+ <wx w="683"/>
+ <wx w="769"/>
+ <wx w="644"/>
+ <wx w="644"/>
+ <wx w="644"/>
+ <wx w="767"/>
+ <wx w="767"/>
+ <wx w="821"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="347"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="515"/>
+ <wx w="515"/>
+ <wx w="492"/>
+ <wx w="492"/>
+ <wx w="556"/>
+ <wx w="517"/>
+ <wx w="517"/>
+ <wx w="556"/>
+ <wx w="311"/>
+ <wx w="311"/>
+ <wx w="311"/>
+ <wx w="575"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="547"/>
+ <wx w="729"/>
+ <wx w="729"/>
+ <wx w="729"/>
+ <wx w="729"/>
+ <wx w="729"/>
+ <wx w="725"/>
+ <wx w="683"/>
+ <wx w="683"/>
+ <wx w="683"/>
+ <wx w="683"/>
+ <wx w="347"/>
+ <wx w="347"/>
+ <wx w="347"/>
+ <wx w="347"/>
+ <wx w="0"/>
+ <wx w="767"/>
+ <wx w="821"/>
+ <wx w="821"/>
+ <wx w="821"/>
+ <wx w="821"/>
+ <wx w="821"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="767"/>
+ <wx w="767"/>
+ <wx w="767"/>
+ <wx w="767"/>
+ <wx w="729"/>
+ <wx w="0"/>
+ <wx w="515"/>
+ <wx w="515"/>
+ <wx w="515"/>
+ <wx w="515"/>
+ <wx w="515"/>
+ <wx w="515"/>
+ <wx w="492"/>
+ <wx w="517"/>
+ <wx w="517"/>
+ <wx w="517"/>
+ <wx w="517"/>
+ <wx w="311"/>
+ <wx w="311"/>
+ <wx w="311"/>
+ <wx w="311"/>
+ <wx w="0"/>
+ <wx w="575"/>
+ <wx w="547"/>
+ <wx w="547"/>
+ <wx w="547"/>
+ <wx w="547"/>
+ <wx w="547"/>
+ <wx w="0"/>
+ <wx w="0"/>
+ <wx w="575"/>
+ <wx w="575"/>
+ <wx w="575"/>
+ <wx w="575"/>
+ <wx w="476"/>
+ <wx w="0"/>
+ <wx w="476"/>
+ </cid-widths>
+ </multibyte-extras>
+</font-metrics>
diff --git a/test/resources/images/cmyk.jpg b/test/resources/images/cmyk.jpg
new file mode 100644
index 000000000..cfdcca1aa
--- /dev/null
+++ b/test/resources/images/cmyk.jpg
Binary files differ
diff --git a/test/test.xconf b/test/test.xconf
new file mode 100644
index 000000000..c0df5b0dd
--- /dev/null
+++ b/test/test.xconf
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<fop version="1.0">
+ <renderers>
+ <renderer mime="application/pdf">
+ <fonts>
+ <font metrics-url="test/resources/fonts/glb12.ttf.xml" embed-url="test/resources/fonts/glb12.ttf">
+ <font-triplet name="Gladiator" style="normal" weight="normal"/>
+ </font>
+ <font metrics-url="test/resources/fonts/glb12.ttf.ansi.xml" embed-url="test/resources/fonts/glb12.ttf">
+ <font-triplet name="Gladiator-Ansi" style="normal" weight="normal"/>
+ </font>
+ <font metrics-url="test/resources/fonts/glb12.ttf.ansi.xml">
+ <font-triplet name="Gladiator-Non-Embedded" style="normal" weight="normal"/>
+ </font>
+ </fonts>
+ </renderer>
+ </renderers>
+</fop>
diff --git a/test/xml/pdf-a/base14-font.fo b/test/xml/pdf-a/base14-font.fo
new file mode 100644
index 000000000..a33623ec9
--- /dev/null
+++ b/test/xml/pdf-a/base14-font.fo
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-family="Helvetica">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="A4" page-height="29.7cm" page-width="21cm" margin="2cm">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="A4">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>Hello World!</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>
diff --git a/test/xml/pdf-a/minimal-pdf-a.fo b/test/xml/pdf-a/minimal-pdf-a.fo
new file mode 100644
index 000000000..0081be30b
--- /dev/null
+++ b/test/xml/pdf-a/minimal-pdf-a.fo
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-family="Gladiator">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="A4" page-height="29.7cm" page-width="21cm" margin="2cm">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="A4">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>Hello World!</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>
diff --git a/test/xml/pdf-a/with-cmyk-images.fo b/test/xml/pdf-a/with-cmyk-images.fo
new file mode 100644
index 000000000..f3429d304
--- /dev/null
+++ b/test/xml/pdf-a/with-cmyk-images.fo
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-family="Gladiator">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="A4" page-height="29.7cm" page-width="21cm" margin="2cm">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="A4">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>Hello World!</fo:block>
+ <fo:block><fo:external-graphic src="test/resources/images/cmyk.jpg"/></fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>
diff --git a/test/xml/pdf-a/with-eps.fo b/test/xml/pdf-a/with-eps.fo
new file mode 100644
index 000000000..0f8913f01
--- /dev/null
+++ b/test/xml/pdf-a/with-eps.fo
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-family="Gladiator">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="A4" page-height="29.7cm" page-width="21cm" margin="2cm">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="A4">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>Hello World!</fo:block>
+ <fo:block><fo:external-graphic src="test/resources/images/barcode.eps"/></fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>
diff --git a/test/xml/pdf-a/with-rgb-images.fo b/test/xml/pdf-a/with-rgb-images.fo
new file mode 100644
index 000000000..952da63eb
--- /dev/null
+++ b/test/xml/pdf-a/with-rgb-images.fo
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-family="Gladiator">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="A4" page-height="29.7cm" page-width="21cm" margin="2cm">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="A4">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>Hello World!</fo:block>
+ <fo:block><fo:external-graphic src="test/resources/images/bgimg300dpi.jpg"/></fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>