aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/fop/render
diff options
context:
space:
mode:
authorJeremias Maerki <jeremias@apache.org>2008-07-22 15:48:39 +0000
committerJeremias Maerki <jeremias@apache.org>2008-07-22 15:48:39 +0000
commit8091bd11210ac68aa3289532643e42b66545a495 (patch)
tree0c500f554120284fef9debb77c463d738804ff3b /src/java/org/apache/fop/render
parentcd64af07c5661e82a7121a2ac6ad756a3b2e91e9 (diff)
downloadxmlgraphics-fop-8091bd11210ac68aa3289532643e42b66545a495.tar.gz
xmlgraphics-fop-8091bd11210ac68aa3289532643e42b66545a495.zip
Started the IFParser.
Started a PDF painter. Factored out common code to PDFRenderingUtil. Smaller infrastructure changes for the new IF (like MIME type reporting). git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_AreaTreeNewDesign@678780 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/fop/render')
-rw-r--r--src/java/org/apache/fop/render/AbstractRendererConfigurator.java7
-rw-r--r--src/java/org/apache/fop/render/RendererFactory.java13
-rw-r--r--src/java/org/apache/fop/render/intermediate/AbstractBinaryWritingIFPainter.java167
-rw-r--r--src/java/org/apache/fop/render/intermediate/AbstractIFPainterMaker.java11
-rw-r--r--src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFPainter.java5
-rw-r--r--src/java/org/apache/fop/render/intermediate/AffineTransformArrayParser.java146
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFPainter.java8
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFPainterConfigurator.java35
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFParser.java552
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFRenderer.java5
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFSerializer.java5
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java52
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFPainter.java537
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFPainterMaker.java54
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderer.java511
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java166
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java439
17 files changed, 2217 insertions, 496 deletions
diff --git a/src/java/org/apache/fop/render/AbstractRendererConfigurator.java b/src/java/org/apache/fop/render/AbstractRendererConfigurator.java
index 982b23f05..09540dfbb 100644
--- a/src/java/org/apache/fop/render/AbstractRendererConfigurator.java
+++ b/src/java/org/apache/fop/render/AbstractRendererConfigurator.java
@@ -5,9 +5,9 @@
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -23,6 +23,7 @@ import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.apache.fop.apps.FOUserAgent;
/**
@@ -68,7 +69,7 @@ public abstract class AbstractRendererConfigurator {
* @param mimeType the MIME type of the renderer
* @return the requested configuration subtree, null if there's no configuration
*/
- private Configuration getRendererConfig(String mimeType) {
+ protected Configuration getRendererConfig(String mimeType) {
Configuration cfg = userAgent.getFactory().getUserConfig();
if (cfg == null) {
if (log.isDebugEnabled()) {
diff --git a/src/java/org/apache/fop/render/RendererFactory.java b/src/java/org/apache/fop/render/RendererFactory.java
index 2706a723e..3bf4881b9 100644
--- a/src/java/org/apache/fop/render/RendererFactory.java
+++ b/src/java/org/apache/fop/render/RendererFactory.java
@@ -36,6 +36,7 @@ import org.apache.fop.area.AreaTreeHandler;
import org.apache.fop.fo.FOEventHandler;
import org.apache.fop.render.intermediate.AbstractIFPainterMaker;
import org.apache.fop.render.intermediate.IFPainter;
+import org.apache.fop.render.intermediate.IFPainterConfigurator;
import org.apache.fop.render.intermediate.IFRenderer;
/**
@@ -247,7 +248,7 @@ public class RendererFactory {
if (painterMaker != null) {
IFRenderer rend = new IFRenderer();
rend.setUserAgent(userAgent);
- IFPainter painter = painterMaker.makePainter(userAgent);
+ IFPainter painter = createPainter(userAgent, outputFormat);
rend.setPainter(painter);
return rend;
} else {
@@ -281,7 +282,9 @@ public class RendererFactory {
boolean outputStreamMissing = (userAgent.getRendererOverride() == null);
if (rendMaker == null) {
painterMaker = getPainterMaker(outputFormat);
- outputStreamMissing &= (out == null) && (painterMaker.needsOutputStream());
+ if (painterMaker != null) {
+ outputStreamMissing &= (out == null) && (painterMaker.needsOutputStream());
+ }
} else {
outputStreamMissing &= (out == null) && (rendMaker.needsOutputStream());
}
@@ -325,12 +328,10 @@ public class RendererFactory {
}
IFPainter painter = maker.makePainter(userAgent);
painter.setUserAgent(userAgent);
- //TODO Add configuration
- /*
- RendererConfigurator configurator = maker.getConfigurator(userAgent);
+ IFPainterConfigurator configurator = maker.getConfigurator(userAgent);
if (configurator != null) {
configurator.configure(painter);
- }*/
+ }
return painter;
//}
}
diff --git a/src/java/org/apache/fop/render/intermediate/AbstractBinaryWritingIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractBinaryWritingIFPainter.java
new file mode 100644
index 000000000..860de7946
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/AbstractBinaryWritingIFPainter.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.intermediate;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.List;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+
+import org.apache.fop.fonts.CustomFontCollection;
+import org.apache.fop.fonts.FontCollection;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontManager;
+import org.apache.fop.fonts.FontResolver;
+import org.apache.fop.fonts.base14.Base14FontCollection;
+import org.apache.fop.render.DefaultFontResolver;
+
+/**
+ * Abstract base class for binary-writing IFPainter implementations.
+ */
+public abstract class AbstractBinaryWritingIFPainter extends AbstractIFPainter {
+
+ /** The output stream to write the document to */
+ protected OutputStream outputStream;
+
+ private boolean ownOutputStream;
+
+ /** Font configuration */
+ protected FontInfo fontInfo;
+
+ /** Font resolver */
+ protected FontResolver fontResolver = null;
+
+ /** list of fonts */
+ protected List/*<EmbedFontInfo>*/ embedFontInfoList = null;
+
+ /** {@inheritDoc} */
+ public void setResult(Result result) throws IFException {
+ if (result instanceof StreamResult) {
+ StreamResult streamResult = (StreamResult)result;
+ OutputStream out = streamResult.getOutputStream();
+ if (out == null) {
+ if (streamResult.getWriter() != null) {
+ throw new IllegalArgumentException(
+ "FOP cannot use a Writer. Please supply an OutputStream!");
+ }
+ try {
+ URL url = new URL(streamResult.getSystemId());
+ File f = FileUtils.toFile(url);
+ if (f != null) {
+ out = new java.io.FileOutputStream(f);
+ } else {
+ out = url.openConnection().getOutputStream();
+ }
+ } catch (IOException ioe) {
+ throw new IFException("I/O error while opening output stream" , ioe);
+ }
+ out = new java.io.BufferedOutputStream(out);
+ this.ownOutputStream = true;
+ }
+ if (out == null) {
+ throw new IllegalArgumentException("Need a StreamResult with an OutputStream");
+ }
+ this.outputStream = out;
+ } else {
+ throw new UnsupportedOperationException(
+ "Unsupported Result subclass: " + result.getClass().getName());
+ }
+ }
+
+ /**
+ * Adds a font list to current list of fonts
+ * @param fontList a font info list
+ */
+ public void addFontList(List/*<EmbedFontInfo>*/ fontList) {
+ if (embedFontInfoList == null) {
+ setFontList(fontList);
+ } else {
+ embedFontInfoList.addAll(fontList);
+ }
+ }
+
+ /**
+ * @param embedFontInfoList list of available fonts
+ */
+ public void setFontList(List/*<EmbedFontInfo>*/ embedFontInfoList) {
+ this.embedFontInfoList = embedFontInfoList;
+ }
+
+ /**
+ * @return list of available embedded fonts
+ */
+ public List/*<EmbedFontInfo>*/ getFontList() {
+ return this.embedFontInfoList;
+ }
+
+ /**
+ * Returns the {@code FontResolver} used by this painter.
+ * @return the font resolver
+ */
+ public FontResolver getFontResolver() {
+ if (this.fontResolver == null) {
+ this.fontResolver = new DefaultFontResolver(getUserAgent());
+ }
+ return this.fontResolver;
+ }
+
+ /**
+ * Returns the {@code FontInfo} object.
+ * @return the font info
+ */
+ public FontInfo getFontInfo() {
+ return this.fontInfo;
+ }
+
+ public void setFontInfo(FontInfo fontInfo) {
+ this.fontInfo = fontInfo;
+ }
+
+ /**
+ * Set up the font info
+ *
+ * @param inFontInfo font info to set up
+ */
+ public void setupFontInfo(FontInfo inFontInfo) {
+ setFontInfo(inFontInfo);
+ FontManager fontManager = getUserAgent().getFactory().getFontManager();
+ FontCollection[] fontCollections = new FontCollection[] {
+ new Base14FontCollection(fontManager.isBase14KerningEnabled()),
+ new CustomFontCollection(getFontResolver(), getFontList())
+ };
+ fontManager.setup(getFontInfo(), fontCollections);
+ }
+
+ /** {@inheritDoc} */
+ public void endDocument() throws IFException {
+ if (this.ownOutputStream) {
+ IOUtils.closeQuietly(this.outputStream);
+ this.outputStream = null;
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFPainterMaker.java b/src/java/org/apache/fop/render/intermediate/AbstractIFPainterMaker.java
index 273f90170..aa653cd3e 100644
--- a/src/java/org/apache/fop/render/intermediate/AbstractIFPainterMaker.java
+++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainterMaker.java
@@ -45,15 +45,12 @@ public abstract class AbstractIFPainterMaker {
public abstract String[] getSupportedMimeTypes();
/**
- * Returns a renderer config object that can be used to
+ * Returns a configurator object that can be used to
* configure the painter.
- * @param userAgent user agent
- * @return a config object that can be used to configure the painter
+ * @param userAgent the user agent
+ * @return a configurator object that can be used to configure the painter
*/
- /*
- public RendererConfigurator getConfigurator(FOUserAgent userAgent) {
- return null;
- }*/
+ public abstract IFPainterConfigurator getConfigurator(FOUserAgent userAgent);
/**
* Indicates whether a specific MIME type is supported by this painter.
diff --git a/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFPainter.java
index 166b6df20..bb226dd0c 100644
--- a/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFPainter.java
+++ b/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFPainter.java
@@ -57,11 +57,6 @@ public abstract class AbstractXMLWritingIFPainter extends AbstractIFPainter {
protected ContentHandler handler;
/** {@inheritDoc} */
- public ContentHandler getContentHandler() {
- return this.handler;
- }
-
- /** {@inheritDoc} */
public void setResult(Result result) throws IFException {
if (result instanceof SAXResult) {
SAXResult saxResult = (SAXResult)result;
diff --git a/src/java/org/apache/fop/render/intermediate/AffineTransformArrayParser.java b/src/java/org/apache/fop/render/intermediate/AffineTransformArrayParser.java
new file mode 100644
index 000000000..36a783427
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/AffineTransformArrayParser.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.intermediate;
+
+import java.awt.geom.AffineTransform;
+import java.io.Reader;
+import java.util.List;
+
+import org.apache.batik.parser.ParseException;
+import org.apache.batik.parser.TransformListHandler;
+import org.apache.batik.parser.TransformListParser;
+
+/**
+ * This class parses a sequence of transformations into an array of {@code AffineTransform}
+ * instances.
+ */
+public class AffineTransformArrayParser implements TransformListHandler {
+
+ private List transforms;
+
+ /**
+ * Utility method for creating an AffineTransform array.
+ * @param r The reader used to read the transform specification.
+ * @return the AffineTransform array
+ * @throws ParseException if there's a parse error
+ */
+ public static AffineTransform[] createAffineTransform(Reader r)
+ throws ParseException {
+ TransformListParser p = new TransformListParser();
+ AffineTransformArrayParser th = new AffineTransformArrayParser();
+
+ p.setTransformListHandler(th);
+ p.parse(r);
+
+ return th.getAffineTransforms();
+ }
+
+ /**
+ * Utility method for creating an AffineTransform.
+ * @param s The transform specification.
+ * @return the AffineTransform array
+ * @throws ParseException if there's a parse error
+ */
+ public static AffineTransform[] createAffineTransform(String s)
+ throws ParseException {
+ TransformListParser p = new TransformListParser();
+ AffineTransformArrayParser th = new AffineTransformArrayParser();
+
+ p.setTransformListHandler(th);
+ p.parse(s);
+
+ return th.getAffineTransforms();
+ }
+
+ /**
+ * Returns the AffineTransform array initialized during the last parsing.
+ * @return the array or null if this handler has not been used by
+ * a parser.
+ */
+ public AffineTransform[] getAffineTransforms() {
+ if (this.transforms == null) {
+ return null;
+ } else {
+ int count = this.transforms.size();
+ return (AffineTransform[])this.transforms.toArray(new AffineTransform[count]);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startTransformList() throws ParseException {
+ this.transforms = new java.util.ArrayList();
+ }
+
+ /** {@inheritDoc} */
+ public void matrix(float a, float b, float c, float d, float e, float f)
+ throws ParseException {
+ this.transforms.add(new AffineTransform(a, b, c, d, e, f));
+ }
+
+ /** {@inheritDoc} */
+ public void rotate(float theta) throws ParseException {
+ this.transforms.add(AffineTransform.getRotateInstance(Math.toRadians(theta)));
+ }
+
+ /** {@inheritDoc} */
+ public void rotate(float theta, float cx, float cy) throws ParseException {
+ AffineTransform at
+ = AffineTransform.getRotateInstance(Math.toRadians(theta), cx, cy);
+ this.transforms.add(at);
+ }
+
+ /** {@inheritDoc} */
+ public void translate(float tx) throws ParseException {
+ AffineTransform at = AffineTransform.getTranslateInstance(tx, 0);
+ this.transforms.add(at);
+ }
+
+ /** {@inheritDoc} */
+ public void translate(float tx, float ty) throws ParseException {
+ AffineTransform at = AffineTransform.getTranslateInstance(tx, ty);
+ this.transforms.add(at);
+ }
+
+ /** {@inheritDoc} */
+ public void scale(float sx) throws ParseException {
+ this.transforms.add(AffineTransform.getScaleInstance(sx, sx));
+ }
+
+ /** {@inheritDoc} */
+ public void scale(float sx, float sy) throws ParseException {
+ this.transforms.add(AffineTransform.getScaleInstance(sx, sy));
+ }
+
+ /** {@inheritDoc} */
+ public void skewX(float skx) throws ParseException {
+ this.transforms.add
+ (AffineTransform.getShearInstance(Math.tan(Math.toRadians(skx)), 0));
+ }
+
+ /** {@inheritDoc} */
+ public void skewY(float sky) throws ParseException {
+ this.transforms.add
+ (AffineTransform.getShearInstance(0, Math.tan(Math.toRadians(sky))));
+ }
+
+ /** {@inheritDoc} */
+ public void endTransformList() throws ParseException {
+ }
+}
diff --git a/src/java/org/apache/fop/render/intermediate/IFPainter.java b/src/java/org/apache/fop/render/intermediate/IFPainter.java
index 83045b6bc..44e02fe68 100644
--- a/src/java/org/apache/fop/render/intermediate/IFPainter.java
+++ b/src/java/org/apache/fop/render/intermediate/IFPainter.java
@@ -97,6 +97,12 @@ public interface IFPainter {
boolean supportsPagesOutOfOrder();
/**
+ * Returns the MIME type of the output format that is generated by this implementation.
+ * @return the MIME type
+ */
+ String getMimeType();
+
+ /**
* Indicates the start of a document. This method may only be called once before any other
* event method.
* @throws IFException if an error occurs while handling this event
@@ -141,7 +147,7 @@ public interface IFPainter {
/**
* Indicates the start of a new page.
- * @param index the index of the page within the document (0-based)
+ * @param index the index of the page (0-based)
* @param name the page name (usually the formatted page number)
* @param size the size of the page (equivalent to the MediaBox in PDF)
* @throws IFException if an error occurs while handling this event
diff --git a/src/java/org/apache/fop/render/intermediate/IFPainterConfigurator.java b/src/java/org/apache/fop/render/intermediate/IFPainterConfigurator.java
new file mode 100644
index 000000000..e42f52665
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/IFPainterConfigurator.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.intermediate;
+
+import org.apache.fop.apps.FOPException;
+
+/**
+ * This interface is implemented by classes that configure an {@code IFPainter} instance.
+ */
+public interface IFPainterConfigurator {
+
+ /**
+ * Configures a painter.
+ * @param painter the painter instance
+ * @throws FOPException if an error occurs while configuring the object
+ */
+ void configure(IFPainter painter) throws FOPException;
+}
diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java
new file mode 100644
index 000000000..ba0a6c60b
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/IFParser.java
@@ -0,0 +1,552 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.intermediate;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.util.Map;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fo.ElementMappingRegistry;
+import org.apache.fop.fo.expr.PropertyException;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+import org.apache.fop.util.ColorUtil;
+import org.apache.fop.util.ContentHandlerFactory;
+import org.apache.fop.util.ContentHandlerFactoryRegistry;
+import org.apache.fop.util.ConversionUtils;
+import org.apache.fop.util.DefaultErrorListener;
+
+/**
+ * This is a parser for the intermediate format XML which converts the intermediate file into
+ * {@code IFPainter} events.
+ */
+public class IFParser implements IFConstants {
+
+ /** Logger instance */
+ protected static Log log = LogFactory.getLog(IFParser.class);
+
+ private static SAXTransformerFactory tFactory
+ = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+
+ /**
+ * Parses an intermediate file and paints it.
+ * @param src the Source instance pointing to the intermediate file
+ * @param painter the intermediate format painter used to process the IF events
+ * @param userAgent the user agent
+ * @throws TransformerException if an error occurs while parsing the area tree XML
+ */
+ public void parse(Source src, IFPainter painter, FOUserAgent userAgent)
+ throws TransformerException {
+ Transformer transformer = tFactory.newTransformer();
+ transformer.setErrorListener(new DefaultErrorListener(log));
+
+ SAXResult res = new SAXResult(getContentHandler(painter, userAgent));
+
+ transformer.transform(src, res);
+ }
+
+ /**
+ * Creates a new ContentHandler instance that you can send the area tree XML to. The parsed
+ * pages are added to the AreaTreeModel instance you pass in as a parameter.
+ * @param painter the intermediate format painter used to process the IF events
+ * @param userAgent the user agent
+ * @return the ContentHandler instance to receive the SAX stream from the area tree XML
+ */
+ public ContentHandler getContentHandler(IFPainter painter, FOUserAgent userAgent) {
+ ElementMappingRegistry elementMappingRegistry
+ = userAgent.getFactory().getElementMappingRegistry();
+ return new Handler(painter, userAgent, elementMappingRegistry);
+ }
+
+ private static class Handler extends DefaultHandler {
+
+ private Map elementHandlers = new java.util.HashMap();
+
+ private IFPainter painter;
+ private FOUserAgent userAgent;
+ private ElementMappingRegistry elementMappingRegistry;
+
+ private Attributes lastAttributes;
+
+ private StringBuffer content = new StringBuffer();
+ private boolean ignoreCharacters = true;
+
+ //private Stack delegateStack = new Stack();
+ private int delegateDepth;
+ private ContentHandler delegate;
+ private DOMImplementation domImplementation;
+
+
+ public Handler(IFPainter painter, FOUserAgent userAgent,
+ ElementMappingRegistry elementMappingRegistry) {
+ this.painter = painter;
+ this.userAgent = userAgent;
+ this.elementMappingRegistry = elementMappingRegistry;
+ elementHandlers.put("document", new DocumentHandler());
+ elementHandlers.put("header", new DocumentHeaderHandler());
+ elementHandlers.put("page-sequence", new PageSequenceHandler());
+ elementHandlers.put("page", new PageHandler());
+ elementHandlers.put("page-header", new PageHeaderHandler());
+ elementHandlers.put("content", new PageContentHandler());
+ elementHandlers.put("page-trailer", new PageTrailerHandler());
+ //Page content
+ elementHandlers.put("box", new BoxHandler());
+ elementHandlers.put("font", new FontHandler());
+ elementHandlers.put("text", new TextHandler());
+ elementHandlers.put("rect", new RectHandler());
+ }
+
+
+ /** {@inheritDoc} */
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
+ throws SAXException {
+ if (delegate != null) {
+ //delegateStack.push(qName);
+ delegateDepth++;
+ delegate.startElement(uri, localName, qName, attributes);
+ } else if (domImplementation != null) {
+ //domImplementation is set so we need to start a new DOM building sub-process
+ TransformerHandler handler;
+ try {
+ handler = tFactory.newTransformerHandler();
+ } catch (TransformerConfigurationException e) {
+ throw new SAXException("Error creating a new TransformerHandler", e);
+ }
+ Document doc = domImplementation.createDocument(uri, qName, null);
+ //It's easier to work with an empty document, so remove the root element
+ doc.removeChild(doc.getDocumentElement());
+ handler.setResult(new DOMResult(doc));
+ //Area parent = (Area)areaStack.peek();
+ //((ForeignObject)parent).setDocument(doc);
+
+ //activate delegate for nested foreign document
+ domImplementation = null; //Not needed anymore now
+ this.delegate = handler;
+ //delegateStack.push(qName);
+ delegateDepth++;
+ delegate.startDocument();
+ delegate.startElement(uri, localName, qName, attributes);
+ } else {
+ lastAttributes = attributes;
+ boolean handled = true;
+ if (NAMESPACE.equals(uri)) {
+ ElementHandler elementHandler = (ElementHandler)elementHandlers.get(localName);
+ content.setLength(0);
+ ignoreCharacters = true;
+ if (elementHandler != null) {
+ ignoreCharacters = elementHandler.ignoreCharacters();
+ try {
+ elementHandler.startElement(attributes);
+ } catch (IFException ife) {
+ handleIFException(ife);
+ }
+ } else if ("extension-attachments".equals(localName)) {
+ //TODO implement me
+ } else {
+ handled = false;
+ }
+ } else {
+ ContentHandlerFactoryRegistry registry
+ = userAgent.getFactory().getContentHandlerFactoryRegistry();
+ ContentHandlerFactory factory = registry.getFactory(uri);
+ if (factory != null) {
+ delegate = factory.createContentHandler();
+ //delegateStack.push(qName);
+ delegateDepth++;
+ delegate.startDocument();
+ delegate.startElement(uri, localName, qName, attributes);
+ } else {
+ handled = false;
+ }
+ }
+ if (!handled) {
+ if (uri == null || uri.length() == 0) {
+ throw new SAXException("Unhandled element " + localName
+ + " in namespace: " + uri);
+ } else {
+ log.warn("Unhandled element " + localName
+ + " in namespace: " + uri);
+ }
+ }
+ }
+ }
+
+ private void handleIFException(IFException ife) throws SAXException {
+ if (ife.getCause() instanceof SAXException) {
+ //unwrap
+ throw (SAXException)ife.getCause();
+ } else {
+ //wrap
+ throw new SAXException(ife);
+ }
+ }
+
+
+ /** {@inheritDoc} */
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (delegate != null) {
+ delegate.endElement(uri, localName, qName);
+ //delegateStack.pop();
+ delegateDepth--;
+ if (delegateDepth == 0) {
+ delegate.endDocument();
+ if (delegate instanceof ContentHandlerFactory.ObjectSource) {
+ Object obj = ((ContentHandlerFactory.ObjectSource)delegate).getObject();
+ handleExternallyGeneratedObject(obj);
+ }
+ delegate = null; //Sub-document is processed, return to normal processing
+ }
+ } else {
+ if (NAMESPACE.equals(uri)) {
+ ElementHandler elementHandler = (ElementHandler)elementHandlers.get(localName);
+ if (elementHandler != null) {
+ try {
+ elementHandler.endElement();
+ } catch (IFException ife) {
+ handleIFException(ife);
+ }
+ content.setLength(0);
+ }
+ ignoreCharacters = true;
+ } else {
+ //log.debug("Ignoring " + localName + " in namespace: " + uri);
+ }
+ }
+ }
+
+ // ============== Element handlers for the intermediate format =============
+
+ private static interface ElementHandler {
+ void startElement(Attributes attributes) throws IFException, SAXException;
+ void endElement() throws IFException;
+ boolean ignoreCharacters();
+ }
+
+ private abstract class AbstractElementHandler implements ElementHandler {
+
+ public void startElement(Attributes attributes) throws IFException, SAXException {
+ //nop
+ }
+
+ public void endElement() throws IFException {
+ //nop
+ }
+
+ public boolean ignoreCharacters() {
+ return true;
+ }
+ }
+
+ private class DocumentHandler extends AbstractElementHandler {
+
+ public void startElement(Attributes attributes) throws IFException {
+ painter.startDocument();
+ }
+
+ public void endElement() throws IFException {
+ painter.endDocument();
+ }
+
+ }
+
+ private class DocumentHeaderHandler extends AbstractElementHandler {
+
+ public void startElement(Attributes attributes) throws IFException {
+ painter.startDocumentHeader();
+ }
+
+ public void endElement() throws IFException {
+ painter.endDocumentHeader();
+ }
+
+ }
+
+ private class PageSequenceHandler extends AbstractElementHandler {
+
+ public void startElement(Attributes attributes) throws IFException {
+ String id = attributes.getValue("id");
+ painter.startPageSequence(id);
+ }
+
+ public void endElement() throws IFException {
+ painter.endPageSequence();
+ }
+
+ }
+
+ private class PageHandler extends AbstractElementHandler {
+
+ public void startElement(Attributes attributes) throws IFException {
+ int index = Integer.parseInt(attributes.getValue("index"));
+ String name = attributes.getValue("name");
+ int width = Integer.parseInt(attributes.getValue("width"));
+ int height = Integer.parseInt(attributes.getValue("height"));
+ painter.startPage(index, name, new Dimension(width, height));
+ }
+
+ public void endElement() throws IFException {
+ painter.endPage();
+ }
+
+ }
+
+ private class PageHeaderHandler extends AbstractElementHandler {
+
+ public void startElement(Attributes attributes) throws IFException {
+ painter.startPageHeader();
+ }
+
+ public void endElement() throws IFException {
+ painter.endPageHeader();
+ }
+
+ }
+
+ private class PageContentHandler extends AbstractElementHandler {
+
+ public void startElement(Attributes attributes) throws IFException {
+ painter.startPageContent();
+ }
+
+ public void endElement() throws IFException {
+ painter.endPageContent();
+ }
+
+ }
+
+ private class PageTrailerHandler extends AbstractElementHandler {
+
+ public void startElement(Attributes attributes) throws IFException {
+ painter.startPageTrailer();
+ }
+
+ public void endElement() throws IFException {
+ painter.endPageTrailer();
+ }
+
+ }
+
+ private class BoxHandler extends AbstractElementHandler {
+
+ public void startElement(Attributes attributes) throws IFException {
+ String transform = attributes.getValue("transform");
+ AffineTransform[] transforms
+ = AffineTransformArrayParser.createAffineTransform(transform);
+ //TODO Incomplete implementation
+ painter.startBox(transforms, null, false);
+ }
+
+ public void endElement() throws IFException {
+ painter.endBox();
+ }
+
+ }
+
+ private class FontHandler extends AbstractElementHandler {
+
+ public void startElement(Attributes attributes) throws IFException {
+ String family = attributes.getValue("family");
+ String style = attributes.getValue("style");
+ Integer weight = getAttributeAsInteger(attributes, "weight");
+ String variant = attributes.getValue("variant");
+ Integer size = getAttributeAsInteger(attributes, "size");
+ Color color;
+ try {
+ color = getAttributeAsColor(attributes, "color");
+ } catch (PropertyException pe) {
+ throw new IFException("Error parsing the color attribute", pe);
+ }
+ painter.setFont(family, style, weight, variant, size, color);
+ }
+
+ }
+
+ private class TextHandler extends AbstractElementHandler {
+
+ public void endElement() throws IFException {
+ int x = Integer.parseInt(lastAttributes.getValue("x"));
+ int y = Integer.parseInt(lastAttributes.getValue("y"));
+ int[] dx = getAttributeAsIntArray(lastAttributes, "dx");
+ int[] dy = getAttributeAsIntArray(lastAttributes, "dy");
+ painter.drawText(x, y, dx, dy, content.toString());
+ }
+
+ public boolean ignoreCharacters() {
+ return false;
+ }
+
+ }
+
+ private class RectHandler extends AbstractElementHandler {
+
+ public void startElement(Attributes attributes) throws IFException {
+ int x = Integer.parseInt(lastAttributes.getValue("x"));
+ int y = Integer.parseInt(lastAttributes.getValue("y"));
+ int width = Integer.parseInt(lastAttributes.getValue("width"));
+ int height = Integer.parseInt(lastAttributes.getValue("height"));
+ Color fillColor;
+ try {
+ fillColor = getAttributeAsColor(attributes, "fill");
+ } catch (PropertyException pe) {
+ throw new IFException("Error parsing the fill attribute", pe);
+ }
+ Color strokeColor;
+ try {
+ strokeColor = getAttributeAsColor(attributes, "stroke");
+ } catch (PropertyException pe) {
+ throw new IFException("Error parsing the stroke attribute", pe);
+ }
+ painter.drawRect(new Rectangle(x, y, width, height), fillColor, strokeColor);
+ }
+
+ }
+
+
+ // ====================================================================
+
+
+ private void assertObjectOfClass(Object obj, Class clazz) {
+ if (!clazz.isInstance(obj)) {
+ throw new IllegalStateException("Object is not an instance of "
+ + clazz.getName() + " but of " + obj.getClass().getName());
+ }
+ }
+
+ /**
+ * Handles objects created by "sub-parsers" that implement the ObjectSource interface.
+ * An example of object handled here are ExtensionAttachments.
+ * @param obj the Object to be handled.
+ */
+ protected void handleExternallyGeneratedObject(Object obj) {
+ if (obj instanceof ExtensionAttachment) {
+ ExtensionAttachment attachment = (ExtensionAttachment)obj;
+ //TODO Implement me
+ /*
+ if (this.currentPageViewport == null) {
+ this.treeModel.handleOffDocumentItem(
+ new OffDocumentExtensionAttachment(attachment));
+ } else {
+ this.currentPageViewport.addExtensionAttachment(attachment);
+ }
+ */
+ } else {
+ log.warn("Don't know how to handle externally generated object: " + obj);
+ }
+ }
+
+ private static boolean getAttributeAsBoolean(Attributes attributes, String name,
+ boolean defaultValue) {
+ String s = attributes.getValue(name);
+ if (s == null) {
+ return defaultValue;
+ } else {
+ return Boolean.valueOf(s).booleanValue();
+ }
+ }
+
+ private static int getAttributeAsInteger(Attributes attributes, String name,
+ int defaultValue) {
+ String s = attributes.getValue(name);
+ if (s == null) {
+ return defaultValue;
+ } else {
+ return Integer.parseInt(s);
+ }
+ }
+
+ private static Integer getAttributeAsInteger(Attributes attributes, String name) {
+ String s = attributes.getValue(name);
+ if (s == null) {
+ return null;
+ } else {
+ return new Integer(s);
+ }
+ }
+
+ private Color getAttributeAsColor(Attributes attributes, String name)
+ throws PropertyException {
+ String s = attributes.getValue(name);
+ if (s == null) {
+ return null;
+ } else {
+ return ColorUtil.parseColorString(userAgent, s);
+ }
+ }
+
+ private static Rectangle2D getAttributeAsRectangle2D(Attributes attributes, String name) {
+ String s = attributes.getValue(name).trim();
+ double[] values = ConversionUtils.toDoubleArray(s, "\\s");
+ if (values.length != 4) {
+ throw new IllegalArgumentException("Rectangle must consist of 4 double values!");
+ }
+ return new Rectangle2D.Double(values[0], values[1], values[2], values[3]);
+ }
+
+ private static Rectangle getAttributeAsRectangle(Attributes attributes, String name) {
+ String s = attributes.getValue(name).trim();
+ int[] values = ConversionUtils.toIntArray(s, "\\s");
+ if (values.length != 4) {
+ throw new IllegalArgumentException("Rectangle must consist of 4 int values!");
+ }
+ return new Rectangle(values[0], values[1], values[2], values[3]);
+ }
+
+ private static int[] getAttributeAsIntArray(Attributes attributes, String name) {
+ String s = attributes.getValue(name);
+ if (s == null) {
+ return null;
+ } else {
+ return ConversionUtils.toIntArray(s.trim(), "\\s");
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ if (delegate != null) {
+ delegate.characters(ch, start, length);
+ } else if (!ignoreCharacters) {
+ this.content.append(ch, start, length);
+ }
+ }
+ }
+}
diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
index 83e7b5397..c4243a86a 100644
--- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
@@ -121,6 +121,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
public void setupFontInfo(FontInfo inFontInfo) {
if (mimic != null) {
mimic.setupFontInfo(inFontInfo);
+ this.fontInfo = inFontInfo;
} else {
super.setupFontInfo(inFontInfo);
}
@@ -164,6 +165,10 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
this.painter = new IFSerializer();
}
this.painter.setUserAgent(getUserAgent());
+ if (this.painter instanceof AbstractBinaryWritingIFPainter) {
+ //TODO THIS IS UGLY. FIX ME!!!
+ ((AbstractBinaryWritingIFPainter)this.painter).setFontInfo(fontInfo);
+ }
this.painter.setResult(result);
}
super.startRenderer(null);
diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
index 41cecd1e7..8cfbcad11 100644
--- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
@@ -56,6 +56,11 @@ public class IFSerializer extends AbstractXMLWritingIFPainter implements IFConst
}
/** {@inheritDoc} */
+ public String getMimeType() {
+ return MIME_TYPE;
+ }
+
+ /** {@inheritDoc} */
public void startDocument() throws IFException {
try {
handler.startDocument();
diff --git a/src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java b/src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java
new file mode 100644
index 000000000..841dd7e01
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+
+/**
+ * Constants used for configuring PDF output.
+ */
+public interface PDFConfigurationConstants {
+
+ /** PDF encryption parameter: all parameters as object, datatype: PDFEncryptionParams */
+ String ENCRYPTION_PARAMS = "encryption-params";
+ /** PDF encryption parameter: user password, datatype: String */
+ String USER_PASSWORD = "user-password";
+ /** PDF encryption parameter: owner password, datatype: String */
+ String OWNER_PASSWORD = "owner-password";
+ /** PDF encryption parameter: Forbids printing, datatype: Boolean or "true"/"false" */
+ String NO_PRINT = "noprint";
+ /** PDF encryption parameter: Forbids copying content, datatype: Boolean or "true"/"false" */
+ String NO_COPY_CONTENT = "nocopy";
+ /** PDF encryption parameter: Forbids editing content, datatype: Boolean or "true"/"false" */
+ String NO_EDIT_CONTENT = "noedit";
+ /** PDF encryption parameter: Forbids annotations, datatype: Boolean or "true"/"false" */
+ String NO_ANNOTATIONS = "noannotations";
+ /** Rendering Options key for the PDF/A mode. */
+ String PDF_A_MODE = "pdf-a-mode";
+ /** Rendering Options key for the PDF/X mode. */
+ String PDF_X_MODE = "pdf-x-mode";
+ /** Rendering Options key for the ICC profile for the output intent. */
+ String KEY_OUTPUT_PROFILE = "output-profile";
+ /**
+ * Rendering Options key for disabling the sRGB color space (only possible if no PDF/A or
+ * PDF/X profile is active).
+ */
+ String KEY_DISABLE_SRGB_COLORSPACE = "disable-srgb-colorspace";
+}
diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java
new file mode 100644
index 000000000..8d1b80c28
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java
@@ -0,0 +1,537 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Paint;
+import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.fo.extensions.xmp.XMPMetadata;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.fonts.FontTriplet;
+import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.fonts.SingleByteFont;
+import org.apache.fop.fonts.Typeface;
+import org.apache.fop.pdf.PDFAnnotList;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFFilterList;
+import org.apache.fop.pdf.PDFNumber;
+import org.apache.fop.pdf.PDFPage;
+import org.apache.fop.pdf.PDFResourceContext;
+import org.apache.fop.pdf.PDFResources;
+import org.apache.fop.pdf.PDFState;
+import org.apache.fop.pdf.PDFStream;
+import org.apache.fop.pdf.PDFTextUtil;
+import org.apache.fop.render.intermediate.AbstractBinaryWritingIFPainter;
+import org.apache.fop.render.intermediate.IFException;
+import org.apache.fop.render.intermediate.IFState;
+import org.apache.fop.util.CharUtilities;
+import org.apache.fop.util.ColorUtil;
+
+/**
+ * IFPainter implementation that produces PDF.
+ */
+public class PDFPainter extends AbstractBinaryWritingIFPainter {
+
+ /** logging instance */
+ private static Log log = LogFactory.getLog(PDFPainter.class);
+
+ /** Holds the intermediate format state */
+ protected IFState state;
+
+ /** the PDF Document being created */
+ protected PDFDocument pdfDoc;
+
+ /**
+ * Utility class which enables all sorts of features that are not directly connected to the
+ * normal rendering process.
+ */
+ protected PDFRenderingUtil pdfUtil;
+
+ /** the /Resources object of the PDF document being created */
+ protected PDFResources pdfResources;
+
+ /** the current stream to add PDF commands to */
+ protected PDFStream currentStream;
+
+ /** the current annotation list to add annotations to */
+ protected PDFResourceContext currentContext;
+
+ /**
+ * Map of pages using the PageViewport as the key
+ * this is used for prepared pages that cannot be immediately
+ * rendered
+ */
+ protected Map pages;
+
+ /** the current page to add annotations to */
+ protected PDFPage currentPage;
+
+ /** the current page's PDF reference string (to avoid numerous function calls) */
+ protected String currentPageRef;
+
+ /** drawing state */
+ protected PDFState currentState;
+
+ /** Text generation utility holding the current font status */
+ protected PDFTextUtil textutil;
+
+
+ /** Image handler registry */
+ private PDFImageHandlerRegistry imageHandlerRegistry = new PDFImageHandlerRegistry();
+
+ /**
+ * Default constructor.
+ */
+ public PDFPainter() {
+ }
+
+ /** {@inheritDoc} */
+ public boolean supportsPagesOutOfOrder() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public String getMimeType() {
+ return MimeConstants.MIME_PDF;
+ }
+
+ /** {@inheritDoc} */
+ public void setUserAgent(FOUserAgent ua) {
+ super.setUserAgent(ua);
+ this.pdfUtil = new PDFRenderingUtil(ua);
+ }
+
+ PDFRenderingUtil getPDFUtil() {
+ return this.pdfUtil;
+ }
+
+ /** {@inheritDoc} */
+ public void startDocument() throws IFException {
+ try {
+ if (getUserAgent() == null) {
+ throw new IllegalStateException(
+ "User agent must be set before starting PDF generation");
+ }
+ if (this.outputStream == null) {
+ throw new IllegalStateException("OutputStream hasn't been set through setResult()");
+ }
+ this.pdfDoc = pdfUtil.setupPDFDocument(this.outputStream);
+ } catch (IOException e) {
+ throw new IFException("I/O error in startDocument()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startDocumentHeader() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void endDocumentHeader() throws IFException {
+ pdfUtil.generateDefaultXMPMetadata();
+ }
+
+ /** {@inheritDoc} */
+ public void endDocument() throws IFException {
+ try {
+ //finishOpenGoTos();
+
+ pdfDoc.getResources().addFonts(pdfDoc, fontInfo);
+ pdfDoc.outputTrailer(this.outputStream);
+
+ this.pdfDoc = null;
+
+ this.pages = null;
+
+ //pageReferences.clear();
+ pdfResources = null;
+ currentStream = null;
+ currentContext = null;
+ currentPage = null;
+ currentState = null;
+ this.textutil = null;
+
+ //idPositions.clear();
+ //idGoTos.clear();
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in endDocument()", ioe);
+ }
+ super.endDocument();
+ }
+
+ /** {@inheritDoc} */
+ public void startPageSequence(String id) throws IFException {
+ //TODO page sequence title, country and language
+ }
+
+ /** {@inheritDoc} */
+ public void endPageSequence() throws IFException {
+ //nop
+ }
+
+ /** {@inheritDoc} */
+ public void startPage(int index, String name, Dimension size) throws IFException {
+ this.pdfResources = this.pdfDoc.getResources();
+
+ this.currentPage = this.pdfDoc.getFactory().makePage(
+ this.pdfResources,
+ (int)Math.round(size.getWidth() / 1000),
+ (int)Math.round(size.getHeight() / 1000),
+ index);
+ //pageReferences.put(new Integer(index)/*page.getKey()*/, currentPage.referencePDF());
+ //pvReferences.put(page.getKey(), page);
+
+ pdfUtil.generatePageLabel(index, name);
+
+ currentPageRef = currentPage.referencePDF();
+
+ currentStream = this.pdfDoc.getFactory()
+ .makeStream(PDFFilterList.CONTENT_FILTER, false);
+ this.textutil = new PDFTextUtil() {
+ protected void write(String code) {
+ currentStream.add(code);
+ }
+ };
+
+ currentState = new PDFState();
+ // Transform the PDF's default coordinate system (0,0 at lower left) to the PDFPainter's
+ AffineTransform basicPageTransform = new AffineTransform(1, 0, 0, -1, 0,
+ size.height);
+ currentState.concatenate(basicPageTransform);
+ currentStream.add(CTMHelper.toPDFString(basicPageTransform, true) + " cm\n");
+ }
+
+ /** {@inheritDoc} */
+ public void startPageHeader() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void endPageHeader() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void startPageContent() throws IFException {
+ this.state = IFState.create();
+ }
+
+ /** {@inheritDoc} */
+ public void endPageContent() throws IFException {
+ assert this.state.pop() == null;
+ }
+
+ /** {@inheritDoc} */
+ public void startPageTrailer() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void endPageTrailer() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void endPage() throws IFException {
+ try {
+ this.pdfDoc.registerObject(currentStream);
+ currentPage.setContents(currentStream);
+ PDFAnnotList annots = currentPage.getAnnotations();
+ if (annots != null) {
+ this.pdfDoc.addObject(annots);
+ }
+ this.pdfDoc.addObject(currentPage);
+ this.pdfDoc.output(this.outputStream);
+ this.textutil = null;
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in endPage()", ioe);
+ }
+ }
+
+ /** {@inheritDoc} */
+ private void saveGraphicsState() {
+ //endTextObject();
+ currentState.push();
+ this.state = this.state.push();
+ currentStream.add("q\n");
+ }
+
+ private void restoreGraphicsState(boolean popState) {
+ endTextObject();
+ currentStream.add("Q\n");
+ if (popState) {
+ currentState.pop();
+ this.state = this.state.pop();
+ }
+ }
+
+ private void restoreGraphicsState() {
+ restoreGraphicsState(true);
+ }
+
+ /** {@inheritDoc} */
+ public void startBox(AffineTransform transform, Dimension size, boolean clip)
+ throws IFException {
+ saveGraphicsState();
+ currentStream.add(CTMHelper.toPDFString(transform, true) + " cm\n");
+ }
+
+ /** {@inheritDoc} */
+ public void startBox(AffineTransform[] transforms, Dimension size, boolean clip)
+ throws IFException {
+ AffineTransform at = new AffineTransform();
+ for (int i = 0, c = transforms.length; i < c; i++) {
+ at.concatenate(transforms[i]);
+ }
+ startBox(at, size, clip);
+ }
+
+ /** {@inheritDoc} */
+ public void endBox() throws IFException {
+ restoreGraphicsState();
+ }
+
+ /** {@inheritDoc} */
+ public void startImage(Rectangle rect) throws IFException {
+ // TODO Auto-generated method stub
+
+ }
+
+ /** {@inheritDoc} */
+ public void drawImage(String uri, Rectangle rect) throws IFException {
+ // TODO Auto-generated method stub
+
+ }
+
+ /** {@inheritDoc} */
+ public void endImage() throws IFException {
+ // TODO Auto-generated method stub
+
+ }
+
+ /** {@inheritDoc} */
+ public void addTarget(String name, int x, int y) throws IFException {
+ // TODO Auto-generated method stub
+
+ }
+
+ private static String toString(Paint paint) {
+ if (paint instanceof Color) {
+ return ColorUtil.colorToString((Color)paint);
+ } else {
+ throw new UnsupportedOperationException("Paint not supported: " + paint);
+ }
+ }
+
+ /**
+ * Formats a int value (normally coordinates in millipoints) as Strings.
+ * @param value the value (in millipoints)
+ * @return the formatted value
+ */
+ protected static String format(int value) {
+ return PDFNumber.doubleOut(value / 1000f);
+ }
+
+ /**
+ * Establishes a new foreground or fill color.
+ * @param col the color to apply (null skips this operation)
+ * @param fill true to set the fill color, false for the foreground color
+ */
+ private void updateColor(Color col, boolean fill) {
+ if (col == null) {
+ return;
+ }
+ boolean update = false;
+ if (fill) {
+ update = currentState.setBackColor(col);
+ } else {
+ update = currentState.setColor(col);
+ }
+
+ if (update) {
+ pdfUtil.setColor(col, fill, this.currentStream);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void drawRect(Rectangle rect, Paint fill, Color stroke) throws IFException {
+ if (fill == null && stroke == null) {
+ return;
+ }
+ endTextObject();
+ if (rect.width != 0 && rect.height != 0) {
+ if (fill != null) {
+ if (fill instanceof Color) {
+ updateColor((Color)fill, true);
+ } else {
+ throw new UnsupportedOperationException("Non-Color paints NYI");
+ }
+ }
+ if (stroke != null) {
+ throw new UnsupportedOperationException("stroke NYI");
+ }
+ StringBuffer sb = new StringBuffer();
+ sb.append(format(rect.x)).append(' ');
+ sb.append(format(rect.y)).append(' ');
+ sb.append(format(rect.width)).append(' ');
+ sb.append(format(rect.height)).append(" re");
+ if (fill != null) {
+ sb.append(" f");
+ }
+ if (stroke != null) {
+ sb.append(" S");
+ }
+ sb.append('\n');
+ currentStream.add(sb.toString());
+ }
+ }
+
+ /** Indicates the beginning of a text object. */
+ private void beginTextObject() {
+ if (!textutil.isInTextObject()) {
+ textutil.beginTextObject();
+ }
+ }
+
+ /** Indicates the end of a text object. */
+ private void endTextObject() {
+ if (textutil.isInTextObject()) {
+ textutil.endTextObject();
+ }
+ }
+
+ private Typeface getTypeface(String fontName) {
+ Typeface tf = (Typeface) fontInfo.getFonts().get(fontName);
+ if (tf instanceof LazyFont) {
+ tf = ((LazyFont)tf).getRealFont();
+ }
+ return tf;
+ }
+
+ /** {@inheritDoc} */
+ public void drawText(int x, int y, int[] dx, int[] dy, String text) throws IFException {
+ //Note: dy is currently ignored
+ beginTextObject();
+ FontTriplet triplet = new FontTriplet(
+ state.getFontFamily(), state.getFontStyle(), state.getFontWeight());
+ //TODO Ignored: state.getFontVariant()
+ String fontKey = fontInfo.getInternalFontKey(triplet);
+ int sizeMillipoints = state.getFontSize();
+ float fontSize = sizeMillipoints / 1000f;
+ updateColor(state.getTextColor(), true);
+
+ // This assumes that *all* CIDFonts use a /ToUnicode mapping
+ Typeface tf = getTypeface(fontKey);
+ SingleByteFont singleByteFont = null;
+ if (tf instanceof SingleByteFont) {
+ singleByteFont = (SingleByteFont)tf;
+ }
+ Font font = fontInfo.getFontInstance(triplet, sizeMillipoints);
+ String fontName = font.getFontName();
+
+ textutil.updateTf(fontKey, fontSize, tf.isMultiByte());
+
+ textutil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, x / 1000f, y / 1000f));
+ int l = text.length();
+ int dxl = (dx != null ? dx.length : 0);
+
+ if (dx != null && dxl > 0 && dx[0] != 0) {
+ textutil.adjustGlyphTJ(dx[0] / fontSize);
+ }
+ for (int i = 0; i < l; i++) {
+ char orgChar = text.charAt(i);
+ char ch;
+ float glyphAdjust = 0;
+ if (font.hasChar(orgChar)) {
+ ch = font.mapChar(orgChar);
+ if (singleByteFont != null && singleByteFont.hasAdditionalEncodings()) {
+ int encoding = ch / 256;
+ if (encoding == 0) {
+ textutil.updateTf(fontName, fontSize, tf.isMultiByte());
+ } else {
+ textutil.updateTf(fontName + "_" + Integer.toString(encoding),
+ fontSize, tf.isMultiByte());
+ ch = (char)(ch % 256);
+ }
+ }
+ //int tls = (i < l - 1 ? parentArea.getTextLetterSpaceAdjust() : 0);
+ //glyphAdjust -= tls;
+ } else {
+ if (CharUtilities.isFixedWidthSpace(orgChar)) {
+ //Fixed width space are rendered as spaces so copy/paste works in a reader
+ ch = font.mapChar(CharUtilities.SPACE);
+ glyphAdjust = font.getCharWidth(ch) - font.getCharWidth(orgChar);
+ } else {
+ ch = font.mapChar(orgChar);
+ }
+ }
+ textutil.writeTJMappedChar(ch);
+
+ if (dx != null && i < dxl) {
+ glyphAdjust += dx[i + 1];
+ }
+
+ if (glyphAdjust != 0) {
+ textutil.adjustGlyphTJ(glyphAdjust / fontSize);
+ }
+
+ }
+ textutil.writeTJ();
+ }
+
+ /** {@inheritDoc} */
+ public void setFont(String family, String style, Integer weight, String variant, Integer size,
+ Color color) throws IFException {
+ if (family != null) {
+ state.setFontFamily(family);
+ }
+ if (style != null) {
+ state.setFontStyle(style);
+ }
+ if (weight != null) {
+ state.setFontWeight(weight.intValue());
+ }
+ if (variant != null) {
+ state.setFontVariant(variant);
+ }
+ if (size != null) {
+ state.setFontSize(size.intValue());
+ }
+ if (color != null) {
+ state.setTextColor(color);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void handleExtensionObject(Object extension) throws IFException {
+ if (extension instanceof XMPMetadata) {
+ pdfUtil.renderXMPMetadata((XMPMetadata)extension);
+ } else {
+ throw new UnsupportedOperationException(
+ "Don't know how to handle extension object: " + extension);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/PDFPainterMaker.java b/src/java/org/apache/fop/render/pdf/PDFPainterMaker.java
new file mode 100644
index 000000000..b3fe42824
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFPainterMaker.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.render.intermediate.AbstractIFPainterMaker;
+import org.apache.fop.render.intermediate.IFPainter;
+import org.apache.fop.render.intermediate.IFPainterConfigurator;
+
+/**
+ * Painter factory for PDF output.
+ */
+public class PDFPainterMaker extends AbstractIFPainterMaker {
+
+ private static final String[] MIMES = new String[] {MimeConstants.MIME_PDF};
+
+ /** {@inheritDoc} */
+ public IFPainter makePainter(FOUserAgent ua) {
+ return new PDFPainter();
+ }
+
+ /** {@inheritDoc} */
+ public boolean needsOutputStream() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public String[] getSupportedMimeTypes() {
+ return MIMES;
+ }
+
+ public IFPainterConfigurator getConfigurator(FOUserAgent userAgent) {
+ return new PDFRendererConfigurator(userAgent);
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
index 154365209..cfe8b9902 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
@@ -23,32 +23,21 @@ package org.apache.fop.render.pdf;
import java.awt.Color;
import java.awt.Point;
import java.awt.Rectangle;
-import java.awt.color.ICC_Profile;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
-import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import javax.xml.transform.Source;
-import javax.xml.transform.stream.StreamSource;
-
-import org.apache.commons.io.IOUtils;
-
import org.apache.xmlgraphics.image.loader.ImageException;
import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.xmlgraphics.image.loader.ImageManager;
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
import org.apache.xmlgraphics.image.loader.util.ImageUtil;
-import org.apache.xmlgraphics.xmp.Metadata;
-import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter;
-import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
@@ -84,26 +73,16 @@ import org.apache.fop.fonts.Typeface;
import org.apache.fop.pdf.PDFAMode;
import org.apache.fop.pdf.PDFAction;
import org.apache.fop.pdf.PDFAnnotList;
-import org.apache.fop.pdf.PDFColor;
-import org.apache.fop.pdf.PDFConformanceException;
-import org.apache.fop.pdf.PDFDictionary;
import org.apache.fop.pdf.PDFDocument;
-import org.apache.fop.pdf.PDFEncryptionManager;
import org.apache.fop.pdf.PDFEncryptionParams;
import org.apache.fop.pdf.PDFFactory;
import org.apache.fop.pdf.PDFFilterList;
import org.apache.fop.pdf.PDFGoTo;
-import org.apache.fop.pdf.PDFICCBasedColorSpace;
-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.PDFNumsArray;
import org.apache.fop.pdf.PDFOutline;
-import org.apache.fop.pdf.PDFOutputIntent;
import org.apache.fop.pdf.PDFPage;
-import org.apache.fop.pdf.PDFPageLabels;
import org.apache.fop.pdf.PDFResourceContext;
import org.apache.fop.pdf.PDFResources;
import org.apache.fop.pdf.PDFState;
@@ -115,61 +94,33 @@ import org.apache.fop.render.AbstractPathOrientedRenderer;
import org.apache.fop.render.Graphics2DAdapter;
import org.apache.fop.render.RendererContext;
import org.apache.fop.util.CharUtilities;
-import org.apache.fop.util.ColorProfileUtil;
/**
* Renderer that renders areas to PDF.
*/
-public class PDFRenderer extends AbstractPathOrientedRenderer {
-
- /**
- * The mime type for pdf
- */
+public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConfigurationConstants {
+
+ /** The MIME type for PDF */
public static final String MIME_TYPE = MimeConstants.MIME_PDF;
/** Normal PDF resolution (72dpi) */
public static final int NORMAL_PDF_RESOLUTION = 72;
-
- /** PDF encryption parameter: all parameters as object, datatype: PDFEncryptionParams */
- public static final String ENCRYPTION_PARAMS = "encryption-params";
- /** PDF encryption parameter: user password, datatype: String */
- public static final String USER_PASSWORD = "user-password";
- /** PDF encryption parameter: owner password, datatype: String */
- public static final String OWNER_PASSWORD = "owner-password";
- /** PDF encryption parameter: Forbids printing, datatype: Boolean or "true"/"false" */
- public static final String NO_PRINT = "noprint";
- /** PDF encryption parameter: Forbids copying content, datatype: Boolean or "true"/"false" */
- public static final String NO_COPY_CONTENT = "nocopy";
- /** PDF encryption parameter: Forbids editing content, datatype: Boolean or "true"/"false" */
- public static final String NO_EDIT_CONTENT = "noedit";
- /** PDF encryption parameter: Forbids annotations, datatype: Boolean or "true"/"false" */
- public static final String NO_ANNOTATIONS = "noannotations";
- /** Rendering Options key for the PDF/A mode. */
- public static final String PDF_A_MODE = "pdf-a-mode";
- /** Rendering Options key for the PDF/X mode. */
- public static final String PDF_X_MODE = "pdf-x-mode";
- /** Rendering Options key for the ICC profile for the output intent. */
- public static final String KEY_OUTPUT_PROFILE = "output-profile";
- /**
- * Rendering Options key for disabling the sRGB color space (only possible if no PDF/A or
- * PDF/X profile is active).
- */
- public static final String KEY_DISABLE_SRGB_COLORSPACE = "disable-srgb-colorspace";
+
/** Controls whether comments are written to the PDF stream. */
protected static final boolean WRITE_COMMENTS = true;
-
+
/**
* the PDF Document being created
*/
protected PDFDocument pdfDoc;
- /** the PDF/A mode (Default: disabled) */
- protected PDFAMode pdfAMode = PDFAMode.DISABLED;
-
- /** the PDF/X mode (Default: disabled) */
- protected PDFXMode pdfXMode = PDFXMode.DISABLED;
-
+ /**
+ * Utility class which enables all sorts of features that are not directly connected to the
+ * normal rendering process.
+ */
+ protected PDFRenderingUtil pdfUtil;
+
/**
* Map of pages using the PageViewport as the key
* this is used for prepared pages that cannot be immediately
@@ -185,7 +136,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
/**
* Maps unique PageViewport key back to PageViewport itself
*/
- protected Map pvReferences = new java.util.HashMap();
+ //protected Map pvReferences = new java.util.HashMap();
/**
* Maps XSL-FO element IDs to their on-page XY-positions
@@ -236,19 +187,6 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
*/
protected String currentPageRef;
- /** the (optional) encryption parameters */
- protected PDFEncryptionParams encryptionParams;
-
- /** the ICC stream used as output profile by this document for PDF/A and PDF/X functionality. */
- protected PDFICCStream outputProfile;
- /** the default sRGB color space. */
- protected PDFICCBasedColorSpace sRGBColorSpace;
- /** controls whether the sRGB color space should be installed */
- protected boolean disableSRGBColorSpace = false;
-
- /** Optional URI to an output profile to be used. */
- protected String outputProfileURI;
-
/** drawing state */
protected PDFState currentState = null;
@@ -257,225 +195,32 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
/** page height */
protected int pageHeight;
- /** Registry of PDF filters */
- protected Map filterMap;
-
/** Image handler registry */
private PDFImageHandlerRegistry imageHandlerRegistry = new PDFImageHandlerRegistry();
-
+
/**
* create the PDF renderer
*/
public PDFRenderer() {
}
- private boolean booleanValueOf(Object obj) {
- if (obj instanceof Boolean) {
- return ((Boolean)obj).booleanValue();
- } else if (obj instanceof String) {
- return Boolean.valueOf((String)obj).booleanValue();
- } else {
- throw new IllegalArgumentException("Boolean or \"true\" or \"false\" expected.");
- }
- }
-
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void setUserAgent(FOUserAgent agent) {
super.setUserAgent(agent);
- PDFEncryptionParams params
- = (PDFEncryptionParams)agent.getRendererOptions().get(ENCRYPTION_PARAMS);
- if (params != null) {
- this.encryptionParams = params; //overwrite if available
- }
- String pwd;
- pwd = (String)agent.getRendererOptions().get(USER_PASSWORD);
- if (pwd != null) {
- if (encryptionParams == null) {
- this.encryptionParams = new PDFEncryptionParams();
- }
- this.encryptionParams.setUserPassword(pwd);
- }
- pwd = (String)agent.getRendererOptions().get(OWNER_PASSWORD);
- if (pwd != null) {
- if (encryptionParams == null) {
- this.encryptionParams = new PDFEncryptionParams();
- }
- this.encryptionParams.setOwnerPassword(pwd);
- }
- Object setting;
- setting = agent.getRendererOptions().get(NO_PRINT);
- if (setting != null) {
- if (encryptionParams == null) {
- this.encryptionParams = new PDFEncryptionParams();
- }
- this.encryptionParams.setAllowPrint(!booleanValueOf(setting));
- }
- setting = agent.getRendererOptions().get(NO_COPY_CONTENT);
- if (setting != null) {
- if (encryptionParams == null) {
- this.encryptionParams = new PDFEncryptionParams();
- }
- this.encryptionParams.setAllowCopyContent(!booleanValueOf(setting));
- }
- setting = agent.getRendererOptions().get(NO_EDIT_CONTENT);
- if (setting != null) {
- if (encryptionParams == null) {
- this.encryptionParams = new PDFEncryptionParams();
- }
- this.encryptionParams.setAllowEditContent(!booleanValueOf(setting));
- }
- setting = agent.getRendererOptions().get(NO_ANNOTATIONS);
- if (setting != null) {
- if (encryptionParams == null) {
- this.encryptionParams = new PDFEncryptionParams();
- }
- this.encryptionParams.setAllowEditAnnotations(!booleanValueOf(setting));
- }
- String s = (String)agent.getRendererOptions().get(PDF_A_MODE);
- if (s != null) {
- this.pdfAMode = PDFAMode.valueOf(s);
- }
- s = (String)agent.getRendererOptions().get(PDF_X_MODE);
- if (s != null) {
- this.pdfXMode = PDFXMode.valueOf(s);
- }
- s = (String)agent.getRendererOptions().get(KEY_OUTPUT_PROFILE);
- if (s != null) {
- this.outputProfileURI = s;
- }
- setting = agent.getRendererOptions().get(KEY_DISABLE_SRGB_COLORSPACE);
- if (setting != null) {
- this.disableSRGBColorSpace = booleanValueOf(setting);
- }
+ this.pdfUtil = new PDFRenderingUtil(getUserAgent());
}
- /**
- * {@inheritDoc}
- */
+ PDFRenderingUtil getPDFUtil() {
+ return this.pdfUtil;
+ }
+
+ /** {@inheritDoc} */
public void startRenderer(OutputStream stream) throws IOException {
if (userAgent == null) {
throw new IllegalStateException("UserAgent must be set before starting the renderer");
}
ostream = stream;
- this.pdfDoc = new PDFDocument(
- userAgent.getProducer() != null ? userAgent.getProducer() : "");
- this.pdfDoc.getProfile().setPDFAMode(this.pdfAMode);
- this.pdfDoc.getProfile().setPDFXMode(this.pdfXMode);
- this.pdfDoc.getInfo().setCreator(userAgent.getCreator());
- this.pdfDoc.getInfo().setCreationDate(userAgent.getCreationDate());
- this.pdfDoc.getInfo().setAuthor(userAgent.getAuthor());
- this.pdfDoc.getInfo().setTitle(userAgent.getTitle());
- this.pdfDoc.getInfo().setKeywords(userAgent.getKeywords());
- this.pdfDoc.setFilterMap(filterMap);
- this.pdfDoc.outputHeader(ostream);
-
- //Setup encryption if necessary
- PDFEncryptionManager.setupPDFEncryption(encryptionParams, this.pdfDoc);
-
- addsRGBColorSpace();
- if (this.outputProfileURI != null) {
- addDefaultOutputProfile();
- }
- if (pdfXMode != PDFXMode.DISABLED) {
- log.debug(pdfXMode + " is active.");
- log.warn("Note: " + pdfXMode
- + " support is work-in-progress and not fully implemented, yet!");
- addPDFXOutputIntent();
- }
- if (pdfAMode.isPDFA1LevelB()) {
- log.debug("PDF/A is active. Conformance Level: " + pdfAMode);
- addPDFA1OutputIntent();
- }
-
- }
-
- private void addsRGBColorSpace() throws IOException {
- if (disableSRGBColorSpace) {
- if (this.pdfAMode != PDFAMode.DISABLED
- || this.pdfXMode != PDFXMode.DISABLED
- || this.outputProfileURI != null) {
- throw new IllegalStateException("It is not possible to disable the sRGB color"
- + " space if PDF/A or PDF/X functionality is enabled or an"
- + " output profile is set!");
- }
- } else {
- if (this.sRGBColorSpace != null) {
- return;
- }
- //Map sRGB as default RGB profile for DeviceRGB
- this.sRGBColorSpace = PDFICCBasedColorSpace.setupsRGBAsDefaultRGBColorSpace(pdfDoc);
- }
- }
-
- private void addDefaultOutputProfile() throws IOException {
- if (this.outputProfile != null) {
- return;
- }
- ICC_Profile profile;
- InputStream in = null;
- if (this.outputProfileURI != null) {
- this.outputProfile = pdfDoc.getFactory().makePDFICCStream();
- Source src = userAgent.resolveURI(this.outputProfileURI);
- if (src == null) {
- throw new IOException("Output profile not found: " + this.outputProfileURI);
- }
- if (src instanceof StreamSource) {
- in = ((StreamSource)src).getInputStream();
- } else {
- in = new URL(src.getSystemId()).openStream();
- }
- try {
- profile = ICC_Profile.getInstance(in);
- } finally {
- IOUtils.closeQuietly(in);
- }
- this.outputProfile.setColorSpace(profile, null);
- } else {
- //Fall back to sRGB profile
- outputProfile = sRGBColorSpace.getICCStream();
- }
- }
-
- /**
- * 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 {
- addDefaultOutputProfile();
-
- String desc = ColorProfileUtil.getICCProfileDescription(this.outputProfile.getICCProfile());
- PDFOutputIntent outputIntent = pdfDoc.getFactory().makeOutputIntent();
- outputIntent.setSubtype(PDFOutputIntent.GTS_PDFA1);
- outputIntent.setDestOutputProfile(this.outputProfile);
- outputIntent.setOutputConditionIdentifier(desc);
- outputIntent.setInfo(outputIntent.getOutputConditionIdentifier());
- pdfDoc.getRoot().addOutputIntent(outputIntent);
- }
-
- /**
- * Adds an OutputIntent to the PDF as mandated by PDF/X 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 addPDFXOutputIntent() throws IOException {
- addDefaultOutputProfile();
-
- String desc = ColorProfileUtil.getICCProfileDescription(this.outputProfile.getICCProfile());
- int deviceClass = this.outputProfile.getICCProfile().getProfileClass();
- if (deviceClass != ICC_Profile.CLASS_OUTPUT) {
- throw new PDFConformanceException(pdfDoc.getProfile().getPDFXMode() + " requires that"
- + " the DestOutputProfile be an Output Device Profile. "
- + desc + " does not match that requirement.");
- }
- PDFOutputIntent outputIntent = pdfDoc.getFactory().makeOutputIntent();
- outputIntent.setSubtype(PDFOutputIntent.GTS_PDFX);
- outputIntent.setDestOutputProfile(this.outputProfile);
- outputIntent.setOutputConditionIdentifier(desc);
- outputIntent.setInfo(outputIntent.getOutputConditionIdentifier());
- pdfDoc.getRoot().addOutputIntent(outputIntent);
+ this.pdfDoc = pdfUtil.setupPDFDocument(stream);
}
/**
@@ -498,9 +243,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void stopRenderer() throws IOException {
finishOpenGoTos();
@@ -513,7 +256,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
pages = null;
pageReferences.clear();
- pvReferences.clear();
+ //pvReferences.clear();
pdfResources = null;
currentStream = null;
currentContext = null;
@@ -546,7 +289,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
} else if (odi instanceof OffDocumentExtensionAttachment) {
ExtensionAttachment attachment = ((OffDocumentExtensionAttachment)odi).getAttachment();
if (XMPMetadata.CATEGORY.equals(attachment.getCategory())) {
- renderXMPMetadata((XMPMetadata)attachment);
+ pdfUtil.renderXMPMetadata((XMPMetadata)attachment);
}
}
}
@@ -606,27 +349,12 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
}
}
- private void renderXMPMetadata(XMPMetadata metadata) {
- Metadata docXMP = metadata.getMetadata();
- Metadata fopXMP = PDFMetadata.createXMPFromPDFDocument(pdfDoc);
- //Merge FOP's own metadata into the one from the XSL-FO document
- fopXMP.mergeInto(docXMP);
- XMPBasicAdapter xmpBasic = XMPBasicSchema.getAdapter(docXMP);
- //Metadata was changed so update metadata date
- xmpBasic.setMetadataDate(new java.util.Date());
- PDFMetadata.updateInfoFromMetadata(docXMP, pdfDoc.getInfo());
-
- PDFMetadata pdfMetadata = pdfDoc.getFactory().makeMetadata(
- docXMP, metadata.isReadOnly());
- pdfDoc.getRoot().setMetadata(pdfMetadata);
- }
-
/** {@inheritDoc} */
public Graphics2DAdapter getGraphics2DAdapter() {
return new PDFGraphics2DAdapter(this);
}
- /**
+ /**
* writes out a comment.
* @param text text for the comment
*/
@@ -698,14 +426,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
pdfDoc.getRoot().setLanguage(langCode);
}
}
- if (pdfDoc.getRoot().getMetadata() == null) {
- //If at this time no XMP metadata for the overall document has been set, create it
- //from the PDFInfo object.
- Metadata xmp = PDFMetadata.createXMPFromPDFDocument(pdfDoc);
- PDFMetadata pdfMetadata = pdfDoc.getFactory().makeMetadata(
- xmp, true);
- pdfDoc.getRoot().setMetadata(pdfMetadata);
- }
+ pdfUtil.generateDefaultXMPMetadata();
}
/**
@@ -731,30 +452,18 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
Rectangle2D bounds = page.getViewArea();
double w = bounds.getWidth();
double h = bounds.getHeight();
- currentPage = this.pdfDoc.getFactory().makePage(
+ this.currentPage = this.pdfDoc.getFactory().makePage(
this.pdfResources,
(int) Math.round(w / 1000), (int) Math.round(h / 1000),
page.getPageIndex());
pageReferences.put(page.getKey(), currentPage.referencePDF());
- pvReferences.put(page.getKey(), page);
-
- //Produce page labels
- PDFPageLabels pageLabels = this.pdfDoc.getRoot().getPageLabels();
- if (pageLabels == null) {
- //Set up PageLabels
- pageLabels = this.pdfDoc.getFactory().makePageLabels();
- this.pdfDoc.getRoot().setPageLabels(pageLabels);
- }
- PDFNumsArray nums = pageLabels.getNums();
- PDFDictionary dict = new PDFDictionary(nums);
- dict.put("P", page.getPageNumberString());
- //TODO If the sequence of generated page numbers were inspected, this could be
- //expressed in a more space-efficient way
- nums.put(page.getPageIndex(), dict);
- }
-
+ //pvReferences.put(page.getKey(), page);
+
+ pdfUtil.generatePageLabel(page.getPageIndex(), page.getPageNumberString());
+ }
+
/**
- * This method creates a pdf stream for the current page
+ * This method creates a PDF stream for the current page
* uses it as the contents of a new page. The page is written
* immediately to the output stream.
* {@inheritDoc}
@@ -788,7 +497,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
pageHeight / 1000f);
currentState.concatenate(basicPageTransform);
currentStream.add(CTMHelper.toPDFString(basicPageTransform, false) + " cm\n");
-
+
super.renderPage(page);
this.pdfDoc.registerObject(currentStream);
@@ -810,9 +519,9 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
new AffineTransform(CTMHelper.toPDFArray(ctm)));
if (clippingRect != null) {
- clipRect((float)clippingRect.getX() / 1000f,
- (float)clippingRect.getY() / 1000f,
- (float)clippingRect.getWidth() / 1000f,
+ clipRect((float)clippingRect.getX() / 1000f,
+ (float)clippingRect.getY() / 1000f,
+ (float)clippingRect.getWidth() / 1000f,
(float)clippingRect.getHeight() / 1000f);
}
// multiply with current CTM
@@ -831,7 +540,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
currentStream.add(CTMHelper.toPDFString(at, false) + " cm\n");
}
}
-
+
/**
* Formats a float value (normally coordinates) as Strings.
* @param value the value
@@ -840,9 +549,9 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
protected static String format(float value) {
return PDFNumber.doubleOut(value);
}
-
+
/** {@inheritDoc} */
- protected void drawBorderLine(float x1, float y1, float x2, float y2,
+ protected void drawBorderLine(float x1, float y1, float x2, float y2,
boolean horz, boolean startOrBefore, int style, Color col) {
float w = x2 - x1;
float h = y2 - y1;
@@ -852,7 +561,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
return;
}
switch (style) {
- case Constants.EN_DASHED:
+ case Constants.EN_DASHED:
setColor(col, false, null);
if (horz) {
float unit = Math.abs(2 * h);
@@ -864,7 +573,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
currentStream.add("[" + format(unit) + "] 0 d ");
currentStream.add(format(h) + " w\n");
float ym = y1 + (h / 2);
- currentStream.add(format(x1) + " " + format(ym) + " m "
+ currentStream.add(format(x1) + " " + format(ym) + " m "
+ format(x2) + " " + format(ym) + " l S\n");
} else {
float unit = Math.abs(2 * w);
@@ -876,7 +585,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
currentStream.add("[" + format(unit) + "] 0 d ");
currentStream.add(format(w) + " w\n");
float xm = x1 + (w / 2);
- currentStream.add(format(xm) + " " + format(y1) + " m "
+ currentStream.add(format(xm) + " " + format(y1) + " m "
+ format(xm) + " " + format(y2) + " l S\n");
}
break;
@@ -893,7 +602,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
currentStream.add("[0 " + format(unit) + "] 0 d ");
currentStream.add(format(h) + " w\n");
float ym = y1 + (h / 2);
- currentStream.add(format(x1) + " " + format(ym) + " m "
+ currentStream.add(format(x1) + " " + format(ym) + " m "
+ format(x2) + " " + format(ym) + " l S\n");
} else {
float unit = Math.abs(2 * w);
@@ -905,7 +614,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
currentStream.add("[0 " + format(unit) + " ] 0 d ");
currentStream.add(format(w) + " w\n");
float xm = x1 + (w / 2);
- currentStream.add(format(xm) + " " + format(y1) + " m "
+ currentStream.add(format(xm) + " " + format(y1) + " m "
+ format(xm) + " " + format(y2) + " l S\n");
}
break;
@@ -917,18 +626,18 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
currentStream.add(format(h3) + " w\n");
float ym1 = y1 + (h3 / 2);
float ym2 = ym1 + h3 + h3;
- currentStream.add(format(x1) + " " + format(ym1) + " m "
+ currentStream.add(format(x1) + " " + format(ym1) + " m "
+ format(x2) + " " + format(ym1) + " l S\n");
- currentStream.add(format(x1) + " " + format(ym2) + " m "
+ currentStream.add(format(x1) + " " + format(ym2) + " m "
+ format(x2) + " " + format(ym2) + " l S\n");
} else {
float w3 = w / 3;
currentStream.add(format(w3) + " w\n");
float xm1 = x1 + (w3 / 2);
float xm2 = xm1 + w3 + w3;
- currentStream.add(format(xm1) + " " + format(y1) + " m "
+ currentStream.add(format(xm1) + " " + format(y1) + " m "
+ format(xm1) + " " + format(y2) + " l S\n");
- currentStream.add(format(xm2) + " " + format(y1) + " m "
+ currentStream.add(format(xm2) + " " + format(y1) + " m "
+ format(xm2) + " " + format(y2) + " l S\n");
}
break;
@@ -944,13 +653,13 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
currentStream.add(format(h3) + " w\n");
float ym1 = y1 + (h3 / 2);
setColor(uppercol, false, null);
- currentStream.add(format(x1) + " " + format(ym1) + " m "
+ currentStream.add(format(x1) + " " + format(ym1) + " m "
+ format(x2) + " " + format(ym1) + " l S\n");
setColor(col, false, null);
- currentStream.add(format(x1) + " " + format(ym1 + h3) + " m "
+ currentStream.add(format(x1) + " " + format(ym1 + h3) + " m "
+ format(x2) + " " + format(ym1 + h3) + " l S\n");
setColor(lowercol, false, null);
- currentStream.add(format(x1) + " " + format(ym1 + h3 + h3) + " m "
+ currentStream.add(format(x1) + " " + format(ym1 + h3 + h3) + " m "
+ format(x2) + " " + format(ym1 + h3 + h3) + " l S\n");
} else {
Color leftcol = lightenColor(col, -colFactor);
@@ -959,13 +668,13 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
currentStream.add(format(w3) + " w\n");
float xm1 = x1 + (w3 / 2);
setColor(leftcol, false, null);
- currentStream.add(format(xm1) + " " + format(y1) + " m "
+ currentStream.add(format(xm1) + " " + format(y1) + " m "
+ format(xm1) + " " + format(y2) + " l S\n");
setColor(col, false, null);
- currentStream.add(format(xm1 + w3) + " " + format(y1) + " m "
+ currentStream.add(format(xm1 + w3) + " " + format(y1) + " m "
+ format(xm1 + w3) + " " + format(y2) + " l S\n");
setColor(rightcol, false, null);
- currentStream.add(format(xm1 + w3 + w3) + " " + format(y1) + " m "
+ currentStream.add(format(xm1 + w3 + w3) + " " + format(y1) + " m "
+ format(xm1 + w3 + w3) + " " + format(y2) + " l S\n");
}
break;
@@ -981,14 +690,14 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
currentStream.add(format(h) + " w\n");
float ym1 = y1 + (h / 2);
setColor(c, false, null);
- currentStream.add(format(x1) + " " + format(ym1) + " m "
+ currentStream.add(format(x1) + " " + format(ym1) + " m "
+ format(x2) + " " + format(ym1) + " l S\n");
} else {
c = lightenColor(c, (startOrBefore ? 1 : -1) * colFactor);
currentStream.add(format(w) + " w\n");
float xm1 = x1 + (w / 2);
setColor(c, false, null);
- currentStream.add(format(xm1) + " " + format(y1) + " m "
+ currentStream.add(format(xm1) + " " + format(y1) + " m "
+ format(xm1) + " " + format(y2) + " l S\n");
}
break;
@@ -1001,17 +710,17 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
if (horz) {
currentStream.add(format(h) + " w\n");
float ym = y1 + (h / 2);
- currentStream.add(format(x1) + " " + format(ym) + " m "
+ currentStream.add(format(x1) + " " + format(ym) + " m "
+ format(x2) + " " + format(ym) + " l S\n");
} else {
currentStream.add(format(w) + " w\n");
float xm = x1 + (w / 2);
- currentStream.add(format(xm) + " " + format(y1) + " m "
+ currentStream.add(format(xm) + " " + format(y1) + " m "
+ format(xm) + " " + format(y2) + " l S\n");
}
}
}
-
+
/**
* Sets the current line width in points.
* @param width line width in points
@@ -1022,10 +731,10 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
currentStream.add(format(width) + " w\n");
}
}
-
+
/** {@inheritDoc} */
protected void clipRect(float x, float y, float width, float height) {
- currentStream.add(format(x) + " " + format(y) + " "
+ currentStream.add(format(x) + " " + format(y) + " "
+ format(width) + " " + format(height) + " re ");
clip();
}
@@ -1039,42 +748,42 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
}
/**
- * Moves the current point to (x, y), omitting any connecting line segment.
+ * Moves the current point to (x, y), omitting any connecting line segment.
* @param x x coordinate
* @param y y coordinate
*/
protected void moveTo(float x, float y) {
currentStream.add(format(x) + " " + format(y) + " m ");
}
-
+
/**
- * Appends a straight line segment from the current point to (x, y). The
- * new current point is (x, y).
+ * Appends a straight line segment from the current point to (x, y). The
+ * new current point is (x, y).
* @param x x coordinate
* @param y y coordinate
*/
protected void lineTo(float x, float y) {
currentStream.add(format(x) + " " + format(y) + " l ");
}
-
+
/**
- * Closes the current subpath by appending a straight line segment from
+ * Closes the current subpath by appending a straight line segment from
* the current point to the starting point of the subpath.
*/
protected void closePath() {
currentStream.add("h ");
}
- /**
- * {@inheritDoc}
+ /**
+ * {@inheritDoc}
*/
protected void fillRect(float x, float y, float w, float h) {
if (w != 0 && h != 0) {
- currentStream.add(format(x) + " " + format(y) + " "
+ currentStream.add(format(x) + " " + format(y) + " "
+ format(w) + " " + format(h) + " re f\n");
}
}
-
+
/**
* Draw a line.
*
@@ -1130,7 +839,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
}
/**
- * Returns area's id if it is the first area in the document with that id
+ * Returns area's id if it is the first area in the document with that id
* (i.e. if the area qualifies as a link target).
* Otherwise, or if the area has no id, null is returned.
*
@@ -1215,7 +924,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
* @param pdfPageRef the PDF page reference string
* @param relativeIPP the *relative* IP position in millipoints
* @param relativeBPP the *relative* BP position in millipoints
- * @param tf the transformation to apply once the relative positions have been
+ * @param tf the transformation to apply once the relative positions have been
* converted to points
*/
protected void saveAbsolutePosition(String id, String pdfPageRef,
@@ -1257,13 +966,13 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
* @param relativeBPP the *relative* BP position in millipoints
*/
protected void saveAbsolutePosition(String id, int relativeIPP, int relativeBPP) {
- saveAbsolutePosition(id, currentPageRef,
+ saveAbsolutePosition(id, currentPageRef,
relativeIPP, relativeBPP, currentState.getTransform());
}
/**
- * If the given block area is a possible link target, its id + absolute position will
- * be saved. The saved position is only correct if this function is called at the very
+ * If the given block area is a possible link target, its id + absolute position will
+ * be saved. The saved position is only correct if this function is called at the very
* start of renderBlock!
*
* @param block the block area in question
@@ -1391,7 +1100,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
// warn if link trait found but not allowed, else create link
if (linkTraitFound) {
if (!annotsAllowed) {
- log.warn("Skipping annotation for a link due to PDF profile: "
+ log.warn("Skipping annotation for a link due to PDF profile: "
+ pdfDoc.getProfile());
} else if (action != null) {
PDFLink pdfLink = factory.makeLink(ipRect, action);
@@ -1407,23 +1116,23 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
}
return tf;
}
-
+
/** {@inheritDoc} */
public void renderText(TextArea text) {
renderInlineAreaBackAndBorders(text);
Color ct = (Color) text.getTrait(Trait.COLOR);
updateColor(ct, true);
-
+
beginTextObject();
String fontName = getInternalFontNameForArea(text);
int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue();
-
+
// This assumes that *all* CIDFonts use a /ToUnicode mapping
Typeface tf = getTypeface(fontName);
-
+
textutil.updateTf(fontName, size / 1000f, tf.isMultiByte());
-
+
// word.getOffset() = only height of text itself
// currentBlockIPPosition: 0 for beginning of line; nonzero
@@ -1436,7 +1145,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
super.renderText(text);
textutil.writeTJ();
-
+
renderTextDecoration(tf, size, text, bl, rx);
}
@@ -1445,7 +1154,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
Font font = getFontFromArea(word.getParentArea());
String s = word.getWord();
- escapeText(s, word.getLetterAdjustArray(),
+ escapeText(s, word.getLetterAdjustArray(),
font, (AbstractTextArea)word.getParentArea());
super.renderWord(word);
@@ -1455,7 +1164,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
public void renderSpace(SpaceArea space) {
Font font = getFontFromArea(space.getParentArea());
String s = space.getSpace();
-
+
AbstractTextArea textArea = (AbstractTextArea)space.getParentArea();
escapeText(s, null, font, textArea);
@@ -1484,7 +1193,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
Font font, AbstractTextArea parentArea) {
escapeText(s, 0, s.length(), letterAdjust, font, parentArea);
}
-
+
/**
* Escapes text according to PDF rules.
* @param s Text to escape
@@ -1506,7 +1215,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
}
int l = s.length();
-
+
for (int i = start; i < end; i++) {
char orgChar = s.charAt(i);
char ch;
@@ -1552,21 +1261,19 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
/**
* Establishes a new foreground or fill color. In contrast to updateColor
* this method does not check the PDFState for optimization possibilities.
- * @param col the color to apply
+ * @param col the color to apply
* @param fill true to set the fill color, false for the foreground color
* @param pdf StringBuffer to write the PDF code to, if null, the code is
* written to the current stream.
*/
protected void setColor(Color col, boolean fill, StringBuffer pdf) {
- PDFColor color = new PDFColor(this.pdfDoc, col);
-
if (pdf != null) {
- pdf.append(color.getColorSpaceOut(fill));
+ pdfUtil.setColor(col, fill, pdf);
} else {
- currentStream.add(color.getColorSpaceOut(fill));
+ pdfUtil.setColor(col, fill, this.currentStream);
}
}
-
+
/**
* Establishes a new foreground or fill color.
* @param col the color to apply (null skips this operation)
@@ -1594,7 +1301,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
protected void updateColor(Color col, boolean fill) {
updateColor(col, fill, null);
}
-
+
/** {@inheritDoc} */
public void renderImage(Image image, Rectangle2D pos) {
endTextObject();
@@ -1607,7 +1314,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
endTextObject();
putImage(url, pos, foreignAttributes);
}
-
+
/**
* Adds a PDF XObject (a bitmap or form) to the PDF that will later be referenced.
* @param uri URL of the bitmap
@@ -1617,7 +1324,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
protected void putImage(String uri, Rectangle2D pos) {
putImage(uri, pos, null);
}
-
+
/**
* Adds a PDF XObject (a bitmap or form) to the PDF that will later be referenced.
* @param uri URL of the bitmap
@@ -1649,11 +1356,11 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
try {
ImageSessionContext sessionContext = getUserAgent().getImageSessionContext();
info = manager.getImageInfo(uri, sessionContext);
-
+
Map hints = ImageUtil.getDefaultHints(sessionContext);
org.apache.xmlgraphics.image.loader.Image img = manager.getImage(
info, imageHandlerRegistry.getSupportedFlavors(), hints, sessionContext);
-
+
//First check for a dynamically registered handler
PDFImageHandler handler = imageHandlerRegistry.getHandler(img.getClass());
if (handler != null) {
@@ -1710,13 +1417,13 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
currentStream.add(format(w) + " 0 0 "
+ format(-h) + " "
+ format(currentIPPosition / 1000f + x) + " "
- + format(currentBPPosition / 1000f + h + y)
+ + format(currentBPPosition / 1000f + h + y)
+ " cm\n" + xobj.getName() + " Do\n");
restoreGraphicsState();
}
/** {@inheritDoc} */
- protected RendererContext createRendererContext(int x, int y, int width, int height,
+ protected RendererContext createRendererContext(int x, int y, int width, int height,
Map foreignAttributes) {
RendererContext context = super.createRendererContext(
x, y, width, height, foreignAttributes);
@@ -1747,7 +1454,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
int style = area.getRuleStyle();
float startx = (currentIPPosition + area.getBorderAndPaddingWidthStart()) / 1000f;
float starty = (currentBPPosition + area.getOffset()) / 1000f;
- float endx = (currentIPPosition + area.getBorderAndPaddingWidthStart()
+ float endx = (currentIPPosition + area.getBorderAndPaddingWidthStart()
+ area.getIPD()) / 1000f;
float ruleThickness = area.getRuleThickness() / 1000f;
Color col = (Color)area.getTrait(Trait.COLOR);
@@ -1756,7 +1463,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
case EN_SOLID:
case EN_DASHED:
case EN_DOUBLE:
- drawBorderLine(startx, starty, endx, starty + ruleThickness,
+ drawBorderLine(startx, starty, endx, starty + ruleThickness,
true, true, style, col);
break;
case EN_DOTTED:
@@ -1764,7 +1471,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
//This displaces the dots to the right by half a dot's width
//TODO There's room for improvement here
currentStream.add("1 0 0 1 " + format(ruleThickness / 2) + " 0 cm\n");
- drawBorderLine(startx, starty, endx, starty + ruleThickness,
+ drawBorderLine(startx, starty, endx, starty + ruleThickness,
true, true, style, col);
break;
case EN_GROOVE:
@@ -1809,13 +1516,13 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
public String getMimeType() {
return MIME_TYPE;
}
-
+
/**
* Sets the PDF/A mode for the PDF renderer.
* @param mode the PDF/A mode
*/
public void setAMode(PDFAMode mode) {
- this.pdfAMode = mode;
+ this.pdfUtil.setAMode(mode);
}
/**
@@ -1823,7 +1530,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
* @param mode the PDF/X mode
*/
public void setXMode(PDFXMode mode) {
- this.pdfXMode = mode;
+ this.pdfUtil.setXMode(mode);
}
/**
@@ -1831,7 +1538,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
* @param outputProfileURI the URI to the output color profile
*/
public void setOutputProfileURI(String outputProfileURI) {
- this.outputProfileURI = outputProfileURI;
+ this.pdfUtil.setOutputProfileURI(outputProfileURI);
}
/**
@@ -1839,15 +1546,15 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
* @param filterMap the filter map
*/
public void setFilterMap(Map filterMap) {
- this.filterMap = filterMap;
+ this.pdfUtil.setFilterMap(filterMap);
}
/**
* Sets the encryption parameters used by the PDF renderer.
- * @param encryptionParams the encryption parameters
+ * @param encryptionParams the encryption parameters
*/
public void setEncryptionParams(PDFEncryptionParams encryptionParams) {
- this.encryptionParams = encryptionParams;
+ this.pdfUtil.setEncryptionParams(encryptionParams);
}
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java b/src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java
index 51e13dde1..bee8e1175 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java
@@ -5,9 +5,9 @@
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -33,12 +33,15 @@ import org.apache.fop.pdf.PDFFilterList;
import org.apache.fop.pdf.PDFXMode;
import org.apache.fop.render.PrintRendererConfigurator;
import org.apache.fop.render.Renderer;
+import org.apache.fop.render.intermediate.IFPainter;
+import org.apache.fop.render.intermediate.IFPainterConfigurator;
import org.apache.fop.util.LogUtil;
/**
- * PDF renderer configurator
+ * PDF renderer configurator
*/
-public class PDFRendererConfigurator extends PrintRendererConfigurator {
+public class PDFRendererConfigurator extends PrintRendererConfigurator
+ implements IFPainterConfigurator {
/**
* Default constructor
@@ -59,75 +62,93 @@ public class PDFRendererConfigurator extends PrintRendererConfigurator {
Configuration cfg = super.getRendererConfig(renderer);
if (cfg != null) {
PDFRenderer pdfRenderer = (PDFRenderer)renderer;
- //PDF filters
- try {
- Map filterMap = buildFilterMapFromConfiguration(cfg);
- if (filterMap != null) {
- pdfRenderer.setFilterMap(filterMap);
- }
- } catch (ConfigurationException e) {
- LogUtil.handleException(log, e, false);
- }
-
super.configure(renderer);
-
- String s = cfg.getChild(PDFRenderer.PDF_A_MODE, true).getValue(null);
- if (s != null) {
- pdfRenderer.setAMode(PDFAMode.valueOf(s));
- }
- s = cfg.getChild(PDFRenderer.PDF_X_MODE, true).getValue(null);
- if (s != null) {
- pdfRenderer.setXMode(PDFXMode.valueOf(s));
+
+ PDFRenderingUtil pdfUtil = pdfRenderer.getPDFUtil();
+ configure(cfg, pdfUtil);
+ }
+ }
+
+ private void configure(Configuration cfg, PDFRenderingUtil pdfUtil) throws FOPException {
+ //PDF filters
+ try {
+ Map filterMap = buildFilterMapFromConfiguration(cfg);
+ if (filterMap != null) {
+ pdfUtil.setFilterMap(filterMap);
}
- Configuration encryptionParamsConfig = cfg.getChild(PDFRenderer.ENCRYPTION_PARAMS, false);
- if (encryptionParamsConfig != null) {
- PDFEncryptionParams encryptionParams = new PDFEncryptionParams();
- Configuration ownerPasswordConfig = encryptionParamsConfig.getChild(
- PDFRenderer.OWNER_PASSWORD, false);
- if (ownerPasswordConfig != null) {
- String ownerPassword = ownerPasswordConfig.getValue(null);
- if (ownerPassword != null) {
- encryptionParams.setOwnerPassword(ownerPassword);
- }
- }
- Configuration userPasswordConfig = encryptionParamsConfig.getChild(
- PDFRenderer.USER_PASSWORD, false);
- if (userPasswordConfig != null) {
- String userPassword = userPasswordConfig.getValue(null);
- if (userPassword != null) {
- encryptionParams.setUserPassword(userPassword);
- }
- }
- Configuration noPrintConfig = encryptionParamsConfig.getChild(
- PDFRenderer.NO_PRINT, false);
- if (noPrintConfig != null) {
- encryptionParams.setAllowPrint(false);
- }
- Configuration noCopyContentConfig = encryptionParamsConfig.getChild(
- PDFRenderer.NO_COPY_CONTENT, false);
- if (noCopyContentConfig != null) {
- encryptionParams.setAllowCopyContent(false);
- }
- Configuration noEditContentConfig = encryptionParamsConfig.getChild(
- PDFRenderer.NO_EDIT_CONTENT, false);
- if (noEditContentConfig != null) {
- encryptionParams.setAllowEditContent(false);
+ } catch (ConfigurationException e) {
+ LogUtil.handleException(log, e, false);
+ }
+
+ String s = cfg.getChild(PDFRenderer.PDF_A_MODE, true).getValue(null);
+ if (s != null) {
+ pdfUtil.setAMode(PDFAMode.valueOf(s));
+ }
+ s = cfg.getChild(PDFRenderer.PDF_X_MODE, true).getValue(null);
+ if (s != null) {
+ pdfUtil.setXMode(PDFXMode.valueOf(s));
+ }
+ Configuration encryptionParamsConfig = cfg.getChild(PDFRenderer.ENCRYPTION_PARAMS, false);
+ if (encryptionParamsConfig != null) {
+ PDFEncryptionParams encryptionParams = new PDFEncryptionParams();
+ Configuration ownerPasswordConfig = encryptionParamsConfig.getChild(
+ PDFRenderer.OWNER_PASSWORD, false);
+ if (ownerPasswordConfig != null) {
+ String ownerPassword = ownerPasswordConfig.getValue(null);
+ if (ownerPassword != null) {
+ encryptionParams.setOwnerPassword(ownerPassword);
}
- Configuration noAnnotationsConfig = encryptionParamsConfig.getChild(
- PDFRenderer.NO_ANNOTATIONS, false);
- if (noAnnotationsConfig != null) {
- encryptionParams.setAllowEditAnnotations(false);
+ }
+ Configuration userPasswordConfig = encryptionParamsConfig.getChild(
+ PDFRenderer.USER_PASSWORD, false);
+ if (userPasswordConfig != null) {
+ String userPassword = userPasswordConfig.getValue(null);
+ if (userPassword != null) {
+ encryptionParams.setUserPassword(userPassword);
}
- pdfRenderer.setEncryptionParams(encryptionParams);
}
- s = cfg.getChild(PDFRenderer.KEY_OUTPUT_PROFILE, true).getValue(null);
- if (s != null) {
- pdfRenderer.setOutputProfileURI(s);
+ Configuration noPrintConfig = encryptionParamsConfig.getChild(
+ PDFRenderer.NO_PRINT, false);
+ if (noPrintConfig != null) {
+ encryptionParams.setAllowPrint(false);
+ }
+ Configuration noCopyContentConfig = encryptionParamsConfig.getChild(
+ PDFRenderer.NO_COPY_CONTENT, false);
+ if (noCopyContentConfig != null) {
+ encryptionParams.setAllowCopyContent(false);
+ }
+ Configuration noEditContentConfig = encryptionParamsConfig.getChild(
+ PDFRenderer.NO_EDIT_CONTENT, false);
+ if (noEditContentConfig != null) {
+ encryptionParams.setAllowEditContent(false);
}
- Configuration disableColorSpaceConfig = cfg.getChild(PDFRenderer.KEY_DISABLE_SRGB_COLORSPACE, false);
- if (disableColorSpaceConfig != null) {
- pdfRenderer.disableSRGBColorSpace = disableColorSpaceConfig.getValueAsBoolean(false);
+ Configuration noAnnotationsConfig = encryptionParamsConfig.getChild(
+ PDFRenderer.NO_ANNOTATIONS, false);
+ if (noAnnotationsConfig != null) {
+ encryptionParams.setAllowEditAnnotations(false);
}
+ pdfUtil.setEncryptionParams(encryptionParams);
+ }
+ s = cfg.getChild(PDFRenderer.KEY_OUTPUT_PROFILE, true).getValue(null);
+ if (s != null) {
+ pdfUtil.setOutputProfileURI(s);
+ }
+ Configuration disableColorSpaceConfig
+ = cfg.getChild(PDFRenderer.KEY_DISABLE_SRGB_COLORSPACE, false);
+ if (disableColorSpaceConfig != null) {
+ pdfUtil.setDisableSRGBColorSpace(
+ disableColorSpaceConfig.getValueAsBoolean(false));
+ }
+ }
+
+ public void configure(IFPainter painter) throws FOPException {
+ Configuration cfg = super.getRendererConfig(painter.getMimeType());
+ if (cfg != null) {
+ PDFPainter pdfPainter = (PDFPainter)painter;
+ PDFRenderingUtil pdfUtil = pdfPainter.getPDFUtil();
+ configure(cfg, pdfUtil);
+
+ //TODO Configure fonts
}
}
@@ -137,7 +158,7 @@ public class PDFRendererConfigurator extends PrintRendererConfigurator {
* @return Map the newly built filter map
* @throws ConfigurationException if a filter list is defined twice
*/
- public static Map buildFilterMapFromConfiguration(Configuration cfg)
+ public static Map buildFilterMapFromConfiguration(Configuration cfg)
throws ConfigurationException {
Map filterMap = new java.util.HashMap();
Configuration[] filterLists = cfg.getChildren("filterList");
@@ -150,11 +171,11 @@ public class PDFRendererConfigurator extends PrintRendererConfigurator {
String name = filt[j].getValue();
filterList.add(name);
}
-
+
if (type == null) {
type = PDFFilterList.DEFAULT_FILTER;
}
-
+
if (!filterList.isEmpty() && log.isDebugEnabled()) {
StringBuffer debug = new StringBuffer("Adding PDF filter");
if (filterList.size() != 1) {
@@ -169,13 +190,14 @@ public class PDFRendererConfigurator extends PrintRendererConfigurator {
}
log.debug(debug.toString());
}
-
+
if (filterMap.get(type) != null) {
- throw new ConfigurationException("A filterList of type '"
+ throw new ConfigurationException("A filterList of type '"
+ type + "' has already been defined");
}
filterMap.put(type, filterList);
}
- return filterMap;
+ return filterMap;
}
+
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
new file mode 100644
index 000000000..adc3ff771
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
@@ -0,0 +1,439 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+
+import java.awt.Color;
+import java.awt.color.ICC_Profile;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.Map;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.xmp.Metadata;
+import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter;
+import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fo.extensions.xmp.XMPMetadata;
+import org.apache.fop.pdf.PDFAMode;
+import org.apache.fop.pdf.PDFColor;
+import org.apache.fop.pdf.PDFConformanceException;
+import org.apache.fop.pdf.PDFDictionary;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFEncryptionManager;
+import org.apache.fop.pdf.PDFEncryptionParams;
+import org.apache.fop.pdf.PDFICCBasedColorSpace;
+import org.apache.fop.pdf.PDFICCStream;
+import org.apache.fop.pdf.PDFInfo;
+import org.apache.fop.pdf.PDFMetadata;
+import org.apache.fop.pdf.PDFNumsArray;
+import org.apache.fop.pdf.PDFOutputIntent;
+import org.apache.fop.pdf.PDFPageLabels;
+import org.apache.fop.pdf.PDFStream;
+import org.apache.fop.pdf.PDFXMode;
+import org.apache.fop.util.ColorProfileUtil;
+
+/**
+ * Utility class which enables all sorts of features that are not directly connected to the
+ * normal rendering process.
+ */
+class PDFRenderingUtil implements PDFConfigurationConstants {
+
+ /** logging instance */
+ private static Log log = LogFactory.getLog(PDFRenderingUtil.class);
+
+ private FOUserAgent userAgent;
+
+ /** the PDF Document being created */
+ protected PDFDocument pdfDoc;
+
+ /** the PDF/A mode (Default: disabled) */
+ protected PDFAMode pdfAMode = PDFAMode.DISABLED;
+
+ /** the PDF/X mode (Default: disabled) */
+ protected PDFXMode pdfXMode = PDFXMode.DISABLED;
+
+ /** the (optional) encryption parameters */
+ protected PDFEncryptionParams encryptionParams;
+
+ /** Registry of PDF filters */
+ protected Map filterMap;
+
+ /** the ICC stream used as output profile by this document for PDF/A and PDF/X functionality. */
+ protected PDFICCStream outputProfile;
+ /** the default sRGB color space. */
+ protected PDFICCBasedColorSpace sRGBColorSpace;
+ /** controls whether the sRGB color space should be installed */
+ protected boolean disableSRGBColorSpace = false;
+
+ /** Optional URI to an output profile to be used. */
+ protected String outputProfileURI;
+
+
+ PDFRenderingUtil(FOUserAgent userAgent) {
+ this.userAgent = userAgent;
+ initialize();
+ }
+
+ private static boolean booleanValueOf(Object obj) {
+ if (obj instanceof Boolean) {
+ return ((Boolean)obj).booleanValue();
+ } else if (obj instanceof String) {
+ return Boolean.valueOf((String)obj).booleanValue();
+ } else {
+ throw new IllegalArgumentException("Boolean or \"true\" or \"false\" expected.");
+ }
+ }
+
+ private void initialize() {
+ PDFEncryptionParams params
+ = (PDFEncryptionParams)userAgent.getRendererOptions().get(ENCRYPTION_PARAMS);
+ if (params != null) {
+ this.encryptionParams = params; //overwrite if available
+ }
+ String pwd;
+ pwd = (String)userAgent.getRendererOptions().get(USER_PASSWORD);
+ if (pwd != null) {
+ if (encryptionParams == null) {
+ this.encryptionParams = new PDFEncryptionParams();
+ }
+ this.encryptionParams.setUserPassword(pwd);
+ }
+ pwd = (String)userAgent.getRendererOptions().get(OWNER_PASSWORD);
+ if (pwd != null) {
+ if (encryptionParams == null) {
+ this.encryptionParams = new PDFEncryptionParams();
+ }
+ this.encryptionParams.setOwnerPassword(pwd);
+ }
+ Object setting;
+ setting = userAgent.getRendererOptions().get(NO_PRINT);
+ if (setting != null) {
+ if (encryptionParams == null) {
+ this.encryptionParams = new PDFEncryptionParams();
+ }
+ this.encryptionParams.setAllowPrint(!booleanValueOf(setting));
+ }
+ setting = userAgent.getRendererOptions().get(NO_COPY_CONTENT);
+ if (setting != null) {
+ if (encryptionParams == null) {
+ this.encryptionParams = new PDFEncryptionParams();
+ }
+ this.encryptionParams.setAllowCopyContent(!booleanValueOf(setting));
+ }
+ setting = userAgent.getRendererOptions().get(NO_EDIT_CONTENT);
+ if (setting != null) {
+ if (encryptionParams == null) {
+ this.encryptionParams = new PDFEncryptionParams();
+ }
+ this.encryptionParams.setAllowEditContent(!booleanValueOf(setting));
+ }
+ setting = userAgent.getRendererOptions().get(NO_ANNOTATIONS);
+ if (setting != null) {
+ if (encryptionParams == null) {
+ this.encryptionParams = new PDFEncryptionParams();
+ }
+ this.encryptionParams.setAllowEditAnnotations(!booleanValueOf(setting));
+ }
+ String s = (String)userAgent.getRendererOptions().get(PDF_A_MODE);
+ if (s != null) {
+ this.pdfAMode = PDFAMode.valueOf(s);
+ }
+ s = (String)userAgent.getRendererOptions().get(PDF_X_MODE);
+ if (s != null) {
+ this.pdfXMode = PDFXMode.valueOf(s);
+ }
+ s = (String)userAgent.getRendererOptions().get(KEY_OUTPUT_PROFILE);
+ if (s != null) {
+ this.outputProfileURI = s;
+ }
+ setting = userAgent.getRendererOptions().get(KEY_DISABLE_SRGB_COLORSPACE);
+ if (setting != null) {
+ this.disableSRGBColorSpace = booleanValueOf(setting);
+ }
+ }
+
+ public FOUserAgent getUserAgent() {
+ return this.userAgent;
+ }
+
+ /**
+ * Sets the PDF/A mode for the PDF renderer.
+ * @param mode the PDF/A mode
+ */
+ public void setAMode(PDFAMode mode) {
+ this.pdfAMode = mode;
+ }
+
+ /**
+ * Sets the PDF/X mode for the PDF renderer.
+ * @param mode the PDF/X mode
+ */
+ public void setXMode(PDFXMode mode) {
+ this.pdfXMode = mode;
+ }
+
+ /**
+ * Sets the output color profile for the PDF renderer.
+ * @param outputProfileURI the URI to the output color profile
+ */
+ public void setOutputProfileURI(String outputProfileURI) {
+ this.outputProfileURI = outputProfileURI;
+ }
+
+ /**
+ * Enables or disables the default sRGB color space needed for the PDF document to preserve
+ * the sRGB colors used in XSL-FO.
+ * @param disable true to disable, false to enable
+ */
+ public void setDisableSRGBColorSpace(boolean disable) {
+ this.disableSRGBColorSpace = disable;
+ }
+
+ /**
+ * Sets the filter map to be used by the PDF renderer.
+ * @param filterMap the filter map
+ */
+ public void setFilterMap(Map filterMap) {
+ this.filterMap = filterMap;
+ }
+
+ /**
+ * Sets the encryption parameters used by the PDF renderer.
+ * @param encryptionParams the encryption parameters
+ */
+ public void setEncryptionParams(PDFEncryptionParams encryptionParams) {
+ this.encryptionParams = encryptionParams;
+ }
+
+ private void updateInfo() {
+ PDFInfo info = pdfDoc.getInfo();
+ info.setCreator(userAgent.getCreator());
+ info.setCreationDate(userAgent.getCreationDate());
+ info.setAuthor(userAgent.getAuthor());
+ info.setTitle(userAgent.getTitle());
+ info.setKeywords(userAgent.getKeywords());
+ }
+
+ private void updatePDFProfiles() {
+ pdfDoc.getProfile().setPDFAMode(this.pdfAMode);
+ pdfDoc.getProfile().setPDFXMode(this.pdfXMode);
+ }
+
+ private void addsRGBColorSpace() throws IOException {
+ if (disableSRGBColorSpace) {
+ if (this.pdfAMode != PDFAMode.DISABLED
+ || this.pdfXMode != PDFXMode.DISABLED
+ || this.outputProfileURI != null) {
+ throw new IllegalStateException("It is not possible to disable the sRGB color"
+ + " space if PDF/A or PDF/X functionality is enabled or an"
+ + " output profile is set!");
+ }
+ } else {
+ if (this.sRGBColorSpace != null) {
+ return;
+ }
+ //Map sRGB as default RGB profile for DeviceRGB
+ this.sRGBColorSpace = PDFICCBasedColorSpace.setupsRGBAsDefaultRGBColorSpace(pdfDoc);
+ }
+ }
+
+ private void addDefaultOutputProfile() throws IOException {
+ if (this.outputProfile != null) {
+ return;
+ }
+ ICC_Profile profile;
+ InputStream in = null;
+ if (this.outputProfileURI != null) {
+ this.outputProfile = pdfDoc.getFactory().makePDFICCStream();
+ Source src = getUserAgent().resolveURI(this.outputProfileURI);
+ if (src == null) {
+ throw new IOException("Output profile not found: " + this.outputProfileURI);
+ }
+ if (src instanceof StreamSource) {
+ in = ((StreamSource)src).getInputStream();
+ } else {
+ in = new URL(src.getSystemId()).openStream();
+ }
+ try {
+ profile = ICC_Profile.getInstance(in);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ this.outputProfile.setColorSpace(profile, null);
+ } else {
+ //Fall back to sRGB profile
+ outputProfile = sRGBColorSpace.getICCStream();
+ }
+ }
+
+ /**
+ * 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 {
+ addDefaultOutputProfile();
+
+ String desc = ColorProfileUtil.getICCProfileDescription(this.outputProfile.getICCProfile());
+ PDFOutputIntent outputIntent = pdfDoc.getFactory().makeOutputIntent();
+ outputIntent.setSubtype(PDFOutputIntent.GTS_PDFA1);
+ outputIntent.setDestOutputProfile(this.outputProfile);
+ outputIntent.setOutputConditionIdentifier(desc);
+ outputIntent.setInfo(outputIntent.getOutputConditionIdentifier());
+ pdfDoc.getRoot().addOutputIntent(outputIntent);
+ }
+
+ /**
+ * Adds an OutputIntent to the PDF as mandated by PDF/X 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 addPDFXOutputIntent() throws IOException {
+ addDefaultOutputProfile();
+
+ String desc = ColorProfileUtil.getICCProfileDescription(this.outputProfile.getICCProfile());
+ int deviceClass = this.outputProfile.getICCProfile().getProfileClass();
+ if (deviceClass != ICC_Profile.CLASS_OUTPUT) {
+ throw new PDFConformanceException(pdfDoc.getProfile().getPDFXMode() + " requires that"
+ + " the DestOutputProfile be an Output Device Profile. "
+ + desc + " does not match that requirement.");
+ }
+ PDFOutputIntent outputIntent = pdfDoc.getFactory().makeOutputIntent();
+ outputIntent.setSubtype(PDFOutputIntent.GTS_PDFX);
+ outputIntent.setDestOutputProfile(this.outputProfile);
+ outputIntent.setOutputConditionIdentifier(desc);
+ outputIntent.setInfo(outputIntent.getOutputConditionIdentifier());
+ pdfDoc.getRoot().addOutputIntent(outputIntent);
+ }
+
+ public void renderXMPMetadata(XMPMetadata metadata) {
+ Metadata docXMP = metadata.getMetadata();
+ Metadata fopXMP = PDFMetadata.createXMPFromPDFDocument(pdfDoc);
+ //Merge FOP's own metadata into the one from the XSL-FO document
+ fopXMP.mergeInto(docXMP);
+ XMPBasicAdapter xmpBasic = XMPBasicSchema.getAdapter(docXMP);
+ //Metadata was changed so update metadata date
+ xmpBasic.setMetadataDate(new java.util.Date());
+ PDFMetadata.updateInfoFromMetadata(docXMP, pdfDoc.getInfo());
+
+ PDFMetadata pdfMetadata = pdfDoc.getFactory().makeMetadata(
+ docXMP, metadata.isReadOnly());
+ pdfDoc.getRoot().setMetadata(pdfMetadata);
+ }
+
+ public void generateDefaultXMPMetadata() {
+ if (pdfDoc.getRoot().getMetadata() == null) {
+ //If at this time no XMP metadata for the overall document has been set, create it
+ //from the PDFInfo object.
+ Metadata xmp = PDFMetadata.createXMPFromPDFDocument(pdfDoc);
+ PDFMetadata pdfMetadata = pdfDoc.getFactory().makeMetadata(
+ xmp, true);
+ pdfDoc.getRoot().setMetadata(pdfMetadata);
+ }
+ }
+
+ public PDFDocument setupPDFDocument(OutputStream out) throws IOException {
+ if (this.pdfDoc != null) {
+ throw new IllegalStateException("PDFDocument already set up");
+ }
+ this.pdfDoc = new PDFDocument(
+ userAgent.getProducer() != null ? userAgent.getProducer() : "");
+ updateInfo();
+ updatePDFProfiles();
+ pdfDoc.setFilterMap(filterMap);
+ pdfDoc.outputHeader(out);
+
+ //Setup encryption if necessary
+ PDFEncryptionManager.setupPDFEncryption(encryptionParams, pdfDoc);
+
+ addsRGBColorSpace();
+ if (this.outputProfileURI != null) {
+ addDefaultOutputProfile();
+ }
+ if (pdfXMode != PDFXMode.DISABLED) {
+ log.debug(pdfXMode + " is active.");
+ log.warn("Note: " + pdfXMode
+ + " support is work-in-progress and not fully implemented, yet!");
+ addPDFXOutputIntent();
+ }
+ if (pdfAMode.isPDFA1LevelB()) {
+ log.debug("PDF/A is active. Conformance Level: " + pdfAMode);
+ addPDFA1OutputIntent();
+ }
+ return this.pdfDoc;
+ }
+
+ /**
+ * Generates a page label in the PDF document.
+ * @param pageIndex the index of the page
+ * @param pageNumber the formatted page number
+ */
+ public void generatePageLabel(int pageIndex, String pageNumber) {
+ //Produce page labels
+ PDFPageLabels pageLabels = this.pdfDoc.getRoot().getPageLabels();
+ if (pageLabels == null) {
+ //Set up PageLabels
+ pageLabels = this.pdfDoc.getFactory().makePageLabels();
+ this.pdfDoc.getRoot().setPageLabels(pageLabels);
+ }
+ PDFNumsArray nums = pageLabels.getNums();
+ PDFDictionary dict = new PDFDictionary(nums);
+ dict.put("P", pageNumber);
+ //TODO If the sequence of generated page numbers were inspected, this could be
+ //expressed in a more space-efficient way
+ nums.put(pageIndex, dict);
+ }
+
+ /**
+ * Establishes a new foreground or fill color. In contrast to updateColor
+ * this method does not check the PDFState for optimization possibilities.
+ * @param col the color to apply
+ * @param fill true to set the fill color, false for the foreground color
+ * @param pdf StringBuffer to write the PDF code to
+ */
+ public void setColor(Color col, boolean fill, StringBuffer pdf) {
+ assert pdf != null;
+ PDFColor color = new PDFColor(this.pdfDoc, col);
+ pdf.append(color.getColorSpaceOut(fill));
+ }
+
+ /**
+ * Establishes a new foreground or fill color.
+ * @param col the color to apply
+ * @param fill true to set the fill color, false for the foreground color
+ * @param stream the PDFStream to write the PDF code to
+ */
+ public void setColor(Color col, boolean fill, PDFStream stream) {
+ assert stream != null;
+ PDFColor color = new PDFColor(this.pdfDoc, col);
+ stream.add(color.getColorSpaceOut(fill));
+ }
+
+
+}