View Javadoc

1   /*
2    * Copyright 2007 - 2007 JEuclid, http://jeuclid.sf.net
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  /* $Id: Processor.java 375 2007-07-05 11:51:58Z maxberger $ */
18  
19  package net.sourceforge.jeuclid.app.foprep;
20  
21  import java.awt.Dimension;
22  import java.io.IOException;
23  
24  import javax.xml.parsers.ParserConfigurationException;
25  import javax.xml.transform.Result;
26  import javax.xml.transform.Source;
27  import javax.xml.transform.Transformer;
28  import javax.xml.transform.TransformerException;
29  import javax.xml.transform.TransformerFactory;
30  import javax.xml.transform.dom.DOMResult;
31  import javax.xml.transform.dom.DOMSource;
32  
33  import net.sourceforge.jeuclid.DOMBuilder;
34  import net.sourceforge.jeuclid.MathBase;
35  import net.sourceforge.jeuclid.elements.AbstractJEuclidElement;
36  import net.sourceforge.jeuclid.elements.generic.MathImpl;
37  import net.sourceforge.jeuclid.parser.Parser;
38  
39  import org.apache.batik.dom.GenericDOMImplementation;
40  import org.apache.batik.svggen.SVGGeneratorContext;
41  import org.apache.batik.svggen.SVGGraphics2D;
42  import org.apache.commons.logging.Log;
43  import org.apache.commons.logging.LogFactory;
44  import org.w3c.dom.DOMImplementation;
45  import org.w3c.dom.Document;
46  import org.w3c.dom.Element;
47  import org.w3c.dom.Node;
48  import org.w3c.dom.NodeList;
49  import org.xml.sax.SAXException;
50  
51  /**
52   * Contains the actual processing routines.
53   * <p>
54   * To use this class obtain an instance of the Processor singleton instance.
55   * Then use the {@link #process(Source, Result)} function to process your
56   * Document.
57   * <p>
58   * This will replace all occurrences of MathML within fo:instream tags by the
59   * equivalent SVG code. It will also add a baseline-shift attribute so that
60   * the formula is in line with the rest of the text.
61   * 
62   * @author Max Berger
63   * @version $Revision: 375 $
64   */
65  public final class Processor {
66  
67      private static Processor processor;
68  
69      /**
70       * Logger for this class
71       */
72      private static final Log LOGGER = LogFactory.getLog(Processor.class);
73  
74      private final Transformer transformer;
75  
76      private Processor() throws TransformerException {
77          this.transformer = TransformerFactory.newInstance().newTransformer();
78      }
79  
80      /**
81       * Retrieve the processor singleton object.
82       * 
83       * @return the Processor.
84       * @throws TransformerException
85       *             an error occurred creating the necessary Transformer
86       *             instance.
87       */
88      public static synchronized Processor getProcessor()
89              throws TransformerException {
90          if (Processor.processor == null) {
91              Processor.processor = new Processor();
92          }
93          return Processor.processor;
94      }
95  
96      /**
97       * Pre-process a .fo file.
98       * 
99       * @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 }