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,v 5a7becda9147 2009/10/23 10:57:54 max $ */
018
019 package net.sourceforge.jeuclid.converter;
020
021 import java.io.IOException;
022
023 import javax.xml.transform.Result;
024 import javax.xml.transform.Source;
025 import javax.xml.transform.Transformer;
026 import javax.xml.transform.TransformerException;
027 import javax.xml.transform.TransformerFactory;
028 import javax.xml.transform.dom.DOMResult;
029 import javax.xml.transform.dom.DOMSource;
030
031 import net.sourceforge.jeuclid.LayoutContext;
032 import net.sourceforge.jeuclid.context.LayoutContextImpl;
033 import net.sourceforge.jeuclid.converter.ConverterPlugin.DocumentWithDimension;
034 import net.sourceforge.jeuclid.elements.AbstractJEuclidElement;
035 import net.sourceforge.jeuclid.elements.generic.MathImpl;
036 import net.sourceforge.jeuclid.parser.Parser;
037
038 import org.apache.commons.logging.Log;
039 import org.apache.commons.logging.LogFactory;
040 import org.w3c.dom.Element;
041 import org.w3c.dom.Node;
042 import org.w3c.dom.NodeList;
043 import org.xml.sax.SAXException;
044
045 /**
046 * Contains the actual processing routines.
047 * <p>
048 * To use this class obtain an instance of the Processor singleton instance.
049 * Then use the {@link #process(Source, Result)} function to process your
050 * Document.
051 * <p>
052 * This will replace all occurrences of MathML within fo:instream tags by the
053 * equivalent SVG code. It will also add a baseline-shift attribute so that the
054 * formula is in line with the rest of the text.
055 *
056 * @version $Revision: 5a7becda9147 $
057 */
058 public final class Processor {
059
060 private static final class SingletonHolder {
061 private static final Processor INSTANCE = new Processor();
062
063 private SingletonHolder() {
064 }
065 }
066
067 /**
068 * Logger for this class
069 */
070 private static final Log LOGGER = LogFactory.getLog(Processor.class);
071
072 // private static final String NAMESPACE_HTML =
073 // "http://www.w3.org/1999/xhtml";
074
075 private final Transformer transformer;
076
077 /**
078 * Default constructor.
079 */
080 protected Processor() {
081 Transformer t;
082 try {
083 t = TransformerFactory.newInstance().newTransformer();
084 } catch (final TransformerException e) {
085 t = null;
086 Processor.LOGGER.warn(e.getMessage());
087 assert false;
088 }
089 this.transformer = t;
090 }
091
092 /**
093 * Retrieve the processor singleton object.
094 *
095 * @return the Processor.
096 */
097 public static Processor getInstance() {
098 return Processor.SingletonHolder.INSTANCE;
099 }
100
101 /**
102 * use {@link #getInstance()} instead.
103 *
104 * @return see {@link #getInstance()}
105 * @throws TransformerException
106 * see {@link #getInstance()}
107 * @deprecated use {@link #getInstance()} instead.
108 */
109 @Deprecated
110 public static Processor getProcessor() throws TransformerException {
111 return Processor.getInstance();
112 }
113
114 /**
115 * Pre-process a .fo file.
116 *
117 * @param inputSource
118 * Input File
119 * @param result
120 * Output File
121 * @param context
122 * LayoutContext.
123 * @throws TransformerException
124 * an error occurred during the processing.
125 */
126 public void process(final Source inputSource, final Result result,
127 final LayoutContext context) throws TransformerException {
128 Processor.LOGGER.info("Processing " + inputSource.getSystemId()
129 + " to " + result.getSystemId());
130 try {
131 final Node doc = Parser.getInstance().parse(inputSource);
132 this.processSubtree(doc, context);
133 final DOMSource source = new DOMSource(doc);
134 this.transformer.transform(source, result);
135 } catch (final IOException e) {
136 throw new TransformerException("IOException", e);
137 } catch (final SAXException e) {
138 throw new TransformerException("SAXException", e);
139 }
140 }
141
142 /**
143 * Pre-process a .fo file.
144 *
145 * @param inputSource
146 * Input File
147 * @param result
148 * Output File
149 * @throws TransformerException
150 * an error occurred during the processing.
151 */
152 public void process(final Source inputSource, final Result result)
153 throws TransformerException {
154 this.process(inputSource, result, LayoutContextImpl
155 .getDefaultLayoutContext());
156 }
157
158 private void processSubtree(final Node node, final LayoutContext context) {
159 if (AbstractJEuclidElement.URI.equals(node.getNamespaceURI())
160 && MathImpl.ELEMENT.equals(node.getLocalName())) {
161
162 final DocumentWithDimension svgdocdim = Converter
163 .getInstance()
164 .convert(
165 node,
166 net.sourceforge.jeuclid.converter.Converter.TYPE_SVG,
167 context);
168
169 final float baselinePercent = -(svgdocdim.getBaseline() / (float) svgdocdim
170 .getDimension().getHeight()) * 100f;
171
172 final Node parent = node.getParentNode();
173 if ("http://www.w3.org/1999/XSL/Format".equals(parent
174 .getNamespaceURI())
175 && "instream-foreign-object".equals(parent.getLocalName())) {
176 final Element pElement = (Element) parent;
177 pElement
178 .setAttribute("alignment-adjust", baselinePercent + "%");
179 }
180 this.safeReplaceChild(parent, node, svgdocdim.getDocument()
181 .getFirstChild());
182 } else {
183 this.processChildren(node, context);
184 // TODO: This is an IE-Fix, but does not work yet.
185 // final Node parent = node.getParentNode();
186 // if ((parent != null)
187 // && (Processor.NAMESPACE_HTML.equals(parent
188 // .getNamespaceURI()))
189 // && ("html".equals(parent.getLocalName()))
190 // && ("head".equals(node.getLocalName()))) {
191 // ((Element) parent).setAttribute("xmlns:svg",
192 // "http://www.w3.org/2000/svg");
193 // final Document ownerDoc = node.getOwnerDocument();
194 // final Element objectElement = ownerDoc
195 // .createElement("object");
196 // objectElement.setAttribute("id", "AdobeSVG");
197 // objectElement.setAttribute("classid",
198 // "clsid:78156a80-c6a1-4bbf-8e6a-3cd390eeb4e2");
199 // node.appendChild(objectElement);
200 // final ProcessingInstruction pi = ownerDoc
201 // .createProcessingInstruction("import",
202 // "namespace=\"svg\" implementation=\"#AdobeSVG\"");
203 // node.appendChild(pi);
204 // }
205 }
206 }
207
208 private void safeReplaceChild(final Node parent, final Node oldChild,
209 final Node newChild) {
210 try {
211 final DOMSource source = new DOMSource(newChild);
212 final DOMResult result = new DOMResult(parent);
213
214 this.transformer.transform(source, result);
215 } catch (final TransformerException e) {
216 Processor.LOGGER.warn("TranformerException: " + e.getMessage());
217 }
218 parent.removeChild(oldChild);
219 }
220
221 private void processChildren(final Node node, final LayoutContext context) {
222 final NodeList childList = node.getChildNodes();
223 if (childList != null) {
224 for (int i = 0; i < childList.getLength(); i++) {
225 final Node child = childList.item(i);
226 this.processSubtree(child, context);
227 }
228 }
229 }
230
231 }