aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/java/META-INF/services/org.apache.fop.render.Renderer1
-rw-r--r--src/java/org/apache/fop/apps/MimeConstants.java3
-rw-r--r--src/java/org/apache/fop/cli/CommandLineOptions.java31
-rw-r--r--src/java/org/apache/fop/render/Graphics2DImagePainter.java2
-rw-r--r--src/java/org/apache/fop/render/RendererFactory.java227
-rw-r--r--src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java50
-rw-r--r--src/java/org/apache/fop/render/intermediate/AbstractIFPainterMaker.java72
-rw-r--r--src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFPainter.java206
-rw-r--r--src/java/org/apache/fop/render/intermediate/DelegatingFragmentContentHandler.java68
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFConstants.java51
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFContentHandler.java89
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFException.java46
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFPainter.java254
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFRenderer.java565
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFRendererMaker.java56
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFSerializer.java363
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFState.java188
-rw-r--r--src/java/org/apache/fop/util/DelegatingContentHandler.java130
-rw-r--r--src/sandbox/META-INF/services/org.apache.fop.render.Renderer1
-rw-r--r--src/sandbox/META-INF/services/org.apache.fop.render.intermediate.IFPainter2
-rw-r--r--src/sandbox/org/apache/fop/render/svg/AbstractSVGPainter.java303
-rw-r--r--src/sandbox/org/apache/fop/render/svg/SVGConstants.java49
-rw-r--r--src/sandbox/org/apache/fop/render/svg/SVGPainter.java254
-rw-r--r--src/sandbox/org/apache/fop/render/svg/SVGPainterMaker.java48
-rw-r--r--src/sandbox/org/apache/fop/render/svg/SVGPrintPainter.java172
-rw-r--r--src/sandbox/org/apache/fop/render/svg/SVGPrintPainterMaker.java48
26 files changed, 3141 insertions, 138 deletions
diff --git a/src/java/META-INF/services/org.apache.fop.render.Renderer b/src/java/META-INF/services/org.apache.fop.render.Renderer
index 869798b0f..7548d7936 100644
--- a/src/java/META-INF/services/org.apache.fop.render.Renderer
+++ b/src/java/META-INF/services/org.apache.fop.render.Renderer
@@ -8,3 +8,4 @@ org.apache.fop.render.awt.AWTRendererMaker
org.apache.fop.render.print.PrintRendererMaker
org.apache.fop.render.afp.AFPRendererMaker
org.apache.fop.render.pcl.PCLRendererMaker
+org.apache.fop.render.intermediate.IFRendererMaker
diff --git a/src/java/org/apache/fop/apps/MimeConstants.java b/src/java/org/apache/fop/apps/MimeConstants.java
index cdd30c0e1..418dc8524 100644
--- a/src/java/org/apache/fop/apps/MimeConstants.java
+++ b/src/java/org/apache/fop/apps/MimeConstants.java
@@ -73,7 +73,8 @@ public interface MimeConstants {
String MIME_FOP_PRINT = "application/X-fop-print";
/** Apache FOP's area tree XML */
String MIME_FOP_AREA_TREE = "application/X-fop-areatree";
-
+ /** Apache FOP's intermediate format XML */
+ String MIME_FOP_IF = "application/X-fop-intermediate-format";
/** Proposed but non-registered MIME type for XSL-FO */
String MIME_XSL_FO = "text/xsl";
diff --git a/src/java/org/apache/fop/cli/CommandLineOptions.java b/src/java/org/apache/fop/cli/CommandLineOptions.java
index c740339b2..7faea532e 100644
--- a/src/java/org/apache/fop/cli/CommandLineOptions.java
+++ b/src/java/org/apache/fop/cli/CommandLineOptions.java
@@ -45,6 +45,7 @@ import org.apache.fop.pdf.PDFEncryptionParams;
import org.apache.fop.pdf.PDFXMode;
import org.apache.fop.render.Renderer;
import org.apache.fop.render.awt.AWTRenderer;
+import org.apache.fop.render.intermediate.IFRenderer;
import org.apache.fop.render.pdf.PDFRenderer;
import org.apache.fop.render.print.PagesMode;
import org.apache.fop.render.print.PrintRenderer;
@@ -201,6 +202,13 @@ public class CommandLineOptions {
//Make sure the prepared XMLRenderer is used
foUserAgent.setRendererOverride(xmlRenderer);
+ } else if (MimeConstants.MIME_FOP_IF.equals(outputmode)) {
+ // render from FO to Intermediate Format
+ IFRenderer xml2Renderer = new IFRenderer();
+ xml2Renderer.setUserAgent(foUserAgent);
+
+ //Make sure the prepared IFRenderer is used
+ foUserAgent.setRendererOverride(xml2Renderer);
}
return true;
}
@@ -306,6 +314,8 @@ public class CommandLineOptions {
i = i + parseUnknownOption(args, i);
} else if (args[i].equals("-at")) {
i = i + parseAreaTreeOption(args, i);
+ } else if (args[i].equals("-if")) {
+ i = i + parseIntermediateFormatOption(args, i);
} else if (args[i].equals("-v")) {
System.out.println("FOP Version " + Version.getVersion());
} else if (args[i].equals("-param")) {
@@ -651,6 +661,23 @@ public class CommandLineOptions {
}
}
+ private int parseIntermediateFormatOption(String[] args, int i) throws FOPException {
+ setOutputMode(MimeConstants.MIME_FOP_IF);
+ if ((i + 1 == args.length)
+ || (args[i + 1].charAt(0) == '-')) {
+ throw new FOPException("you must specify the intermediate format output file");
+ } else if ((i + 2 == args.length)
+ || (args[i + 2].charAt(0) == '-')) {
+ // only output file is specified
+ outfile = new File(args[i + 1]);
+ return 1;
+ } else {
+ // mimic format and output file have been specified
+ outfile = new File(args[i + 2]);
+ return 2;
+ }
+ }
+
private int parseAreaTreeInputOption(String[] args, int i) throws FOPException {
inputmode = AREATREE_INPUT;
if ((i + 1 == args.length)
@@ -1063,6 +1090,7 @@ public class CommandLineOptions {
+ " -at [mime] out representation of area tree as XML (outfile req'd) \n"
+ " specify optional mime output to allow AT to be converted\n"
+ " to final format later\n"
+ + " -if out representation of area tree as intermediate format XML (outfile req'd)\n"
+ " -print input file will be rendered and sent to the printer \n"
+ " see options with \"-print help\" \n"
+ " -out mime outfile input will be rendered using the given MIME type\n"
@@ -1155,6 +1183,9 @@ public class CommandLineOptions {
} else {
log.info("output file: " + outfile.toString());
}
+ } else if (MimeConstants.MIME_FOP_IF.equals(outputmode)) {
+ log.info("intermediate format");
+ log.info("output file: " + outfile.toString());
} else {
log.info(outputmode);
if (this.useStdOut) {
diff --git a/src/java/org/apache/fop/render/Graphics2DImagePainter.java b/src/java/org/apache/fop/render/Graphics2DImagePainter.java
index 0167417fe..b6464c661 100644
--- a/src/java/org/apache/fop/render/Graphics2DImagePainter.java
+++ b/src/java/org/apache/fop/render/Graphics2DImagePainter.java
@@ -27,4 +27,4 @@ package org.apache.fop.render;
public interface Graphics2DImagePainter
extends org.apache.xmlgraphics.java2d.Graphics2DImagePainter {
-} \ No newline at end of file
+}
diff --git a/src/java/org/apache/fop/render/RendererFactory.java b/src/java/org/apache/fop/render/RendererFactory.java
index d81f900e0..2706a723e 100644
--- a/src/java/org/apache/fop/render/RendererFactory.java
+++ b/src/java/org/apache/fop/render/RendererFactory.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.
@@ -16,7 +16,7 @@
*/
/* $Id$ */
-
+
package org.apache.fop.render;
import java.io.OutputStream;
@@ -34,27 +34,31 @@ import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
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.IFRenderer;
/**
* Factory for FOEventHandlers and Renderers.
*/
public class RendererFactory {
-
+
/** the logger */
private static Log log = LogFactory.getLog(RendererFactory.class);
private Map rendererMakerMapping = new java.util.HashMap();
private Map eventHandlerMakerMapping = new java.util.HashMap();
-
-
+ private Map painterMakerMapping = new java.util.HashMap();
+
/**
* Main constructor.
*/
public RendererFactory() {
discoverRenderers();
discoverFOEventHandlers();
+ discoverPainters();
}
-
+
/**
* Add a new RendererMaker. If another maker has already been registered for a
* particular MIME type, this call overwrites the existing one.
@@ -65,13 +69,13 @@ public class RendererFactory {
for (int i = 0; i < mimes.length; i++) {
//This overrides any renderer previously set for a MIME type
if (rendererMakerMapping.get(mimes[i]) != null) {
- log.trace("Overriding renderer for " + mimes[i]
+ log.trace("Overriding renderer for " + mimes[i]
+ " with " + maker.getClass().getName());
}
rendererMakerMapping.put(mimes[i], maker);
}
}
-
+
/**
* Add a new FOEventHandlerMaker. If another maker has already been registered for a
* particular MIME type, this call overwrites the existing one.
@@ -82,13 +86,30 @@ public class RendererFactory {
for (int i = 0; i < mimes.length; i++) {
//This overrides any event handler previously set for a MIME type
if (eventHandlerMakerMapping.get(mimes[i]) != null) {
- log.trace("Overriding FOEventHandler for " + mimes[i]
+ log.trace("Overriding FOEventHandler for " + mimes[i]
+ " with " + maker.getClass().getName());
}
eventHandlerMakerMapping.put(mimes[i], maker);
}
}
-
+
+ /**
+ * Add a new painter maker. If another maker has already been registered for a
+ * particular MIME type, this call overwrites the existing one.
+ * @param maker the painter maker
+ */
+ public void addPainterMaker(AbstractIFPainterMaker maker) {
+ String[] mimes = maker.getSupportedMimeTypes();
+ for (int i = 0; i < mimes.length; i++) {
+ //This overrides any renderer previously set for a MIME type
+ if (painterMakerMapping.get(mimes[i]) != null) {
+ log.trace("Overriding painter for " + mimes[i]
+ + " with " + maker.getClass().getName());
+ }
+ painterMakerMapping.put(mimes[i], maker);
+ }
+ }
+
/**
* Add a new RendererMaker. If another maker has already been registered for a
* particular MIME type, this call overwrites the existing one.
@@ -114,7 +135,7 @@ public class RendererFactory {
+ AbstractRendererMaker.class.getName());
}
}
-
+
/**
* Add a new FOEventHandlerMaker. If another maker has already been registered for a
* particular MIME type, this call overwrites the existing one.
@@ -140,7 +161,33 @@ public class RendererFactory {
+ AbstractFOEventHandlerMaker.class.getName());
}
}
-
+
+ /**
+ * Add a new painter maker. If another maker has already been registered for a
+ * particular MIME type, this call overwrites the existing one.
+ * @param className the fully qualified class name of the painter maker
+ */
+ public void addPainterMaker(String className) {
+ try {
+ AbstractIFPainterMaker makerInstance
+ = (AbstractIFPainterMaker)Class.forName(className).newInstance();
+ addPainterMaker(makerInstance);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException("Could not find "
+ + className);
+ } catch (InstantiationException e) {
+ throw new IllegalArgumentException("Could not instantiate "
+ + className);
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException("Could not access "
+ + className);
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException(className
+ + " is not an "
+ + AbstractIFPainterMaker.class.getName());
+ }
+ }
+
/**
* Returns a RendererMaker which handles the given MIME type.
* @param mime the requested output format
@@ -151,7 +198,7 @@ public class RendererFactory {
= (AbstractRendererMaker)rendererMakerMapping.get(mime);
return maker;
}
-
+
/**
* Returns a FOEventHandlerMaker which handles the given MIME type.
* @param mime the requested output format
@@ -162,7 +209,18 @@ public class RendererFactory {
= (AbstractFOEventHandlerMaker)eventHandlerMakerMapping.get(mime);
return maker;
}
-
+
+ /**
+ * Returns a RendererMaker which handles the given MIME type.
+ * @param mime the requested output format
+ * @return the requested RendererMaker or null if none is available
+ */
+ public AbstractIFPainterMaker getPainterMaker(String mime) {
+ AbstractIFPainterMaker maker
+ = (AbstractIFPainterMaker)painterMakerMapping.get(mime);
+ return maker;
+ }
+
/**
* Creates a Renderer object based on render-type desired
* @param userAgent the user agent for access to configuration
@@ -170,27 +228,36 @@ public class RendererFactory {
* @return the new Renderer instance
* @throws FOPException if the renderer cannot be properly constructed
*/
- public Renderer createRenderer(FOUserAgent userAgent, String outputFormat)
+ public Renderer createRenderer(FOUserAgent userAgent, String outputFormat)
throws FOPException {
if (userAgent.getRendererOverride() != null) {
return userAgent.getRendererOverride();
} else {
AbstractRendererMaker maker = getRendererMaker(outputFormat);
- if (maker == null) {
- throw new UnsupportedOperationException(
- "No renderer for the requested format available: " + outputFormat);
- }
- Renderer rend = maker.makeRenderer(userAgent);
- rend.setUserAgent(userAgent);
- RendererConfigurator configurator = maker.getConfigurator(userAgent);
- if (configurator != null) {
- configurator.configure(rend);
+ if (maker != null) {
+ Renderer rend = maker.makeRenderer(userAgent);
+ rend.setUserAgent(userAgent);
+ RendererConfigurator configurator = maker.getConfigurator(userAgent);
+ if (configurator != null) {
+ configurator.configure(rend);
+ }
+ return rend;
+ } else {
+ AbstractIFPainterMaker painterMaker = getPainterMaker(outputFormat);
+ if (painterMaker != null) {
+ IFRenderer rend = new IFRenderer();
+ rend.setUserAgent(userAgent);
+ IFPainter painter = painterMaker.makePainter(userAgent);
+ rend.setPainter(painter);
+ return rend;
+ } else {
+ throw new UnsupportedOperationException(
+ "No renderer for the requested format available: " + outputFormat);
+ }
}
- return rend;
}
}
-
-
+
/**
* Creates FOEventHandler instances based on the desired output.
* @param userAgent the user agent for access to configuration
@@ -199,36 +266,75 @@ public class RendererFactory {
* @return the newly constructed FOEventHandler
* @throws FOPException if the FOEventHandler cannot be properly constructed
*/
- public FOEventHandler createFOEventHandler(FOUserAgent userAgent,
+ public FOEventHandler createFOEventHandler(FOUserAgent userAgent,
String outputFormat, OutputStream out) throws FOPException {
if (userAgent.getFOEventHandlerOverride() != null) {
return userAgent.getFOEventHandlerOverride();
} else {
AbstractFOEventHandlerMaker maker = getFOEventHandlerMaker(outputFormat);
- if (maker == null) {
+ if (maker != null) {
+ return maker.makeFOEventHandler(userAgent, out);
+ } else {
AbstractRendererMaker rendMaker = getRendererMaker(outputFormat);
- if (rendMaker == null && userAgent.getRendererOverride() == null) {
- throw new UnsupportedOperationException(
- "Don't know how to handle \"" + outputFormat + "\" as an output format."
- + " Neither an FOEventHandler, nor a Renderer could be found"
- + " for this output format.");
+ AbstractIFPainterMaker painterMaker = null;
+ boolean outputStreamMissing = (userAgent.getRendererOverride() == null);
+ if (rendMaker == null) {
+ painterMaker = getPainterMaker(outputFormat);
+ outputStreamMissing &= (out == null) && (painterMaker.needsOutputStream());
} else {
- if (out == null
- && userAgent.getRendererOverride() == null
- && rendMaker.needsOutputStream()) {
+ outputStreamMissing &= (out == null) && (rendMaker.needsOutputStream());
+ }
+ if (userAgent.getRendererOverride() != null
+ || rendMaker != null
+ || painterMaker != null) {
+ if (outputStreamMissing) {
throw new FOPException(
"OutputStream has not been set");
}
//Found a Renderer so we need to construct an AreaTreeHandler.
return new AreaTreeHandler(userAgent, outputFormat, out);
+ } else {
+ throw new UnsupportedOperationException(
+ "Don't know how to handle \"" + outputFormat + "\" as an output format."
+ + " Neither an FOEventHandler, nor a Renderer could be found"
+ + " for this output format.");
}
- } else {
- return maker.makeFOEventHandler(userAgent, out);
}
}
}
-
+
+ /**
+ * Creates a {@code IFPainter} object based on render-type desired
+ * @param userAgent the user agent for access to configuration
+ * @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
+ * @return the new {@code IFPainter} instance
+ * @throws FOPException if the painter cannot be properly constructed
+ */
+ public IFPainter createPainter(FOUserAgent userAgent, String outputFormat)
+ throws FOPException {
+ /*
+ if (userAgent.getIFPainterOverride() != null) {
+ return userAgent.getIFPainterOverride();
+ } else {
+ */
+ AbstractIFPainterMaker maker = getPainterMaker(outputFormat);
+ if (maker == null) {
+ throw new UnsupportedOperationException(
+ "No renderer for the requested format available: " + outputFormat);
+ }
+ IFPainter painter = maker.makePainter(userAgent);
+ painter.setUserAgent(userAgent);
+ //TODO Add configuration
+ /*
+ RendererConfigurator configurator = maker.getConfigurator(userAgent);
+ if (configurator != null) {
+ configurator.configure(painter);
+ }*/
+ return painter;
+ //}
+ }
+
/**
* @return an array of all supported MIME types
*/
@@ -242,10 +348,14 @@ public class RendererFactory {
while (iter.hasNext()) {
lst.add(((String)iter.next()));
}
+ iter = this.painterMakerMapping.keySet().iterator();
+ while (iter.hasNext()) {
+ lst.add(((String)iter.next()));
+ }
Collections.sort(lst);
return (String[])lst.toArray(new String[lst.size()]);
}
-
+
/**
* Discovers Renderer implementations through the classpath and dynamically
* registers them.
@@ -259,7 +369,7 @@ public class RendererFactory {
AbstractRendererMaker maker = (AbstractRendererMaker)providers.next();
try {
if (log.isDebugEnabled()) {
- log.debug("Dynamically adding maker for Renderer: "
+ log.debug("Dynamically adding maker for Renderer: "
+ maker.getClass().getName());
}
addRendererMaker(maker);
@@ -270,7 +380,7 @@ public class RendererFactory {
}
}
}
-
+
/**
* Discovers FOEventHandler implementations through the classpath and dynamically
* registers them.
@@ -284,7 +394,7 @@ public class RendererFactory {
AbstractFOEventHandlerMaker maker = (AbstractFOEventHandlerMaker)providers.next();
try {
if (log.isDebugEnabled()) {
- log.debug("Dynamically adding maker for FOEventHandler: "
+ log.debug("Dynamically adding maker for FOEventHandler: "
+ maker.getClass().getName());
}
addFOEventHandlerMaker(maker);
@@ -295,5 +405,30 @@ public class RendererFactory {
}
}
}
-
+
+ /**
+ * Discovers {@code IFPainter} implementations through the classpath and dynamically
+ * registers them.
+ */
+ private void discoverPainters() {
+ // add mappings from available services
+ Iterator providers
+ = Service.providers(IFPainter.class);
+ if (providers != null) {
+ while (providers.hasNext()) {
+ AbstractIFPainterMaker maker = (AbstractIFPainterMaker)providers.next();
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("Dynamically adding maker for IFPainter: "
+ + maker.getClass().getName());
+ }
+ addPainterMaker(maker);
+ } catch (IllegalArgumentException e) {
+ log.error("Error while adding maker for IFPainter", e);
+ }
+
+ }
+ }
+ }
+
}
diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java
new file mode 100644
index 000000000..9c9dda61a
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java
@@ -0,0 +1,50 @@
+/*
+ * 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.FOUserAgent;
+
+/**
+ * Abstract base class for IFPainter implementations.
+ */
+public abstract class AbstractIFPainter implements IFPainter {
+
+ private FOUserAgent userAgent;
+
+ /**
+ * Default constructor.
+ */
+ public AbstractIFPainter() {
+ }
+
+ /** {@inheritDoc} */
+ public void setUserAgent(FOUserAgent ua) {
+ this.userAgent = ua;
+ }
+
+ /**
+ * Returns the user agent.
+ * @return the user agent
+ */
+ protected FOUserAgent getUserAgent() {
+ return this.userAgent;
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFPainterMaker.java b/src/java/org/apache/fop/render/intermediate/AbstractIFPainterMaker.java
new file mode 100644
index 000000000..273f90170
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainterMaker.java
@@ -0,0 +1,72 @@
+/*
+ * 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.FOUserAgent;
+
+/**
+ * Base class for factory classes which instantiate {@code IFPainter}s and provide information
+ * about them.
+ */
+public abstract class AbstractIFPainterMaker {
+
+ /**
+ * Instantiates a new {@code IFPainter}.
+ * @param userAgent the user agent
+ * @return the newly instantiated painter
+ */
+ public abstract IFPainter makePainter(FOUserAgent userAgent);
+
+ /**
+ * @return Indicates whether this painter requires an OutputStream to work with.
+ */
+ public abstract boolean needsOutputStream();
+
+ /**
+ * @return an array of MIME types the painter supports.
+ */
+ public abstract String[] getSupportedMimeTypes();
+
+ /**
+ * Returns a renderer config 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
+ */
+ /*
+ public RendererConfigurator getConfigurator(FOUserAgent userAgent) {
+ return null;
+ }*/
+
+ /**
+ * Indicates whether a specific MIME type is supported by this painter.
+ * @param mimeType the MIME type (ex. "application/pdf")
+ * @return true if the MIME type is supported
+ */
+ public boolean isMimeTypeSupported(String mimeType) {
+ String[] mimes = getSupportedMimeTypes();
+ for (int i = 0; i < mimes.length; i++) {
+ if (mimes[i].equals(mimeType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFPainter.java
new file mode 100644
index 000000000..166b6df20
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFPainter.java
@@ -0,0 +1,206 @@
+/*
+ * 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.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Locale;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * Abstract base class for XML-writing IFPainter implementations.
+ */
+public abstract class AbstractXMLWritingIFPainter extends AbstractIFPainter {
+
+ private static final Attributes EMPTY_ATTS = new AttributesImpl();
+
+ /** Constant for the "CDATA" attribute type. */
+ protected static final String CDATA = "CDATA";
+
+ /**
+ * Default SAXTransformerFactory that can be used by subclasses.
+ */
+ protected SAXTransformerFactory tFactory
+ = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+
+ /** Main SAX ContentHandler to receive the generated SAX events. */
+ 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;
+ this.handler = saxResult.getHandler();
+ } else {
+ this.handler = createContentHandler(result);
+ }
+ }
+
+ /**
+ * Returns the main namespace used for generated XML content.
+ * @return the main namespace
+ */
+ protected abstract String getMainNamespace();
+
+ /**
+ * Creates a ContentHandler for the given JAXP Result instance.
+ * @param result the JAXP Result instance
+ * @return the requested SAX ContentHandler
+ * @throws IFException if an error occurs setting up the output
+ */
+ protected ContentHandler createContentHandler(Result result) throws IFException {
+ try {
+ TransformerHandler tHandler = tFactory.newTransformerHandler();
+ Transformer transformer = tHandler.getTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty(OutputKeys.METHOD, "xml");
+ tHandler.setResult(result);
+ return tHandler;
+ } catch (TransformerConfigurationException tce) {
+ throw new IFException(
+ "Error while setting up the serializer for SVG output", tce);
+ }
+ }
+
+ /* ---=== helper methods ===--- */
+
+ private static final String BASE_FORMAT = "0.################";
+
+ private static class DecimalFormatThreadLocal extends ThreadLocal {
+
+ protected synchronized Object initialValue() {
+ DecimalFormat df = new DecimalFormat(BASE_FORMAT, new DecimalFormatSymbols(Locale.US));
+ return df;
+ }
+ };
+
+ //DecimalFormat is not thread-safe!
+ private static final ThreadLocal DECIMAL_FORMAT = new DecimalFormatThreadLocal();
+
+ private static String format(double value) {
+ DecimalFormat df = (DecimalFormat)DECIMAL_FORMAT.get();
+ return df.format(value);
+ }
+
+ /**
+ * Converts an {@code AffineTransform} instance to an SVG style transform method.
+ * @param transform the transformation matrix
+ * @param sb the StringBuffer to write the transform method to
+ * @return the StringBuffer passed to this method
+ */
+ protected StringBuffer toString(AffineTransform transform, StringBuffer sb) {
+ double[] matrix = new double[6];
+ transform.getMatrix(matrix);
+ if (matrix[0] == 1 && matrix[3] == 1 && matrix[1] == 0 && matrix[2] == 0) {
+ sb.append("translate(");
+ sb.append(format(matrix[4]));
+ if (matrix[5] != 0) {
+ sb.append(',').append(format(matrix[5]));
+ }
+ } else {
+ sb.append("matrix(");
+ for (int i = 0; i < 6; i++) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ sb.append(format(matrix[i]));
+ }
+ }
+ sb.append(')');
+ return sb;
+ }
+
+
+ /**
+ * Convenience method to generate a startElement SAX event.
+ * @param localName the local name of the element
+ * @param atts the attributes
+ * @throws SAXException if a SAX exception occurs
+ */
+ protected void startElement(String localName, Attributes atts) throws SAXException {
+ handler.startElement(getMainNamespace(), localName, localName, atts);
+ }
+
+ /**
+ * Convenience method to generate a startElement SAX event.
+ * @param localName the local name of the element
+ * @throws SAXException if a SAX exception occurs
+ */
+ protected void startElement(String localName) throws SAXException {
+ handler.startElement(getMainNamespace(), localName, localName, EMPTY_ATTS);
+ }
+
+ /**
+ * Convenience method to generate a endElement SAX event.
+ * @param localName the local name of the element
+ * @throws SAXException if a SAX exception occurs
+ */
+ protected void endElement(String localName) throws SAXException {
+ handler.endElement(getMainNamespace(), localName, localName);
+ }
+
+ /**
+ * Convenience method to generate an empty element.
+ * @param localName the local name of the element
+ * @param atts the attributes
+ * @throws SAXException if a SAX exception occurs
+ */
+ protected void element(String localName, Attributes atts) throws SAXException {
+ handler.startElement(getMainNamespace(), localName, localName, atts);
+ handler.endElement(getMainNamespace(), localName, localName);
+ }
+
+ /**
+ * Converts an array of integer coordinates into a space-separated string.
+ * @param coordinates the coordinates
+ * @return the space-separated array of coordinates
+ */
+ protected String toString(int[] coordinates) {
+ if (coordinates == null) {
+ return "";
+ }
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0, c = coordinates.length; i < c; i++) {
+ if (i > 0) {
+ sb.append(' ');
+ }
+ sb.append(Integer.toString(coordinates[i]));
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/java/org/apache/fop/render/intermediate/DelegatingFragmentContentHandler.java b/src/java/org/apache/fop/render/intermediate/DelegatingFragmentContentHandler.java
new file mode 100644
index 000000000..cbd6798da
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/DelegatingFragmentContentHandler.java
@@ -0,0 +1,68 @@
+/*
+ * 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.xml.sax.ContentHandler;
+import org.xml.sax.DTDHandler;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.LexicalHandler;
+
+import org.apache.fop.util.DelegatingContentHandler;
+
+/**
+ * This class is a {@code DelegatingContentHandler} subclass which swallows the
+ * {@code #startDocument()} and {@code #endDocument()} methods. This is useful for handling
+ * XML fragments.
+ */
+public class DelegatingFragmentContentHandler extends DelegatingContentHandler {
+
+ /**
+ * Main constructor
+ * @param delegate the content handler to delegate the SAX events to
+ */
+ public DelegatingFragmentContentHandler(ContentHandler delegate) {
+ setDelegateContentHandler(delegate);
+ if (delegate instanceof LexicalHandler) {
+ setDelegateLexicalHandler((LexicalHandler)delegate);
+ }
+ if (delegate instanceof DTDHandler) {
+ setDelegateDTDHandler((DTDHandler)delegate);
+ }
+ if (delegate instanceof EntityResolver) {
+ setDelegateEntityResolver((EntityResolver)delegate);
+ }
+ if (delegate instanceof ErrorHandler) {
+ setDelegateErrorHandler((ErrorHandler)delegate);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startDocument() throws SAXException {
+ //nop/ignore
+ }
+
+ /** {@inheritDoc} */
+ public void endDocument() throws SAXException {
+ //nop/ignore
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/intermediate/IFConstants.java b/src/java/org/apache/fop/render/intermediate/IFConstants.java
new file mode 100644
index 000000000..4bd3a706e
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/IFConstants.java
@@ -0,0 +1,51 @@
+/*
+ * 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.MimeConstants;
+
+/**
+ * Constants for the intermediate format.
+ */
+public interface IFConstants {
+
+ /** MIME type of the intermediate format. */
+ String MIME_TYPE = MimeConstants.MIME_FOP_IF;
+
+ /** XML namespace of the intermediate format. */
+ String NAMESPACE = "http://xmlgraphics.apache.org/fop/intermediate";
+
+ /** XML namespace. */
+ String XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace";
+
+ /** Namespace prefix for XLink */
+ String XLINK_PREFIX = "xlink";
+ /** XML namespace for XLink */
+ String XLINK_NAMESPACE = "http://www.w3.org/1999/xlink";
+
+ String EL_DOCUMENT = "document";
+ String EL_HEADER = "header";
+ String EL_PAGE_SEQUENCE = "page-sequence";
+ String EL_PAGE = "page";
+ String EL_PAGE_HEADER = "page-header";
+ String EL_PAGE_TRAILER = "page-trailer";
+ String EL_PAGE_CONTENT = "content";
+ String EL_BOX = "box";
+}
diff --git a/src/java/org/apache/fop/render/intermediate/IFContentHandler.java b/src/java/org/apache/fop/render/intermediate/IFContentHandler.java
new file mode 100644
index 000000000..55c65d82a
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/IFContentHandler.java
@@ -0,0 +1,89 @@
+/*
+ * 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.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+public class IFContentHandler implements ContentHandler {
+
+ public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void endDocument() throws SAXException {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void endElement(String arg0, String arg1, String arg2)
+ throws SAXException {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void endPrefixMapping(String arg0) throws SAXException {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void ignorableWhitespace(char[] arg0, int arg1, int arg2)
+ throws SAXException {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void processingInstruction(String arg0, String arg1)
+ throws SAXException {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setDocumentLocator(Locator arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void skippedEntity(String arg0) throws SAXException {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void startDocument() throws SAXException {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void startElement(String arg0, String arg1, String arg2,
+ Attributes arg3) throws SAXException {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void startPrefixMapping(String arg0, String arg1)
+ throws SAXException {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/intermediate/IFException.java b/src/java/org/apache/fop/render/intermediate/IFException.java
new file mode 100644
index 000000000..c3f17f9ca
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/IFException.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+/**
+ * Exception thrown by code dealing with FOP's intermediate format.
+ */
+public class IFException extends Exception {
+
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * Constructs a new exception with the specified detail message and
+ * cause. <p>Note that the detail message associated with
+ * <code>cause</code> is <i>not</i> automatically incorporated in
+ * this exception's detail message.
+ *
+ * @param message the detail message (which is saved for later retrieval
+ * by the {@link #getMessage()} method).
+ * @param cause the cause (which is saved for later retrieval by the
+ * {@link #getCause()} method). (A <tt>null</tt> value is
+ * permitted, and indicates that the cause is nonexistent or
+ * unknown.)
+ */
+ public IFException(String message, Exception cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/intermediate/IFPainter.java b/src/java/org/apache/fop/render/intermediate/IFPainter.java
new file mode 100644
index 000000000..83045b6bc
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/IFPainter.java
@@ -0,0 +1,254 @@
+/*
+ * 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.Paint;
+import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
+
+import javax.xml.transform.Result;
+
+import org.apache.fop.apps.FOUserAgent;
+
+/**
+ * Interface used to paint whole documents layouted by Apache FOP.
+ * <p>
+ * Call sequence:
+ * <p>
+ * <pre>
+ * startDocument()
+ * startDocumentHeader()
+ * [handleExtension()]*
+ * endDocumentHeader()
+ * [
+ * startPageSequence()
+ * [
+ * startPage()
+ * startPageHeader()
+ * [handleExtension()]*
+ * endPageHeader()
+ * startPageContent()
+ * (#pageContent)+
+ * endPageContent()
+ * startPageTrailer()
+ * (addTarget())*
+ * endPageTrailer()
+ * endPage()
+ * ]*
+ * endPageSequence()
+ * ]*
+ * endDocument()
+ *
+ * #box:
+ * startBox()
+ * (#pageContent)+
+ * endBox()
+ *
+ * #pageContent:
+ * (
+ * setFont() |
+ * drawText() |
+ * drawRect() |
+ * drawImage() |
+ * TODO etc. etc. |
+ * handleExtensionObject()
+ * )
+ * </pre>
+ */
+public interface IFPainter {
+
+ /**
+ * Set the user agent.
+ * @param userAgent The user agent
+ */
+ void setUserAgent(FOUserAgent userAgent);
+
+ /**
+ * Sets the JAXP Result object to receive the generated content.
+ * @param result the JAXP Result object to receive the generated content
+ * @throws IFException if an error occurs setting up the output
+ */
+ void setResult(Result result) throws IFException;
+
+ /**
+ * Indicates whether the painter supports to handle the pages in mixed order rather than
+ * ascending order.
+ * @return true if out-of-order handling is supported
+ */
+ boolean supportsPagesOutOfOrder();
+
+ /**
+ * 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
+ */
+ void startDocument() throws IFException;
+
+ /**
+ * Indicates the end of a document. This method may only be called once after the whole
+ * document has been handled. Implementations can release resources (close streams). It is
+ * an error to call any event method after this method.
+ * @throws IFException if an error occurs while handling this event
+ */
+ void endDocument() throws IFException;
+
+ /**
+ * Indicates the start of the document header. This method is called right after the
+ * {@code #startDocument()} method. Extensions sent to this painter between
+ * {@code #startDocumentHeader()} and {@code #endDocumentHeader()} apply to the document as
+ * a whole (like document metadata).
+ * @throws IFException if an error occurs while handling this event
+ */
+ void startDocumentHeader() throws IFException;
+
+ /**
+ * Indicates the end of the document header. This method is called before the first
+ * page sequence.
+ * @throws IFException if an error occurs while handling this event
+ */
+ void endDocumentHeader() throws IFException;
+
+ /**
+ * Indicates the start of a new page sequence.
+ * @param id the page sequence's identifier (or null if none is available)
+ * @throws IFException if an error occurs while handling this event
+ */
+ void startPageSequence(String id) throws IFException;
+ /**
+ * Indicates the end of a page sequence.
+ * @throws IFException if an error occurs while handling this event
+ */
+ void endPageSequence() throws IFException;
+
+ /**
+ * Indicates the start of a new page.
+ * @param index the index of the page within the document (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
+ */
+ void startPage(int index, String name, Dimension size) throws IFException;
+
+ /**
+ * Indicates the end of a page
+ * @throws IFException if an error occurs while handling this event
+ */
+ void endPage() throws IFException;
+
+ /**
+ * Indicates the start of the page header.
+ * @throws IFException if an error occurs while handling this event
+ */
+ void startPageHeader() throws IFException;
+
+ /**
+ * Indicates the end of the page header.
+ * @throws IFException if an error occurs while handling this event
+ */
+ void endPageHeader() throws IFException;
+
+ /**
+ * Indicates the start of the page content.
+ * @throws IFException if an error occurs while handling this event
+ */
+ void startPageContent() throws IFException;
+
+ /**
+ * Indicates the end of the page content.
+ * @throws IFException if an error occurs while handling this event
+ */
+ void endPageContent() throws IFException;
+
+ /**
+ * Indicates the start of the page trailer. The page trailer is used for writing down page
+ * elements which are only know after handling the page itself (like PDF targets).
+ * @throws IFException if an error occurs while handling this event
+ */
+ void startPageTrailer() throws IFException;
+
+ /**
+ * @todo Solve with extension because not all formats support that?
+ */
+ void addTarget(String name, int x, int y) throws IFException;
+
+ /**
+ * Indicates the end of the page trailer.
+ * @throws IFException if an error occurs while handling this event
+ */
+ void endPageTrailer() throws IFException;
+
+ void startBox(AffineTransform transform, Dimension size, boolean clip) throws IFException;
+ void startBox(AffineTransform[] transforms, Dimension size, boolean clip) throws IFException;
+ //For transform, Batik's org.apache.batik.parser.TransformListHandler/Parser can be used
+ void endBox() throws IFException;
+
+ /**
+ * Updates the current font.
+ * @param family the font family (or null if there's no change)
+ * @param style the font style (or null if there's no change)
+ * @param weight the font weight (or null if there's no change)
+ * @param variant the font variant (or null if there's no change)
+ * @param size the font size (or null if there's no change)
+ * @param color the text color (or null if there's no change)
+ * @throws IFException if an error occurs while handling this event
+ */
+ void setFont(String family, String style, Integer weight, String variant, Integer size,
+ Color color) throws IFException;
+
+ /**
+ * Draws text. The initial coordinates (x and y) point to the starting point at the normal
+ * baseline of the font. The arrays (dx and dy) are optional and can be used to achieve
+ * effects like kerning.
+ * @param x X-coordinate of the starting point of the text
+ * @param y Y-coordinate of the starting point of the text
+ * @param dx an array of adjustment values for each character in X-direction
+ * @param dy an array of adjustment values for each character in Y-direction
+ * @param text the text
+ * @throws IFException if an error occurs while handling this event
+ */
+ void drawText(int x, int y, int[] dx, int[] dy, String text) throws IFException;
+
+ /**
+ * Draws a rectangle. Either fill or stroke has to be specified.
+ * @param rect the rectangle's coordinates and extent
+ * @param fill the fill paint (may be null)
+ * @param stroke the stroke color (may be null)
+ * @throws IFException if an error occurs while handling this event
+ */
+ void drawRect(Rectangle rect, Paint fill, Color stroke) throws IFException;
+ void drawImage(String uri, Rectangle rect) throws IFException; //external images
+ void startImage(Rectangle rect) throws IFException; //followed by a SAX stream (SVG etc.)
+ void endImage() throws IFException;
+ //etc. etc.
+
+ /**
+ * Handles an extension object. This can be a DOM document or any arbitrary
+ * object. If an implementation doesn't know how to handle a particular extension it is simply
+ * ignored.
+ * @param extension the extension object
+ * @throws IFException if an error occurs while handling this event
+ */
+ void handleExtensionObject(Object extension) throws IFException;
+
+ //TODO Prototype the following:
+ //ContentHandler handleExtension() throws Exception
+}
diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
new file mode 100644
index 000000000..83e7b5397
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
@@ -0,0 +1,565 @@
+/*
+ * 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.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import javax.xml.transform.stream.StreamResult;
+
+import org.xml.sax.SAXException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.java2d.GraphicContext;
+import org.apache.xmlgraphics.xmp.Metadata;
+import org.apache.xmlgraphics.xmp.schemas.DublinCoreAdapter;
+import org.apache.xmlgraphics.xmp.schemas.DublinCoreSchema;
+import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter;
+import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema;
+
+import org.apache.fop.Version;
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.area.Block;
+import org.apache.fop.area.CTM;
+import org.apache.fop.area.OffDocumentExtensionAttachment;
+import org.apache.fop.area.OffDocumentItem;
+import org.apache.fop.area.PageSequence;
+import org.apache.fop.area.PageViewport;
+import org.apache.fop.area.Trait;
+import org.apache.fop.area.inline.AbstractTextArea;
+import org.apache.fop.area.inline.Image;
+import org.apache.fop.area.inline.SpaceArea;
+import org.apache.fop.area.inline.TextArea;
+import org.apache.fop.area.inline.WordArea;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+import org.apache.fop.fo.extensions.xmp.XMPMetadata;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontTriplet;
+import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.fonts.Typeface;
+import org.apache.fop.render.AbstractPathOrientedRenderer;
+import org.apache.fop.render.Renderer;
+
+public class IFRenderer extends AbstractPathOrientedRenderer {
+
+ /** logging instance */
+ protected static Log log = LogFactory.getLog(IFRenderer.class);
+
+ /** XML MIME type */
+ public static final String IF_MIME_TYPE = MimeConstants.MIME_FOP_IF;
+
+ private IFPainter painter;
+
+ /** If not null, the XMLRenderer will mimic another renderer by using its font setup. */
+ protected Renderer mimic;
+
+ private boolean inPageSequence = false;
+
+ private Stack graphicContextStack = new Stack();
+ private GraphicContext graphicContext = new GraphicContext();
+
+ private Metadata documentMetadata;
+
+ /**
+ * Main constructor
+ */
+ public IFRenderer() {
+ }
+
+ /** {@inheritDoc} */
+ public String getMimeType() {
+ return IF_MIME_TYPE;
+ }
+
+ /**
+ * Sets the {@code IFPainter} to be used by the {@code IFRenderer}.
+ * @param painter the {@code IFPainter}
+ */
+ public void setPainter(IFPainter painter) {
+ this.painter = painter;
+ }
+
+ /**
+ * Call this method to make the XMLRenderer mimic a different renderer by using its font
+ * setup. This is useful when working with the intermediate format parser.
+ * @param renderer the renderer to mimic
+ */
+ public void mimicRenderer(Renderer renderer) {
+ this.mimic = renderer;
+ }
+
+ /** {@inheritDoc} */
+ public void setupFontInfo(FontInfo inFontInfo) {
+ if (mimic != null) {
+ mimic.setupFontInfo(inFontInfo);
+ } else {
+ super.setupFontInfo(inFontInfo);
+ }
+ }
+
+ private void handleIFException(IFException ife) {
+ if (ife.getCause() instanceof SAXException) {
+ throw new RuntimeException(ife.getCause());
+ } else {
+ throw new RuntimeException(ife);
+ }
+ }
+
+ private void handleIFExceptionWithIOException(IFException ife) throws IOException {
+ if (ife.getCause() instanceof IOException) {
+ throw (IOException)ife.getCause();
+ } else {
+ handleIFException(ife);
+ }
+ }
+
+ /**
+ * Creates a default {@code IFPainter} when none has been set.
+ * @return the default IFPainter
+ */
+ protected IFPainter createDefaultPainter() {
+ return new IFSerializer();
+ }
+
+ /** {@inheritDoc} */
+ public void startRenderer(OutputStream outputStream)
+ throws IOException {
+ try {
+ if (outputStream != null) {
+ StreamResult result = new StreamResult(outputStream);
+ if (getUserAgent().getOutputFile() != null) {
+ result.setSystemId(
+ getUserAgent().getOutputFile().toURI().toURL().toExternalForm());
+ }
+ if (this.painter == null) {
+ this.painter = new IFSerializer();
+ }
+ this.painter.setUserAgent(getUserAgent());
+ this.painter.setResult(result);
+ }
+ super.startRenderer(null);
+ if (log.isDebugEnabled()) {
+ log.debug("Rendering areas via painter ("
+ + this.painter.getClass().getName() + ")...");
+ }
+ painter.startDocument();
+ painter.startDocumentHeader();
+ } catch (IFException e) {
+ handleIFExceptionWithIOException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void stopRenderer() throws IOException {
+ try {
+ if (this.inPageSequence) {
+ painter.endPageSequence();
+ this.inPageSequence = false;
+ }
+ painter.endDocument();
+ } catch (IFException e) {
+ handleIFExceptionWithIOException(e);
+ }
+ super.stopRenderer();
+ log.debug("Rendering finished.");
+ }
+
+ /** {@inheritDoc} */
+ public void processOffDocumentItem(OffDocumentItem odi) {
+ if (odi instanceof OffDocumentExtensionAttachment) {
+ ExtensionAttachment attachment = ((OffDocumentExtensionAttachment)odi).getAttachment();
+ if (XMPMetadata.CATEGORY.equals(attachment.getCategory())) {
+ renderXMPMetadata((XMPMetadata)attachment);
+ }
+ }
+ }
+
+ private void renderXMPMetadata(XMPMetadata metadata) {
+ this.documentMetadata = metadata.getMetadata();
+ }
+
+ /** {@inheritDoc} */
+ public void startPageSequence(PageSequence pageSequence) {
+ try {
+ if (this.inPageSequence) {
+ painter.endPageSequence();
+ } else {
+ if (this.documentMetadata == null) {
+ this.documentMetadata = createDefaultDocumentMetadata();
+ }
+ painter.handleExtensionObject(this.documentMetadata);
+ painter.endDocumentHeader();
+ this.inPageSequence = true;
+ }
+ //TODO Put the page-sequence's ID in the area tree
+ painter.startPageSequence(null);
+ } catch (IFException e) {
+ handleIFException(e);
+ }
+ }
+
+ private Metadata createDefaultDocumentMetadata() {
+ Metadata xmp = new Metadata();
+ DublinCoreAdapter dc = DublinCoreSchema.getAdapter(xmp);
+ if (getUserAgent().getTitle() != null) {
+ dc.setTitle(getUserAgent().getTitle());
+ }
+ if (getUserAgent().getAuthor() != null) {
+ dc.addCreator(getUserAgent().getAuthor());
+ }
+ if (getUserAgent().getKeywords() != null) {
+ dc.addSubject(getUserAgent().getKeywords());
+ }
+ XMPBasicAdapter xmpBasic = XMPBasicSchema.getAdapter(xmp);
+ if (getUserAgent().getProducer() != null) {
+ xmpBasic.setCreatorTool(getUserAgent().getProducer());
+ } else {
+ xmpBasic.setCreatorTool(Version.getVersion());
+ }
+ xmpBasic.setMetadataDate(new java.util.Date());
+ if (getUserAgent().getCreationDate() != null) {
+ xmpBasic.setCreateDate(getUserAgent().getCreationDate());
+ } else {
+ xmpBasic.setCreateDate(xmpBasic.getMetadataDate());
+ }
+ return xmp;
+ }
+
+ /** {@inheritDoc} */
+ public void renderPage(PageViewport page) throws IOException, FOPException {
+ if (log.isDebugEnabled()) {
+ log.debug("renderPage() " + page);
+ }
+ try {
+ Rectangle2D viewArea = page.getViewArea();
+ Dimension dim = new Dimension(
+ (int)Math.ceil(viewArea.getWidth()),
+ (int)Math.ceil(viewArea.getHeight()));
+ painter.startPage(page.getPageIndex(), page.getPageNumberString(), dim);
+ painter.startPageHeader();
+ //TODO Handle page header
+ painter.endPageHeader();
+ painter.startPageContent();
+ super.renderPage(page);
+ painter.endPageContent();
+ painter.startPageTrailer();
+ //TODO Handle page trailer
+ painter.endPageTrailer();
+ painter.endPage();
+ } catch (IFException e) {
+ handleIFException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected void saveGraphicsState() {
+ graphicContextStack.push(graphicContext);
+ graphicContext = (GraphicContext)graphicContext.clone();
+ }
+
+ /** {@inheritDoc} */
+ protected void restoreGraphicsState() {
+ graphicContext = (GraphicContext)graphicContextStack.pop();
+ }
+
+ /** {@inheritDoc} */
+ protected List breakOutOfStateStack() {
+ log.debug("Block.FIXED --> break out");
+ List breakOutList = new java.util.ArrayList();
+ while (!this.graphicContextStack.empty()) {
+ breakOutList.add(0, this.graphicContext);
+ restoreGraphicsState();
+ }
+ return breakOutList;
+ }
+
+ /** {@inheritDoc} */
+ protected void restoreStateStackAfterBreakOut(List breakOutList) {
+ log.debug("Block.FIXED --> restoring context after break-out");
+ for (int i = 0, c = breakOutList.size(); i < c; i++) {
+ saveGraphicsState();
+ this.graphicContext = (GraphicContext)breakOutList.get(i);
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected void concatenateTransformationMatrix(AffineTransform at) {
+ if (!at.isIdentity()) {
+ graphicContext.transform(ptToMpt(at));
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected void beginTextObject() {
+ // TODO Auto-generated method stub
+
+ }
+
+ /** {@inheritDoc} */
+ protected void endTextObject() {
+ // TODO Auto-generated method stub
+
+ }
+
+ /** {@inheritDoc} */
+ protected void startVParea(CTM ctm, Rectangle2D clippingRect) {
+ if (log.isDebugEnabled()) {
+ log.debug("startVParea() ctm=" + ctm + ", rect=" + clippingRect);
+ }
+ saveGraphicsState();
+ AffineTransform at = new AffineTransform(ctm.toArray());
+ graphicContext.transform(at);
+ try {
+ painter.startBox(at, null, false);
+ } catch (IFException e) {
+ handleIFException(e);
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("startVPArea: " + at + " --> " + graphicContext.getTransform());
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected void endVParea() {
+ log.debug("endVParea()");
+ try {
+ painter.endBox();
+ } catch (IFException e) {
+ handleIFException(e);
+ }
+ restoreGraphicsState();
+ if (log.isDebugEnabled()) {
+ log.debug("endVPArea() --> " + graphicContext.getTransform());
+ }
+ }
+
+ protected void renderReferenceArea(Block block) {
+ // TODO Auto-generated method stub
+ }
+
+ /** {@inheritDoc} */
+ protected void renderBlock(Block block) {
+ if (log.isDebugEnabled()) {
+ log.debug("renderBlock() " + block);
+ }
+ super.renderBlock(block);
+ }
+
+ private Typeface getTypeface(String fontName) {
+ Typeface tf = (Typeface) fontInfo.getFonts().get(fontName);
+ if (tf instanceof LazyFont) {
+ tf = ((LazyFont)tf).getRealFont();
+ }
+ return tf;
+ }
+
+ /** {@inheritDoc} */
+ protected void renderText(TextArea text) {
+ if (log.isDebugEnabled()) {
+ log.debug("renderText() " + text);
+ }
+ renderInlineAreaBackAndBorders(text);
+ Color ct = (Color) text.getTrait(Trait.COLOR);
+
+ 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);
+
+ FontTriplet triplet = (FontTriplet)text.getTrait(Trait.FONT);
+ try {
+ painter.setFont(triplet.getName(), triplet.getStyle(), new Integer(triplet.getWeight()),
+ "normal", new Integer(size), ct);
+ } catch (IFException e) {
+ handleIFException(e);
+ }
+
+ super.renderText(text);
+
+ int rx = currentIPPosition + text.getBorderAndPaddingWidthStart();
+ int bl = currentBPPosition + text.getOffset() + text.getBaselineOffset();
+ renderTextDecoration(tf, size, text, bl, rx);
+ }
+
+ /** {@inheritDoc} */
+ protected void renderWord(WordArea word) {
+ Font font = getFontFromArea(word.getParentArea());
+ String s = word.getWord();
+
+ renderText(s, word.getLetterAdjustArray(),
+ font, (AbstractTextArea)word.getParentArea());
+
+ super.renderWord(word);
+ }
+
+ /** {@inheritDoc} */
+ protected void renderSpace(SpaceArea space) {
+ Font font = getFontFromArea(space.getParentArea());
+ String s = space.getSpace();
+
+ AbstractTextArea textArea = (AbstractTextArea)space.getParentArea();
+ renderText(s, null, font, textArea);
+
+ if (space.isAdjustable()) {
+ //Used for justified text, for example
+ int tws = -((TextArea) space.getParentArea()).getTextWordSpaceAdjust()
+ - 2 * textArea.getTextLetterSpaceAdjust();
+ this.currentIPPosition -= tws;
+ }
+ super.renderSpace(space);
+ }
+
+ /**
+ * Does low-level rendering of text.
+ * @param s text to render
+ * @param letterAdjust an array of widths for letter adjustment (may be null)
+ * @param font to font in use
+ * @param parentArea the parent text area to retrieve certain traits from
+ */
+ protected void renderText(String s,
+ int[] letterAdjust,
+ Font font, AbstractTextArea parentArea) {
+ int curX = currentIPPosition;
+ float fontSize = font.getFontSize() / 1000f;
+
+ int l = s.length();
+
+ int[] dx = new int[l];
+ boolean hasDX = false;
+ for (int i = 0; i < l; i++) {
+ char ch = s.charAt(i);
+ float glyphAdjust = 0;
+ if (font.hasChar(ch)) {
+ int tls = (i < l - 1 ? parentArea.getTextLetterSpaceAdjust() : 0);
+ glyphAdjust -= tls;
+ }
+ curX += font.getCharWidth(ch);
+ if (letterAdjust != null && i < l - 1) {
+ glyphAdjust -= letterAdjust[i + 1];
+ }
+
+ float adjust = glyphAdjust / fontSize;
+
+ if (adjust != 0) {
+ dx[i] = Math.round(adjust);
+ if (dx[i] != 0) {
+ hasDX = true;
+ }
+ }
+ curX += adjust;
+ }
+ try {
+ int rx = currentIPPosition + parentArea.getBorderAndPaddingWidthStart();
+ int bl = currentBPPosition + parentArea.getOffset() + parentArea.getBaselineOffset();
+ painter.drawText(rx, bl, (hasDX ? dx : null), null, s);
+ } catch (IFException e) {
+ handleIFException(e);
+ }
+ this.currentIPPosition = curX;
+ }
+
+ /** {@inheritDoc} */
+ public void renderImage(Image image, Rectangle2D pos) {
+ if (log.isDebugEnabled()) {
+ log.debug("renderImage() image=" + image + ", pos=" + pos);
+ }
+ super.renderImage(image, pos);
+ }
+
+ protected void drawImage(String url, Rectangle2D pos, Map foreignAttributes) {
+ // TODO Auto-generated method stub
+
+ }
+
+ protected void clip() {
+ // TODO Auto-generated method stub
+
+ }
+
+ protected void clipRect(float x, float y, float width, float height) {
+ // TODO Auto-generated method stub
+
+ }
+
+ protected void closePath() {
+ // TODO Auto-generated method stub
+
+ }
+
+ protected void drawBorderLine(float x1, float y1, float x2, float y2, boolean horz,
+ boolean startOrBefore, int style, Color col) {
+ // TODO Auto-generated method stub
+
+ }
+
+ private Rectangle toMillipointRectangle(float x, float y, float width, float height) {
+ return new Rectangle(
+ (int)(x * 1000), (int)(y * 1000), (int)(width * 1000), (int)(height * 1000));
+ }
+
+ /** {@inheritDoc} */
+ protected void fillRect(float x, float y, float width, float height) {
+ try {
+ painter.drawRect(
+ toMillipointRectangle(x, y, width, height),
+ this.graphicContext.getPaint(), null);
+ } catch (IFException e) {
+ handleIFException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected void moveTo(float x, float y) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /** {@inheritDoc} */
+ protected void lineTo(float x, float y) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /** {@inheritDoc} */
+ protected void updateColor(Color col, boolean fill) {
+ if (fill) {
+ this.graphicContext.setPaint(col);
+ } else {
+ this.graphicContext.setColor(col);
+ }
+
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/intermediate/IFRendererMaker.java b/src/java/org/apache/fop/render/intermediate/IFRendererMaker.java
new file mode 100644
index 000000000..eb70f3028
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/IFRendererMaker.java
@@ -0,0 +1,56 @@
+/*
+ * 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.FOUserAgent;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.render.AbstractRendererMaker;
+import org.apache.fop.render.PrintRendererConfigurator;
+import org.apache.fop.render.Renderer;
+import org.apache.fop.render.RendererConfigurator;
+
+/**
+ * RendererMaker for the Intermediate Format Renderer.
+ */
+public class IFRendererMaker extends AbstractRendererMaker {
+
+ private static final String[] MIMES = new String[] {MimeConstants.MIME_FOP_IF};
+
+ /**{@inheritDoc} */
+ public Renderer makeRenderer(FOUserAgent userAgent) {
+ return new IFRenderer();
+ }
+
+ /**{@inheritDoc} */
+ public RendererConfigurator getConfigurator(FOUserAgent userAgent) {
+ return new PrintRendererConfigurator(userAgent);
+ }
+
+ /** {@inheritDoc} */
+ public boolean needsOutputStream() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public String[] getSupportedMimeTypes() {
+ return MIMES;
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
new file mode 100644
index 000000000..41cecd1e7
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
@@ -0,0 +1,363 @@
+/*
+ * 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.Paint;
+import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.apache.xmlgraphics.util.XMLizable;
+
+import org.apache.fop.util.ColorUtil;
+
+/**
+ * IFPainter implementation that serializes the intermediate format to XML.
+ */
+public class IFSerializer extends AbstractXMLWritingIFPainter implements IFConstants {
+
+ /**
+ * Default constructor.
+ */
+ public IFSerializer() {
+ }
+
+ /** {@inheritDoc} */
+ protected String getMainNamespace() {
+ return NAMESPACE;
+ }
+
+ /** {@inheritDoc} */
+ public boolean supportsPagesOutOfOrder() {
+ return false;
+ //Theoretically supported but disabled to improve performance when
+ //rendering the IF to the final format later on
+ }
+
+ /** {@inheritDoc} */
+ public void startDocument() throws IFException {
+ try {
+ handler.startDocument();
+ handler.startPrefixMapping("", NAMESPACE);
+ handler.startPrefixMapping(XLINK_PREFIX, XLINK_NAMESPACE);
+ startElement(EL_DOCUMENT);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startDocument()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startDocumentHeader() throws IFException {
+ try {
+ startElement(EL_HEADER);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startDocumentHeader()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endDocumentHeader() throws IFException {
+ try {
+ endElement(EL_HEADER);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startDocumentHeader()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endDocument() throws IFException {
+ try {
+ endElement(EL_DOCUMENT);
+ handler.endDocument();
+ } catch (SAXException e) {
+ throw new IFException("SAX error in endDocument()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startPageSequence(String id) throws IFException {
+ try {
+ AttributesImpl atts = new AttributesImpl();
+ if (id != null) {
+ atts.addAttribute(XML_NAMESPACE, "id", "xml:id", CDATA, id);
+ }
+ startElement(EL_PAGE_SEQUENCE, atts);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startPageSequence()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endPageSequence() throws IFException {
+ try {
+ endElement(EL_PAGE_SEQUENCE);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in endPageSequence()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startPage(int index, String name, Dimension size) throws IFException {
+ try {
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute("", "index", "index", CDATA, Integer.toString(index));
+ atts.addAttribute("", "name", "name", CDATA, name);
+ atts.addAttribute("", "width", "width", CDATA, Integer.toString(size.width));
+ atts.addAttribute("", "height", "height", CDATA, Integer.toString(size.height));
+ startElement(EL_PAGE, atts);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startPage()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startPageHeader() throws IFException {
+ try {
+ startElement(EL_PAGE_HEADER);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startPageHeader()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endPageHeader() throws IFException {
+ try {
+ endElement(EL_PAGE_HEADER);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in endPageHeader()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startPageContent() throws IFException {
+ try {
+ startElement(EL_PAGE_CONTENT);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startPageContent()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endPageContent() throws IFException {
+ try {
+ endElement(EL_PAGE_CONTENT);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in endPageContent()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startPageTrailer() throws IFException {
+ try {
+ startElement(EL_PAGE_TRAILER);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startPageTrailer()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endPageTrailer() throws IFException {
+ try {
+ endElement(EL_PAGE_TRAILER);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in endPageTrailer()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endPage() throws IFException {
+ try {
+ endElement(EL_PAGE);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in endPage()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startBox(AffineTransform transform, Dimension size, boolean clip)
+ throws IFException {
+ StringBuffer sb = new StringBuffer();
+ toString(transform, sb);
+ startBox(sb.toString(), size, clip);
+ }
+
+ /** {@inheritDoc} */
+ public void startBox(AffineTransform[] transforms, Dimension size, boolean clip)
+ throws IFException {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0, c = transforms.length; i < c; i++) {
+ if (i > 0) {
+ sb.append(' ');
+ }
+ toString(transforms[i], sb);
+ }
+ startBox(sb.toString(), size, clip);
+ }
+
+ private void startBox(String transform, Dimension size, boolean clip) throws IFException {
+ try {
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute("", "transform", "transform", CDATA, transform);
+ if (size != null) {
+ atts.addAttribute("", "width", "width", CDATA, Integer.toString(size.width));
+ atts.addAttribute("", "height", "height", CDATA, Integer.toString(size.height));
+ }
+ if (clip) {
+ atts.addAttribute("", "clip", "clip", CDATA, "true");
+ }
+ startElement(EL_BOX, atts);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startBox()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endBox() throws IFException {
+ try {
+ endElement(EL_BOX);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in endBox()", e);
+ }
+ }
+
+ /** {@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);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void drawRect(Rectangle rect, Paint fill, Color stroke) throws IFException {
+ if (fill == null && stroke == null) {
+ return;
+ }
+ try {
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute("", "x", "x", CDATA, Integer.toString(rect.x));
+ atts.addAttribute("", "y", "y", CDATA, Integer.toString(rect.y));
+ atts.addAttribute("", "width", "width", CDATA, Integer.toString(rect.width));
+ atts.addAttribute("", "height", "height", CDATA, Integer.toString(rect.height));
+ if (fill != null) {
+ atts.addAttribute("", "fill", "fill", CDATA, toString(fill));
+ }
+ if (stroke != null) {
+ atts.addAttribute("", "stroke", "sroke", CDATA, toString(stroke));
+ }
+ element("rect", atts);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in drawRect()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void drawText(int x, int y, int[] dx, int[] dy, String text) throws IFException {
+ try {
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute("", "x", "x", CDATA, Integer.toString(x));
+ atts.addAttribute("", "y", "y", CDATA, Integer.toString(y));
+ if (dx != null) {
+ atts.addAttribute("", "dx", "dx", CDATA, toString(dx));
+ }
+ if (dy != null) {
+ atts.addAttribute("", "dy", "dy", CDATA, toString(dy));
+ }
+ startElement("text", atts);
+ char[] chars = text.toCharArray();
+ handler.characters(chars, 0, chars.length);
+ endElement("text");
+ } catch (SAXException e) {
+ throw new IFException("SAX error in setFont()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void setFont(String family, String style, Integer weight, String variant, Integer size,
+ Color color) throws IFException {
+ try {
+ AttributesImpl atts = new AttributesImpl();
+ if (family != null) {
+ atts.addAttribute("", "family", "family", CDATA, family);
+ }
+ if (style != null) {
+ atts.addAttribute("", "style", "style", CDATA, style);
+ }
+ if (weight != null) {
+ atts.addAttribute("", "weight", "weight", CDATA, weight.toString());
+ }
+ if (variant != null) {
+ atts.addAttribute("", "variant", "variant", CDATA, variant);
+ }
+ if (size != null) {
+ atts.addAttribute("", "size", "size", CDATA, size.toString());
+ }
+ if (color != null) {
+ atts.addAttribute("", "color", "color", CDATA, toString(color));
+ }
+ element("font", atts);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in setFont()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void handleExtensionObject(Object extension) throws IFException {
+ if (extension instanceof XMLizable) {
+ try {
+ ((XMLizable)extension).toSAX(this.handler);
+ } catch (SAXException e) {
+ throw new IFException("SAX error while handling extension object", e);
+ }
+ } else {
+ throw new UnsupportedOperationException(
+ "Don't know how to handle extension object: " + extension);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/intermediate/IFState.java b/src/java/org/apache/fop/render/intermediate/IFState.java
new file mode 100644
index 000000000..aa073d03c
--- /dev/null
+++ b/src/java/org/apache/fop/render/intermediate/IFState.java
@@ -0,0 +1,188 @@
+/*
+ * 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;
+
+public class IFState {
+
+ private IFState parent;
+
+ private String fontFamily;
+ private int fontSize;
+ private String fontStyle;
+ private int fontWeight;
+ private String fontVariant;
+ private boolean fontChanged = true;
+
+ private Color textColor;
+
+ private IFState() {
+ //nop
+ }
+
+ private IFState(IFState parent) {
+ this.parent = parent;
+
+ this.fontFamily = parent.fontFamily;
+ this.fontSize = parent.fontSize;
+ this.fontStyle = parent.fontStyle;
+ this.fontWeight = parent.fontWeight;
+ this.fontVariant = parent.fontVariant;
+
+ this.textColor = parent.textColor;
+ }
+
+ public static IFState create() {
+ return new IFState();
+ }
+
+ public IFState push() {
+ return new IFState(this);
+ }
+
+ public IFState pop() {
+ return this.parent;
+ }
+
+ public boolean isFontChanged() {
+ return this.fontChanged;
+ }
+
+ public void resetFontChanged() {
+ this.fontChanged = false;
+ }
+
+ /**
+ * Returns the font family.
+ * @return the font family
+ */
+ public String getFontFamily() {
+ return fontFamily;
+ }
+
+ /**
+ * Sets the font family.
+ * @param family the new font family
+ */
+ public void setFontFamily(String family) {
+ if (!family.equals(this.fontFamily)) {
+ this.fontChanged = true;
+ }
+ this.fontFamily = family;
+ }
+
+ /**
+ * Returns the font size.
+ * @return the font size (in mpt)
+ */
+ public int getFontSize() {
+ return fontSize;
+ }
+
+ /**
+ * Sets the font size.
+ * @param size the new font size (in mpt)
+ */
+ public void setFontSize(int size) {
+ if (size != this.fontSize) {
+ this.fontChanged = true;
+ }
+ this.fontSize = size;
+ }
+
+ /**
+ * Returns the font style.
+ * @return the font style
+ */
+ public String getFontStyle() {
+ return fontStyle;
+ }
+
+ /**
+ * Set the font style
+ * @param style the new font style
+ */
+ public void setFontStyle(String style) {
+ if (!style.equals(this.fontStyle)) {
+ this.fontChanged = true;
+ }
+ this.fontStyle = style;
+ }
+
+ /**
+ * Returns the font weight.
+ * @return the font weight
+ */
+ public int getFontWeight() {
+ return fontWeight;
+ }
+
+ /**
+ * Sets the font weight
+ * @param weight the new font weight
+ */
+ public void setFontWeight(int weight) {
+ if (weight != this.fontWeight) {
+ this.fontChanged = true;
+ }
+ this.fontWeight = weight;
+ }
+
+ /**
+ * Returns the font variant.
+ * @return the font variant
+ */
+ public String getFontVariant() {
+ return fontVariant;
+ }
+
+ /**
+ * Sets the font variant.
+ * @param variant the new font variant
+ */
+ public void setFontVariant(String variant) {
+ if (!variant.equals(this.fontVariant)) {
+ this.fontChanged = true;
+ }
+ this.fontVariant = variant;
+ }
+
+ /**
+ * Returns the text color.
+ * @return the text color
+ */
+ public Color getTextColor() {
+ return textColor;
+ }
+
+ /**
+ * Sets the text color.
+ * @param color the new text color
+ */
+ public void setTextColor(Color color) {
+ if (!color.equals(this.textColor)) {
+ this.fontChanged = true;
+ }
+ this.textColor = color;
+ }
+
+
+}
diff --git a/src/java/org/apache/fop/util/DelegatingContentHandler.java b/src/java/org/apache/fop/util/DelegatingContentHandler.java
index 0b371483f..ff712a82b 100644
--- a/src/java/org/apache/fop/util/DelegatingContentHandler.java
+++ b/src/java/org/apache/fop/util/DelegatingContentHandler.java
@@ -38,9 +38,8 @@ import org.xml.sax.ext.LexicalHandler;
* <p>
* The ContentHandler is the only instance that is required. All others (DTDHandler,
* EntityResolver, LexicalHandler and ErrorHandler) may be ignored.
- *
*/
-public class DelegatingContentHandler
+public class DelegatingContentHandler
implements EntityResolver, DTDHandler, ContentHandler, LexicalHandler, ErrorHandler {
private ContentHandler delegate;
@@ -48,7 +47,7 @@ public class DelegatingContentHandler
private DTDHandler dtdHandler;
private LexicalHandler lexicalHandler;
private ErrorHandler errorHandler;
-
+
/**
* Main constructor.
*/
@@ -62,7 +61,7 @@ public class DelegatingContentHandler
public ContentHandler getDelegateContentHandler() {
return this.delegate;
}
-
+
/**
* Sets the delegate ContentHandler that all events are forwarded to.
* @param handler the delegate instance
@@ -70,7 +69,7 @@ public class DelegatingContentHandler
public void setDelegateContentHandler(ContentHandler handler) {
this.delegate = handler;
}
-
+
/**
* Sets the delegate EntityResolver.
* @param resolver the delegate instance
@@ -78,7 +77,7 @@ public class DelegatingContentHandler
public void setDelegateEntityResolver(EntityResolver resolver) {
this.entityResolver = resolver;
}
-
+
/**
* Sets the delegate DTDHandler.
* @param handler the delegate instance
@@ -86,7 +85,7 @@ public class DelegatingContentHandler
public void setDelegateDTDHandler(DTDHandler handler) {
this.dtdHandler = handler;
}
-
+
/**
* Sets the delegate LexicalHandler.
* @param handler the delegate instance
@@ -94,7 +93,7 @@ public class DelegatingContentHandler
public void setDelegateLexicalHandler(LexicalHandler handler) {
this.lexicalHandler = handler;
}
-
+
/**
* Sets the delegate ErrorHandler.
* @param handler the delegate instance
@@ -102,13 +101,12 @@ public class DelegatingContentHandler
public void setDelegateErrorHandler(ErrorHandler handler) {
this.errorHandler = handler;
}
-
+
// ==== EntityResolver
-
- /**
- * {@inheritDoc}
- */
- public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
+
+ /** {@inheritDoc} */
+ public InputSource resolveEntity(String publicId, String systemId)
+ throws SAXException, IOException {
if (entityResolver != null) {
return entityResolver.resolveEntity(publicId, systemId);
} else {
@@ -118,19 +116,15 @@ public class DelegatingContentHandler
// ==== DTDHandler
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void notationDecl(String name, String publicId, String systemId) throws SAXException {
if (dtdHandler != null) {
dtdHandler.notationDecl(name, publicId, systemId);
}
}
- /**
- * {@inheritDoc}
- */
- public void unparsedEntityDecl(String name, String publicId, String systemId,
+ /** {@inheritDoc} */
+ public void unparsedEntityDecl(String name, String publicId, String systemId,
String notationName) throws SAXException {
if (dtdHandler != null) {
dtdHandler.unparsedEntityDecl(name, publicId, systemId, notationName);
@@ -138,174 +132,132 @@ public class DelegatingContentHandler
}
// ==== ContentHandler
-
- /**
- * {@inheritDoc}
- */
+
+ /** {@inheritDoc} */
public void setDocumentLocator(Locator locator) {
delegate.setDocumentLocator(locator);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void startDocument() throws SAXException {
delegate.startDocument();
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void endDocument() throws SAXException {
delegate.endDocument();
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void startPrefixMapping(String prefix, String uri) throws SAXException {
delegate.startPrefixMapping(prefix, uri);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void endPrefixMapping(String prefix) throws SAXException {
delegate.endPrefixMapping(prefix);
}
- /**
- * {@inheritDoc}
- */
- public void startElement(String uri, String localName, String qName,
+ /** {@inheritDoc} */
+ public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException {
delegate.startElement(uri, localName, qName, atts);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void endElement(String uri, String localName, String qName) throws SAXException {
delegate.endElement(uri, localName, qName);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void characters(char[] ch, int start, int length) throws SAXException {
delegate.characters(ch, start, length);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
delegate.ignorableWhitespace(ch, start, length);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void processingInstruction(String target, String data) throws SAXException {
delegate.processingInstruction(target, data);
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void skippedEntity(String name) throws SAXException {
delegate.skippedEntity(name);
}
// ==== LexicalHandler
-
- /**
- * {@inheritDoc}
- */
+
+ /** {@inheritDoc} */
public void startDTD(String name, String publicId, String systemId) throws SAXException {
if (lexicalHandler != null) {
lexicalHandler.startDTD(name, publicId, systemId);
}
-
+
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void endDTD() throws SAXException {
if (lexicalHandler != null) {
lexicalHandler.endDTD();
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void startEntity(String name) throws SAXException {
if (lexicalHandler != null) {
lexicalHandler.startEntity(name);
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void endEntity(String name) throws SAXException {
if (lexicalHandler != null) {
lexicalHandler.endEntity(name);
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void startCDATA() throws SAXException {
if (lexicalHandler != null) {
lexicalHandler.startCDATA();
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void endCDATA() throws SAXException {
if (lexicalHandler != null) {
lexicalHandler.endCDATA();
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void comment(char[] ch, int start, int length) throws SAXException {
if (lexicalHandler != null) {
lexicalHandler.comment(ch, start, length);
}
}
-
+
// ==== ErrorHandler
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void warning(SAXParseException exception) throws SAXException {
if (errorHandler != null) {
errorHandler.warning(exception);
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void error(SAXParseException exception) throws SAXException {
if (errorHandler != null) {
errorHandler.error(exception);
}
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
public void fatalError(SAXParseException exception) throws SAXException {
if (errorHandler != null) {
errorHandler.fatalError(exception);
diff --git a/src/sandbox/META-INF/services/org.apache.fop.render.Renderer b/src/sandbox/META-INF/services/org.apache.fop.render.Renderer
deleted file mode 100644
index ea7c13f92..000000000
--- a/src/sandbox/META-INF/services/org.apache.fop.render.Renderer
+++ /dev/null
@@ -1 +0,0 @@
-org.apache.fop.render.svg.SVGRendererMaker \ No newline at end of file
diff --git a/src/sandbox/META-INF/services/org.apache.fop.render.intermediate.IFPainter b/src/sandbox/META-INF/services/org.apache.fop.render.intermediate.IFPainter
new file mode 100644
index 000000000..7913529fa
--- /dev/null
+++ b/src/sandbox/META-INF/services/org.apache.fop.render.intermediate.IFPainter
@@ -0,0 +1,2 @@
+org.apache.fop.render.svg.SVGPainterMaker
+org.apache.fop.render.svg.SVGPrintPainterMaker \ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/svg/AbstractSVGPainter.java b/src/sandbox/org/apache/fop/render/svg/AbstractSVGPainter.java
new file mode 100644
index 000000000..38b439fe0
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/svg/AbstractSVGPainter.java
@@ -0,0 +1,303 @@
+/*
+ * 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.svg;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Paint;
+import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.apache.xmlgraphics.xmp.Metadata;
+
+import org.apache.fop.render.intermediate.AbstractXMLWritingIFPainter;
+import org.apache.fop.render.intermediate.IFException;
+import org.apache.fop.render.intermediate.IFState;
+import org.apache.fop.util.ColorUtil;
+
+/**
+ * Abstract base class for SVG Painter implementations.
+ */
+public abstract class AbstractSVGPainter extends AbstractXMLWritingIFPainter
+ implements SVGConstants {
+
+ /** Holds the intermediate format state */
+ protected IFState state;
+
+ private static final int MODE_NORMAL = 0;
+ private static final int MODE_TEXT = 1;
+
+ private int mode = MODE_NORMAL;
+
+ /** {@inheritDoc} */
+ protected String getMainNamespace() {
+ return NAMESPACE;
+ }
+
+ /** {@inheritDoc} */
+ public void startDocumentHeader() throws IFException {
+ try {
+ startElement("defs");
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startDocumentHeader()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endDocumentHeader() throws IFException {
+ try {
+ endElement("defs");
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startDocumentHeader()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startPageContent() throws IFException {
+ this.state = IFState.create();
+ }
+
+ /** {@inheritDoc} */
+ public void endPageContent() throws IFException {
+ assert this.state.pop() == null;
+ }
+
+ /** {@inheritDoc} */
+ public void startBox(AffineTransform transform, Dimension size, boolean clip)
+ throws IFException {
+ StringBuffer sb = new StringBuffer();
+ toString(transform, sb);
+ startBox(sb.toString(), size, clip);
+ }
+
+ /** {@inheritDoc} */
+ public void startBox(AffineTransform[] transforms, Dimension size, boolean clip)
+ throws IFException {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0, c = transforms.length; i < c; i++) {
+ if (i > 0) {
+ sb.append(' ');
+ }
+ toString(transforms[i], sb);
+ }
+ startBox(sb.toString(), size, clip);
+ }
+
+ private void startBox(String transform, Dimension size, boolean clip) throws IFException {
+ try {
+ establish(MODE_NORMAL);
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute("", "transform", "transform", CDATA, transform);
+ /*
+ if (size != null) {
+ atts.addAttribute("", "width", "width", CDATA, Integer.toString(size.width));
+ atts.addAttribute("", "height", "height", CDATA, Integer.toString(size.height));
+ }
+ if (clip) {
+ atts.addAttribute("", "clip", "clip", CDATA, "true");
+ }*/
+ startElement("g", atts);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startBox()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endBox() throws IFException {
+ try {
+ establish(MODE_NORMAL);
+ endElement("g");
+ } catch (SAXException e) {
+ throw new IFException("SAX error in endBox()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startImage(Rectangle rect) throws IFException {
+ //establish(MODE_NORMAL);
+ // TODO Auto-generated method stub
+
+ }
+
+ /** {@inheritDoc} */
+ public void drawImage(String uri, Rectangle rect) throws IFException {
+ //establish(MODE_NORMAL);
+ // 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 {
+ //establish(MODE_NORMAL);
+ // TODO Auto-generated method stub
+
+ }
+
+ private static String toString(Paint paint) {
+ //TODO Paint serialization: Fine-tune and extend!
+ if (paint instanceof Color) {
+ return ColorUtil.colorToString((Color)paint);
+ } else {
+ throw new UnsupportedOperationException("Paint not supported: " + paint);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void drawRect(Rectangle rect, Paint fill, Color stroke) throws IFException {
+ if (fill == null && stroke == null) {
+ return;
+ }
+ try {
+ establish(MODE_NORMAL);
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute("", "x", "x", CDATA, Integer.toString(rect.x));
+ atts.addAttribute("", "y", "y", CDATA, Integer.toString(rect.y));
+ atts.addAttribute("", "width", "width", CDATA, Integer.toString(rect.width));
+ atts.addAttribute("", "height", "height", CDATA, Integer.toString(rect.height));
+ if (fill != null) {
+ atts.addAttribute("", "fill", "fill", CDATA, toString(fill));
+ }
+ if (stroke != null) {
+ atts.addAttribute("", "stroke", "sroke", CDATA, toString(stroke));
+ }
+ element("rect", atts);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in drawRect()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void drawText(int x, int y, int[] dx, int[] dy, String text) throws IFException {
+ try {
+ establish(MODE_TEXT);
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute("", "x", "x", CDATA, Integer.toString(x));
+ atts.addAttribute("", "y", "y", CDATA, Integer.toString(y));
+ if (dx != null) {
+ atts.addAttribute("", "dx", "dx", CDATA, toString(dx));
+ }
+ if (dy != null) {
+ atts.addAttribute("", "dy", "dy", CDATA, toString(dy));
+ }
+ startElement("text", atts);
+ char[] chars = text.toCharArray();
+ handler.characters(chars, 0, chars.length);
+ endElement("text");
+ } catch (SAXException e) {
+ throw new IFException("SAX error in setFont()", e);
+ }
+ }
+
+ /** {@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);
+ }
+ }
+
+ private void leaveTextMode() throws SAXException {
+ assert this.mode == MODE_TEXT;
+ endElement("g");
+ this.mode = MODE_NORMAL;
+ }
+
+ private void establish(int newMode) throws SAXException {
+ switch (newMode) {
+ case MODE_TEXT:
+ enterTextMode();
+ break;
+ default:
+ if (this.mode == MODE_TEXT) {
+ leaveTextMode();
+ }
+ }
+ }
+
+ private void enterTextMode() throws SAXException {
+ if (state.isFontChanged() && this.mode == MODE_TEXT) {
+ leaveTextMode();
+ }
+ if (this.mode != MODE_TEXT) {
+ startTextGroup();
+ this.mode = MODE_TEXT;
+ }
+ }
+
+ private void startTextGroup() throws SAXException {
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute("", "font-family", "font-family",
+ CDATA, state.getFontFamily());
+ atts.addAttribute("", "font-style", "font-style",
+ CDATA, state.getFontStyle());
+ atts.addAttribute("", "font-weight", "font-weight",
+ CDATA, Integer.toString(state.getFontWeight()));
+ atts.addAttribute("", "font-variant", "font-variant",
+ CDATA, state.getFontVariant());
+ atts.addAttribute("", "font-size", "font-size",
+ CDATA, Integer.toString(state.getFontSize()));
+ atts.addAttribute("", "fill", "fill",
+ CDATA, toString(state.getTextColor()));
+ startElement("g", atts);
+ state.resetFontChanged();
+ }
+
+ /** {@inheritDoc} */
+ public void handleExtensionObject(Object extension) throws IFException {
+ if (extension instanceof Metadata) {
+ Metadata meta = (Metadata)extension;
+ try {
+ establish(MODE_NORMAL);
+ startElement("metadata");
+ meta.toSAX(this.handler);
+ endElement("metadata");
+ } catch (SAXException e) {
+ throw new IFException("SAX error while handling extension object", e);
+ }
+ } else {
+ throw new UnsupportedOperationException(
+ "Don't know how to handle extension object: " + extension);
+ }
+ }
+}
diff --git a/src/sandbox/org/apache/fop/render/svg/SVGConstants.java b/src/sandbox/org/apache/fop/render/svg/SVGConstants.java
new file mode 100644
index 000000000..56b8d2a8a
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/svg/SVGConstants.java
@@ -0,0 +1,49 @@
+/*
+ * 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.svg;
+
+import org.apache.fop.apps.MimeConstants;
+
+/**
+ * Constants for the intermediate format.
+ */
+public interface SVGConstants {
+
+ /** MIME type for SVG. */
+ String MIME_TYPE = MimeConstants.MIME_SVG;
+
+ /** MIME type for SVG Print. */
+ String MIME_SVG_PRINT = MimeConstants.MIME_SVG + ";profile=print";
+
+ /** File extension for SVG. */
+ String FILE_EXTENSION_SVG = "svg";
+
+ /** XML namespace for SVG. */
+ String NAMESPACE = "http://www.w3.org/2000/svg";
+
+ /** XML namespace. */
+ String XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace";
+
+ /** Namespace prefix for XLink */
+ String XLINK_PREFIX = "xlink";
+ /** XML namespace for XLink */
+ String XLINK_NAMESPACE = "http://www.w3.org/1999/xlink";
+
+}
diff --git a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
new file mode 100644
index 000000000..3e461e6a0
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
@@ -0,0 +1,254 @@
+/*
+ * 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.svg;
+
+import java.awt.Dimension;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Result;
+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.dom.DOMSource;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Document;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.apache.commons.io.IOUtils;
+
+import org.apache.fop.render.bitmap.MultiFileRenderingUtil;
+import org.apache.fop.render.intermediate.DelegatingFragmentContentHandler;
+import org.apache.fop.render.intermediate.IFException;
+
+/**
+ * IFPainter implementation that writes SVG.
+ */
+public class SVGPainter extends AbstractSVGPainter {
+
+ /** Helper class for generating multiple files */
+ private MultiFileRenderingUtil multiFileUtil;
+
+ private StreamResult firstStream;
+ private StreamResult currentStream;
+
+ private Document reusedParts;
+
+ /**
+ * Default constructor.
+ */
+ public SVGPainter() {
+ //nop
+ }
+
+ /** {@inheritDoc} */
+ public boolean supportsPagesOutOfOrder() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public void setResult(Result result) throws IFException {
+ if (result instanceof StreamResult) {
+ multiFileUtil = new MultiFileRenderingUtil(FILE_EXTENSION_SVG,
+ getUserAgent().getOutputFile());
+ this.firstStream = (StreamResult)result;
+ } else {
+ throw new UnsupportedOperationException("Result is not supported: " + result);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startDocument() throws IFException {
+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+ builderFactory.setNamespaceAware(true);
+ builderFactory.setValidating(false);
+ try {
+ DocumentBuilder builder = builderFactory.newDocumentBuilder();
+ this.reusedParts = builder.newDocument();
+ } catch (ParserConfigurationException e) {
+ throw new IFException("Error while setting up a DOM for SVG generation", e);
+ }
+
+ try {
+ TransformerHandler toDOMHandler = tFactory.newTransformerHandler();
+ toDOMHandler.setResult(new DOMResult(this.reusedParts));
+ this.handler = toDOMHandler;
+ } catch (TransformerConfigurationException e) {
+ throw new IFException(
+ "Error while setting up a TransformerHandler for SVG generation", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endDocument() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void startPageSequence(String id) throws IFException {
+ //nop
+ }
+
+ /** {@inheritDoc} */
+ public void endPageSequence() throws IFException {
+ //nop
+ }
+
+ /** {@inheritDoc} */
+ public void startPage(int index, String name, Dimension size) throws IFException {
+ OutputStream out;
+ try {
+ out = this.multiFileUtil.createOutputStream(index);
+ } catch (IOException ioe) {
+ throw new IFException("I/O exception while setting up output file", ioe);
+ }
+ if (out == null) {
+ this.handler = createContentHandler(this.firstStream);
+ } else {
+ this.currentStream = new StreamResult(out);
+ this.handler = createContentHandler(this.currentStream);
+ }
+ if (false) {
+ final ContentHandler originalHandler = this.handler;
+ this.handler = (ContentHandler)Proxy.newProxyInstance(
+ ContentHandler.class.getClassLoader(),
+ new Class[] {ContentHandler.class},
+ new InvocationHandler() {
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ String methodName = method.getName();
+ System.out.println(methodName + ":");
+ if (args != null) {
+ for (int i = 0; i < args.length; i++) {
+ System.out.println(" " + args[i]);
+ }
+ }
+ return method.invoke(originalHandler, args);
+ }
+ });
+ }
+ try {
+ handler.startDocument();
+ handler.startPrefixMapping("", NAMESPACE);
+ handler.startPrefixMapping(XLINK_PREFIX, XLINK_NAMESPACE);
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute("", "version", "version", CDATA, "1.1"); //SVG 1.1
+ /*
+ atts.addAttribute("", "index", "index", CDATA, Integer.toString(index));
+ atts.addAttribute("", "name", "name", CDATA, name);
+ */
+ atts.addAttribute("", "width", "width", CDATA,
+ Float.toString(size.width / 1000f) + "pt");
+ atts.addAttribute("", "height", "height", CDATA,
+ Float.toString(size.height / 1000f) + "pt");
+ atts.addAttribute("", "viewBox", "viewBox", CDATA,
+ "0 0 " + Integer.toString(size.width) + " " + Integer.toString(size.height));
+ startElement("svg", atts);
+
+ try {
+ Transformer transformer = tFactory.newTransformer();
+ Source src = new DOMSource(this.reusedParts.getDocumentElement());
+ Result res = new SAXResult(new DelegatingFragmentContentHandler(this.handler));
+ transformer.transform(src, res);
+ } catch (TransformerConfigurationException tce) {
+ throw new IFException("Error setting up a Transformer", tce);
+ } catch (TransformerException te) {
+ if (te.getCause() instanceof SAXException) {
+ throw (SAXException)te.getCause();
+ } else {
+ throw new IFException("Error while serializing reused parts", te);
+ }
+ }
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startPage()", e);
+ }
+ }
+
+ private void closeCurrentStream() {
+ if (this.currentStream != null) {
+ IOUtils.closeQuietly(currentStream.getOutputStream());
+ currentStream.setOutputStream(null);
+ IOUtils.closeQuietly(currentStream.getWriter());
+ currentStream.setWriter(null);
+ this.currentStream = null;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startPageHeader() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void endPageHeader() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void startPageContent() throws IFException {
+ super.startPageContent();
+ try {
+ startElement("g");
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startPageContent()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endPageContent() throws IFException {
+ try {
+ endElement("g");
+ } catch (SAXException e) {
+ throw new IFException("SAX error in endPageContent()", e);
+ }
+ super.endPageContent();
+ }
+
+ /** {@inheritDoc} */
+ public void startPageTrailer() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void endPageTrailer() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void endPage() throws IFException {
+ try {
+ endElement("svg");
+ this.handler.endDocument();
+ } catch (SAXException e) {
+ throw new IFException("SAX error in endPage()", e);
+ }
+ closeCurrentStream();
+ }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/svg/SVGPainterMaker.java b/src/sandbox/org/apache/fop/render/svg/SVGPainterMaker.java
new file mode 100644
index 000000000..a81f41cd3
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/svg/SVGPainterMaker.java
@@ -0,0 +1,48 @@
+/*
+ * 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.svg;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.render.intermediate.AbstractIFPainterMaker;
+import org.apache.fop.render.intermediate.IFPainter;
+
+/**
+ * Painter factory for SVG output.
+ */
+public class SVGPainterMaker extends AbstractIFPainterMaker {
+
+ private static final String[] MIMES = new String[] {SVGConstants.MIME_TYPE};
+
+ /** {@inheritDoc} */
+ public IFPainter makePainter(FOUserAgent ua) {
+ return new SVGPainter();
+ }
+
+ /** {@inheritDoc} */
+ public boolean needsOutputStream() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public String[] getSupportedMimeTypes() {
+ return MIMES;
+ }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/svg/SVGPrintPainter.java b/src/sandbox/org/apache/fop/render/svg/SVGPrintPainter.java
new file mode 100644
index 000000000..db6a96afe
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/svg/SVGPrintPainter.java
@@ -0,0 +1,172 @@
+/*
+ * 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.svg;
+
+import java.awt.Dimension;
+
+import javax.xml.transform.Result;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.apache.fop.render.intermediate.IFConstants;
+import org.apache.fop.render.intermediate.IFException;
+
+/**
+ * IFPainter implementation that writes SVG Print.
+ */
+public class SVGPrintPainter extends AbstractSVGPainter {
+
+ /**
+ * Default constructor.
+ */
+ public SVGPrintPainter() {
+ //nop
+ }
+
+ /**
+ * Creates a new SVGPrintPainter that sends the XML content it generates to the given
+ * SAX ContentHandler.
+ * @param result the JAXP Result object to receive the generated content
+ * @throws IFException if an error occurs setting up the output
+ */
+ public SVGPrintPainter(Result result) throws IFException {
+ setResult(result);
+ }
+
+ /** {@inheritDoc} */
+ public boolean supportsPagesOutOfOrder() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public void startDocument() throws IFException {
+ try {
+ handler.startDocument();
+ handler.startPrefixMapping("", NAMESPACE);
+ handler.startPrefixMapping(XLINK_PREFIX, XLINK_NAMESPACE);
+ handler.startPrefixMapping("if", IFConstants.NAMESPACE);
+ AttributesImpl atts = new AttributesImpl();
+ atts.addAttribute("", "version", "version", CDATA, "1.2"); //SVG Print is SVG 1.2
+ startElement("svg", atts);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startDocument()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endDocument() throws IFException {
+ try {
+ endElement("svg");
+ handler.endDocument();
+ } catch (SAXException e) {
+ throw new IFException("SAX error in endDocument()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startPageSequence(String id) throws IFException {
+ try {
+ AttributesImpl atts = new AttributesImpl();
+ if (id != null) {
+ atts.addAttribute(XML_NAMESPACE, "id", "xml:id", CDATA, id);
+ }
+ startElement("pageSet", atts);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startPageSequence()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endPageSequence() throws IFException {
+ try {
+ endElement("pageSet");
+ } catch (SAXException e) {
+ throw new IFException("SAX error in endPageSequence()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startPage(int index, String name, Dimension size) throws IFException {
+ try {
+ AttributesImpl atts = new AttributesImpl();
+ /*
+ atts.addAttribute("", "index", "index", CDATA, Integer.toString(index));
+ atts.addAttribute("", "name", "name", CDATA, name);
+ */
+ //NOTE: SVG Print doesn't support individual page sizes for each page
+ atts.addAttribute(IFConstants.NAMESPACE, "width", "if:width",
+ CDATA, Integer.toString(size.width));
+ atts.addAttribute(IFConstants.NAMESPACE, "height", "if:height",
+ CDATA, Integer.toString(size.height));
+ atts.addAttribute(IFConstants.NAMESPACE, "viewBox", "if:viewBox", CDATA,
+ "0 0 " + Integer.toString(size.width) + " " + Integer.toString(size.height));
+ startElement("page", atts);
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startPage()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startPageHeader() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void endPageHeader() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void startPageContent() throws IFException {
+ super.startPageContent();
+ try {
+ startElement("g");
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startPageContent()", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endPageContent() throws IFException {
+ try {
+ endElement("g");
+ } catch (SAXException e) {
+ throw new IFException("SAX error in endPageContent()", e);
+ }
+ super.endPageContent();
+ }
+
+ /** {@inheritDoc} */
+ public void startPageTrailer() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void endPageTrailer() throws IFException {
+ }
+
+ /** {@inheritDoc} */
+ public void endPage() throws IFException {
+ try {
+ endElement("page");
+ } catch (SAXException e) {
+ throw new IFException("SAX error in endPage()", e);
+ }
+ }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/svg/SVGPrintPainterMaker.java b/src/sandbox/org/apache/fop/render/svg/SVGPrintPainterMaker.java
new file mode 100644
index 000000000..f647ec4cd
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/svg/SVGPrintPainterMaker.java
@@ -0,0 +1,48 @@
+/*
+ * 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.svg;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.render.intermediate.AbstractIFPainterMaker;
+import org.apache.fop.render.intermediate.IFPainter;
+
+/**
+ * Painter factory for SVG Print output.
+ */
+public class SVGPrintPainterMaker extends AbstractIFPainterMaker {
+
+ private static final String[] MIMES = new String[] {SVGConstants.MIME_SVG_PRINT};
+
+ /** {@inheritDoc} */
+ public IFPainter makePainter(FOUserAgent ua) {
+ return new SVGPrintPainter();
+ }
+
+ /** {@inheritDoc} */
+ public boolean needsOutputStream() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public String[] getSupportedMimeTypes() {
+ return MIMES;
+ }
+
+}