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 }