diff options
Diffstat (limited to 'src')
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; + } + +} |