001 /*
002 * Copyright 2007 - 2007 JEuclid, http://jeuclid.sf.net
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 /* $Id: Processor.java 375 2007-07-05 11:51:58Z maxberger $ */
018
019 package net.sourceforge.jeuclid.app.foprep;
020
021 import java.awt.Dimension;
022 import java.io.IOException;
023
024 import javax.xml.parsers.ParserConfigurationException;
025 import javax.xml.transform.Result;
026 import javax.xml.transform.Source;
027 import javax.xml.transform.Transformer;
028 import javax.xml.transform.TransformerException;
029 import javax.xml.transform.TransformerFactory;
030 import javax.xml.transform.dom.DOMResult;
031 import javax.xml.transform.dom.DOMSource;
032
033 import net.sourceforge.jeuclid.DOMBuilder;
034 import net.sourceforge.jeuclid.MathBase;
035 import net.sourceforge.jeuclid.elements.AbstractJEuclidElement;
036 import net.sourceforge.jeuclid.elements.generic.MathImpl;
037 import net.sourceforge.jeuclid.parser.Parser;
038
039 import org.apache.batik.dom.GenericDOMImplementation;
040 import org.apache.batik.svggen.SVGGeneratorContext;
041 import org.apache.batik.svggen.SVGGraphics2D;
042 import org.apache.commons.logging.Log;
043 import org.apache.commons.logging.LogFactory;
044 import org.w3c.dom.DOMImplementation;
045 import org.w3c.dom.Document;
046 import org.w3c.dom.Element;
047 import org.w3c.dom.Node;
048 import org.w3c.dom.NodeList;
049 import org.xml.sax.SAXException;
050
051 /**
052 * Contains the actual processing routines.
053 * <p>
054 * To use this class obtain an instance of the Processor singleton instance.
055 * Then use the {@link #process(Source, Result)} function to process your
056 * Document.
057 * <p>
058 * This will replace all occurrences of MathML within fo:instream tags by the
059 * equivalent SVG code. It will also add a baseline-shift attribute so that
060 * the formula is in line with the rest of the text.
061 *
062 * @author Max Berger
063 * @version $Revision: 375 $
064 */
065 public final class Processor {
066
067 private static Processor processor;
068
069 /**
070 * Logger for this class
071 */
072 private static final Log LOGGER = LogFactory.getLog(Processor.class);
073
074 private final Transformer transformer;
075
076 private Processor() throws TransformerException {
077 this.transformer = TransformerFactory.newInstance().newTransformer();
078 }
079
080 /**
081 * Retrieve the processor singleton object.
082 *
083 * @return the Processor.
084 * @throws TransformerException
085 * an error occurred creating the necessary Transformer
086 * instance.
087 */
088 public static synchronized Processor getProcessor()
089 throws TransformerException {
090 if (Processor.processor == null) {
091 Processor.processor = new Processor();
092 }
093 return Processor.processor;
094 }
095
096 /**
097 * Pre-process a .fo file.
098 *
099 * @param inputSource
100 * Input File
101 * @param result
102 * Output File
103 * @throws TransformerException
104 * an error occurred during the processing.
105 */
106 public void process(final Source inputSource, final Result result)
107 throws TransformerException {
108 Processor.LOGGER.info("Processing " + inputSource + " to " + result);
109 try {
110 final Node doc = Parser.getParser().parse(inputSource);
111
112 this.processSubtree(doc);
113
114 final DOMSource source = new DOMSource(doc);
115
116 this.transformer.transform(source, result);
117
118 } catch (final IOException e) {
119 throw new TransformerException("IOException", e);
120 } catch (final SAXException e) {
121 throw new TransformerException("SAXException", e);
122 } catch (final ParserConfigurationException e) {
123 throw new TransformerException("ParserConfigurationException", e);
124 }
125 }
126
127 private void processSubtree(final Node node) {
128 if (AbstractJEuclidElement.URI.equals(node.getNamespaceURI())
129 && MathImpl.ELEMENT.equals(node.getLocalName())) {
130
131 final MathBase mathBase = new MathBase(MathBase
132 .getDefaultParameters());
133 new DOMBuilder(node, mathBase);
134
135 final SVGGraphics2D svgGenerator = this
136 .createSVGGenerator(mathBase);
137 mathBase.paint(svgGenerator);
138 final float descender = mathBase.getDescender(svgGenerator);
139
140 final Node parent = node.getParentNode();
141 if ("http://www.w3.org/1999/XSL/Format".equals(parent
142 .getNamespaceURI())
143 && "instream-foreign-object"
144 .equals(parent.getLocalName())) {
145 final Element pElement = (Element) parent;
146 pElement.setAttribute("baseline-shift", -descender + "pt");
147 }
148 this.safeReplaceChild(parent, node, svgGenerator.getRoot());
149 } else {
150 this.processChildren(node);
151 }
152 }
153
154 private SVGGraphics2D createSVGGenerator(final MathBase mathBase) {
155 final DOMImplementation domImpl = GenericDOMImplementation
156 .getDOMImplementation();
157
158 final Document document = domImpl.createDocument(null,
159 net.sourceforge.jeuclid.Converter.EXTENSION_SVG, null);
160 final SVGGeneratorContext svgContext = SVGGeneratorContext
161 .createDefault(document);
162 svgContext.setComment("Converted from MathML using JEuclid");
163 final SVGGraphics2D svgGenerator = new SVGGraphics2D(svgContext, true);
164
165 final Dimension size = new Dimension((int) Math.ceil(mathBase
166 .getWidth(svgGenerator)), (int) Math.ceil(mathBase
167 .getHeight(svgGenerator)));
168 svgGenerator.setSVGCanvasSize(size);
169 return svgGenerator;
170 }
171
172 private void safeReplaceChild(final Node parent, final Node oldChild,
173 final Node newChild) {
174 try {
175 final DOMSource source = new DOMSource(newChild);
176 final DOMResult result = new DOMResult(parent);
177
178 this.transformer.transform(source, result);
179 } catch (final TransformerException e) {
180 Processor.LOGGER.warn("TranformerException: " + e.getMessage());
181 }
182 parent.removeChild(oldChild);
183 }
184
185 private void processChildren(final Node node) {
186 final NodeList childList = node.getChildNodes();
187 if (childList != null) {
188 for (int i = 0; i < childList.getLength(); i++) {
189 final Node child = childList.item(i);
190 this.processSubtree(child);
191 }
192 }
193 }
194
195 }