1 16 package org.outerj.daisy.books.publisher.impl.publicationprocess; 17 18 import org.outerj.daisy.books.store.BookInstance; 19 import org.outerj.daisy.books.publisher.impl.BookInstanceLayout; 20 import org.outerj.daisy.books.publisher.impl.util.AbstractContentHandler; 21 import org.outerj.daisy.books.publisher.impl.util.PublicationLog; 22 import org.outerj.daisy.xmlutil.LocalSAXParserFactory; 23 import org.outerj.daisy.xmlutil.XmlSerializer; 24 import org.xml.sax.InputSource ; 25 import org.xml.sax.ContentHandler ; 26 import org.xml.sax.Attributes ; 27 import org.xml.sax.SAXException ; 28 import org.apache.batik.transcoder.image.PNGTranscoder; 29 import org.apache.batik.transcoder.image.JPEGTranscoder; 30 import org.apache.batik.transcoder.*; 31 import org.apache.batik.bridge.BridgeContext; 32 import org.apache.batik.bridge.UserAgentAdapter; 33 import org.apache.batik.bridge.GVTBuilder; 34 import org.apache.batik.dom.svg.SAXSVGDocumentFactory; 35 import org.apache.batik.util.XMLResourceDescriptor; 36 import org.apache.cocoon.xml.AttributesImpl; 37 import org.outerx.daisy.x10Bookstoremeta.ResourcePropertiesDocument; 38 import org.w3c.dom.Document ; 39 import org.w3c.dom.svg.SVGDocument; 40 41 import javax.xml.parsers.SAXParser ; 42 import java.io.InputStream ; 43 import java.io.OutputStream ; 44 import java.util.Map ; 45 import java.awt.*; 46 import java.awt.geom.Dimension2D ; 47 48 57 public class SvgRenderTask implements PublicationProcessTask { 58 private final String input; 59 private final String output; 60 private final String outputPrefix; 61 private final String format; 62 private final float dpi; 63 private final float defaultDpi = 96; 64 private final float quality; 65 private final Color backgroundColor; 66 private final boolean enableScripts; 67 private final float maxPrintWidth; 68 private final float maxPrintHeight; 69 private static final String NS = "http://outerx.org/daisy/1.0#bookSvgRenderTask"; 70 71 public SvgRenderTask(Map attributes) throws Exception { 72 this.input = PublicationProcessTaskUtil.getRequiredAttribute(attributes, "input", "renderSVG"); 73 this.output = PublicationProcessTaskUtil.getRequiredAttribute(attributes, "output", "renderSVG"); 74 75 if (attributes.containsKey("outputPrefix")) { 76 outputPrefix = (String )attributes.get("outputPrefix"); 77 } else { 78 outputPrefix = "from-svg/"; 79 } 80 81 if (attributes.containsKey("format")) { 82 format = (String )attributes.get("format"); 83 } else { 84 format = "jpg"; 85 } 86 87 if (attributes.containsKey("dpi")) { 88 dpi = parseFloat((String )attributes.get("dpi"), "dpi"); 89 } else { 90 dpi = defaultDpi; 91 } 92 93 if (attributes.containsKey("quality")) { 94 quality = parseFloat((String )attributes.get("quality"), "quality"); 95 } else { 96 quality = 1f; 97 } 98 99 if (attributes.containsKey("backgroundColor")) { 100 backgroundColor = parseColor((String )attributes.get("backgroundColor")); 101 } else { 102 backgroundColor = null; 103 } 104 105 if (attributes.containsKey("enableScripts")) { 106 enableScripts = "true".equalsIgnoreCase((String )attributes.get("enableScripts")); 107 } else { 108 enableScripts = false; 109 } 110 111 if (attributes.containsKey("maxPrintWidth")) { 112 maxPrintWidth = parseFloat((String )attributes.get("maxPrintWidth"), "maxPrintWidth"); 113 } else { 114 maxPrintWidth = 6.45f; 115 } 116 117 if (attributes.containsKey("maxPrintHeight")) { 118 maxPrintHeight = parseFloat((String )attributes.get("maxPrintHeight"), "maxPrintHeight"); 119 } else { 120 maxPrintHeight = 8.6f; 121 } 122 } 123 124 private static float parseFloat(String value, String name) throws Exception { 125 try { 126 return Float.parseFloat(value); 127 } catch (NumberFormatException e) { 128 throw new Exception ("Invalid float value specified for " + name + ": " + value); 129 } 130 } 131 132 private static Color parseColor(String value) throws Exception { 133 if (!value.startsWith("#") && value.length() != 7) { 134 throw new Exception ("Color specification should be in the form #FFFFFF, not: " + value); 135 } 136 137 int r, g, b; 138 try { 139 r = Integer.parseInt(value.substring(1, 3), 16); 140 g = Integer.parseInt(value.substring(3, 5), 16); 141 b = Integer.parseInt(value.substring(5, 7), 16); 142 } catch (NumberFormatException e) { 143 throw new Exception ("Invalid color specification: " + value); 144 } 145 146 return new Color(r, g, b); 147 } 148 149 public void run(PublicationContext context) throws Exception { 150 context.getPublicationLog().info("Running SVG render task."); 151 BookInstance bookInstance = context.getBookInstance(); 152 String publicationOutputPath = BookInstanceLayout.getPublicationOutputPath(context.getPublicationOutputName()); 153 String startXmlLocation = publicationOutputPath + input; 154 InputStream is = null; 155 OutputStream os = null; 156 try { 157 is = bookInstance.getResource(startXmlLocation); 158 os = bookInstance.getResourceOutputStream(publicationOutputPath + output); 159 SAXParser parser = LocalSAXParserFactory.getSAXParserFactory().newSAXParser(); 160 XmlSerializer serializer = new XmlSerializer(os); 161 SvgRenderHandler svgRenderHandler = new SvgRenderHandler(serializer, context); 162 parser.getXMLReader().setContentHandler(svgRenderHandler); 163 parser.getXMLReader().parse(new InputSource (is)); 164 } finally { 165 if (is != null) 166 try { is.close(); } catch (Exception e) { } 167 if (os != null) 168 try { os.close(); } catch (Exception e) { } 169 } 170 } 171 172 class SvgRenderHandler extends AbstractContentHandler { 173 private PublicationContext context; 174 175 public SvgRenderHandler(ContentHandler consumer, PublicationContext context) { 176 super(consumer); 177 this.context = context; 178 } 179 180 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { 181 if (namespaceURI.equals(NS) && localName.equals("renderSVG")) { 182 InputStream svgInputStream = null; 183 OutputStream svgOutputStream = null; 184 try { 185 String svgPath = atts.getValue("bookStorePath"); 186 svgInputStream = context.getBookInstance().getResource(svgPath); 187 188 SAXSVGDocumentFactory svgDocumentFactory = new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName()); 189 SVGDocument svgDocument = (SVGDocument)svgDocumentFactory.createDocument(context.getBookInstance().getResourceURI(svgPath).toString(), svgInputStream); 190 191 Dimension2D svgDimension = getSvgDimension(svgDocument); 192 193 float svgWidth = (float)svgDimension.getWidth(); 194 float svgHeight = (float)svgDimension.getHeight(); 195 float width = (float)Math.floor(svgWidth * (dpi / defaultDpi)); 196 float height = (float)Math.floor(svgHeight * (dpi / defaultDpi)); 197 198 Transcoder transcoder; 199 if (format.equals("jpg")) 200 transcoder = new JPEGTranscoder(); 201 else if (format.equals("png")) 202 transcoder = new PNGTranscoder(); 203 else 204 throw new Exception ("svgRender task: unrecognized format: " + format); 205 206 transcoder.setErrorHandler(new MyErrorHandler(context.getPublicationLog())); 207 transcoder.addTranscodingHint(JPEGTranscoder.KEY_HEIGHT, new Float (height)); 208 if (backgroundColor != null) 209 transcoder.addTranscodingHint(JPEGTranscoder.KEY_BACKGROUND_COLOR, backgroundColor); 210 transcoder.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, new Float (quality)); 211 if (enableScripts) { 212 transcoder.addTranscodingHint(JPEGTranscoder.KEY_EXECUTE_ONLOAD, Boolean.TRUE); 213 transcoder.addTranscodingHint(JPEGTranscoder.KEY_CONSTRAIN_SCRIPT_ORIGIN, Boolean.FALSE); 214 } 215 216 TranscoderInput transcoderInput = new TranscoderInput(svgDocument); 217 218 String publicationOutputPath = BookInstanceLayout.getPublicationOutputPath(context.getPublicationOutputName()); 219 String svgOutputPath = publicationOutputPath + outputPrefix + PublicationProcessTaskUtil.getFileName(svgPath); 220 svgOutputStream = context.getBookInstance().getResourceOutputStream(svgOutputPath); 221 TranscoderOutput transcoderOutput = new TranscoderOutput(svgOutputStream); 222 223 transcoder.transcode(transcoderInput, transcoderOutput); 225 226 ResourcePropertiesDocument propertiesDocument = ResourcePropertiesDocument.Factory.newInstance(); 228 propertiesDocument.addNewResourceProperties().setMimeType("image/" + format); 229 context.getBookInstance().storeResourceProperties(svgOutputPath, propertiesDocument); 230 231 float printWidth = width / dpi; 232 float printHeight = height / dpi; 233 if (printWidth > maxPrintWidth) { 234 printHeight = printHeight * (maxPrintWidth / printWidth); 235 printWidth = maxPrintWidth; 236 } 237 if (printHeight > maxPrintHeight) { 238 printWidth = printWidth * (maxPrintHeight / printHeight); 239 printHeight = maxPrintHeight; 240 } 241 242 AttributesImpl attrs = new AttributesImpl(); 243 attrs.addCDATAAttribute("src", "bookinstance:" + svgOutputPath); 244 attrs.addCDATAAttribute("width", String.valueOf((int)width)); 245 attrs.addCDATAAttribute("height", String.valueOf((int)height)); 246 attrs.addCDATAAttribute("print-width", String.valueOf(printWidth) + "in"); 247 attrs.addCDATAAttribute("print-height", String.valueOf(printHeight) + "in"); 248 startElement("", "img", "img", attrs); 249 endElement("", "img", "img"); 250 } catch (Exception e) { 251 throw new SAXException ("Error rendering SVG.", e); 252 } finally { 253 if (svgInputStream != null) 254 try { svgInputStream.close(); } catch (Exception e) { } 255 if (svgOutputStream != null) 256 try { svgOutputStream.close(); } catch (Exception e) { } 257 } 258 } else { 259 super.startElement(namespaceURI, localName, qName, atts); 260 } 261 } 262 263 private Dimension2D getSvgDimension(Document document) { 264 UserAgentAdapter userAgent = new UserAgentAdapter(); 265 BridgeContext ctx = new BridgeContext(userAgent); 266 GVTBuilder builder = new GVTBuilder(); 267 ctx.setDynamicState(enableScripts ? BridgeContext.DYNAMIC : BridgeContext.STATIC); 268 builder.build(ctx, document); 269 return ctx.getDocumentSize(); 270 } 271 272 public void endElement(String namespaceURI, String localName, String qName) throws SAXException { 273 if (namespaceURI.equals(NS) && localName.equals("renderSVG")) { 274 } else { 276 super.endElement(namespaceURI, localName, qName); 277 } 278 } 279 } 280 281 static class MyErrorHandler implements ErrorHandler { 282 private PublicationLog log; 283 284 public MyErrorHandler(PublicationLog log) { 285 this.log = log; 286 } 287 288 public void error(TranscoderException transcoderException) throws TranscoderException { 289 log.error("Error reported by Batik.", transcoderException); 290 } 291 292 public void fatalError(TranscoderException transcoderException) throws TranscoderException { 293 log.error("Fatal error reported by Batik.", transcoderException); 294 } 295 296 public void warning(TranscoderException transcoderException) throws TranscoderException { 297 log.error("Warning reported by Batik.", transcoderException); 298 } 299 } 300 301 } 302 | Popular Tags |