You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PDFSVGHandler.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /*
  2. * Copyright 1999-2005 The Apache Software Foundation.
  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. /* $Id$ */
  17. package org.apache.fop.render.pdf;
  18. import java.io.OutputStream;
  19. import java.awt.Color;
  20. import java.awt.geom.AffineTransform;
  21. import org.w3c.dom.Document;
  22. import org.w3c.dom.svg.SVGDocument;
  23. import org.w3c.dom.svg.SVGSVGElement;
  24. import org.apache.fop.render.Renderer;
  25. import org.apache.fop.render.XMLHandler;
  26. import org.apache.fop.render.RendererContext;
  27. import org.apache.fop.pdf.PDFDocument;
  28. import org.apache.fop.pdf.PDFNumber;
  29. import org.apache.fop.pdf.PDFPage;
  30. import org.apache.fop.pdf.PDFState;
  31. import org.apache.fop.pdf.PDFStream;
  32. import org.apache.fop.pdf.PDFResourceContext;
  33. import org.apache.fop.svg.PDFBridgeContext;
  34. import org.apache.fop.svg.PDFGraphics2D;
  35. import org.apache.fop.svg.SVGUserAgent;
  36. import org.apache.fop.fonts.FontInfo;
  37. // Commons-Logging
  38. import org.apache.commons.logging.Log;
  39. import org.apache.commons.logging.LogFactory;
  40. import org.apache.avalon.framework.configuration.Configuration;
  41. import org.apache.batik.bridge.GVTBuilder;
  42. import org.apache.batik.bridge.BridgeContext;
  43. import org.apache.batik.bridge.ViewBox;
  44. import org.apache.batik.dom.svg.SVGDOMImplementation;
  45. import org.apache.batik.gvt.GraphicsNode;
  46. /**
  47. * PDF XML handler for SVG (uses Apache Batik).
  48. * This handler handles XML for foreign objects when rendering to PDF.
  49. * It renders SVG to the PDF document using the PDFGraphics2D.
  50. * The properties from the PDF renderer are subject to change.
  51. */
  52. public class PDFSVGHandler implements XMLHandler, PDFRendererContextConstants {
  53. /** logging instance */
  54. private static Log log = LogFactory.getLog(PDFSVGHandler.class);
  55. /**
  56. * Create a new PDF XML handler for use by the PDF renderer.
  57. */
  58. public PDFSVGHandler() {
  59. }
  60. /** @see org.apache.fop.render.XMLHandler */
  61. public void handleXML(RendererContext context,
  62. Document doc, String ns) throws Exception {
  63. PDFInfo pdfi = getPDFInfo(context);
  64. String svg = "http://www.w3.org/2000/svg";
  65. if (svg.equals(ns)) {
  66. renderSVGDocument(context, doc, pdfi);
  67. }
  68. }
  69. /**
  70. * Get the pdf information from the render context.
  71. *
  72. * @param context the renderer context
  73. * @return the pdf information retrieved from the context
  74. */
  75. public static PDFInfo getPDFInfo(RendererContext context) {
  76. PDFInfo pdfi = new PDFInfo();
  77. pdfi.pdfDoc = (PDFDocument)context.getProperty(PDF_DOCUMENT);
  78. pdfi.outputStream = (OutputStream)context.getProperty(OUTPUT_STREAM);
  79. pdfi.pdfState = (PDFState)context.getProperty(PDF_STATE);
  80. pdfi.pdfPage = (PDFPage)context.getProperty(PDF_PAGE);
  81. pdfi.pdfContext = (PDFResourceContext)context.getProperty(PDF_CONTEXT);
  82. pdfi.currentStream = (PDFStream)context.getProperty(PDF_STREAM);
  83. pdfi.width = ((Integer)context.getProperty(WIDTH)).intValue();
  84. pdfi.height = ((Integer)context.getProperty(HEIGHT)).intValue();
  85. pdfi.fi = (FontInfo)context.getProperty(PDF_FONT_INFO);
  86. pdfi.currentFontName = (String)context.getProperty(PDF_FONT_NAME);
  87. pdfi.currentFontSize = ((Integer)context.getProperty(PDF_FONT_SIZE)).intValue();
  88. pdfi.currentXPosition = ((Integer)context.getProperty(XPOS)).intValue();
  89. pdfi.currentYPosition = ((Integer)context.getProperty(YPOS)).intValue();
  90. pdfi.cfg = (Configuration)context.getProperty(HANDLER_CONFIGURATION);
  91. return pdfi;
  92. }
  93. /**
  94. * PDF information structure for drawing the XML document.
  95. */
  96. public static class PDFInfo {
  97. /** see PDF_DOCUMENT */
  98. public PDFDocument pdfDoc;
  99. /** see OUTPUT_STREAM */
  100. public OutputStream outputStream;
  101. /** see PDF_STATE */
  102. public PDFState pdfState;
  103. /** see PDF_PAGE */
  104. public PDFPage pdfPage;
  105. /** see PDF_CONTEXT */
  106. public PDFResourceContext pdfContext;
  107. /** see PDF_STREAM */
  108. public PDFStream currentStream;
  109. /** see PDF_WIDTH */
  110. public int width;
  111. /** see PDF_HEIGHT */
  112. public int height;
  113. /** see PDF_FONT_INFO */
  114. public FontInfo fi;
  115. /** see PDF_FONT_NAME */
  116. public String currentFontName;
  117. /** see PDF_FONT_SIZE */
  118. public int currentFontSize;
  119. /** see PDF_XPOS */
  120. public int currentXPosition;
  121. /** see PDF_YPOS */
  122. public int currentYPosition;
  123. /** see PDF_HANDLER_CONFIGURATION */
  124. public Configuration cfg;
  125. }
  126. /**
  127. * Render the svg document.
  128. * @param context the renderer context
  129. * @param doc the svg document
  130. * @param pdfInfo the pdf information of the current context
  131. */
  132. protected void renderSVGDocument(RendererContext context,
  133. Document doc, PDFInfo pdfInfo) {
  134. int xOffset = pdfInfo.currentXPosition;
  135. int yOffset = pdfInfo.currentYPosition;
  136. log.debug("Generating SVG at "
  137. + context.getUserAgent().getTargetResolution()
  138. + "dpi.");
  139. final float deviceResolution = context.getUserAgent().getTargetResolution();
  140. final float uaResolution = context.getUserAgent().getSourceResolution();
  141. SVGUserAgent ua = new SVGUserAgent(25.4f / uaResolution, new AffineTransform());
  142. GVTBuilder builder = new GVTBuilder();
  143. //TODO This AffineTransform here has to be fixed!!!
  144. AffineTransform linkTransform = pdfInfo.pdfState.getTransform();
  145. linkTransform.translate(xOffset / 1000f, yOffset / 1000f);
  146. //Controls whether text painted by Batik is generated using text or path operations
  147. boolean strokeText = false;
  148. Configuration cfg = pdfInfo.cfg;
  149. if (cfg != null) {
  150. strokeText = cfg.getChild("stroke-text", true).getValueAsBoolean(strokeText);
  151. }
  152. BridgeContext ctx = new PDFBridgeContext(ua,
  153. (strokeText ? null : pdfInfo.fi),
  154. linkTransform);
  155. GraphicsNode root;
  156. try {
  157. root = builder.build(ctx, doc);
  158. } catch (Exception e) {
  159. log.error("svg graphic could not be built: "
  160. + e.getMessage(), e);
  161. return;
  162. }
  163. // get the 'width' and 'height' attributes of the SVG document
  164. float w = (float)ctx.getDocumentSize().getWidth() * 1000f;
  165. float h = (float)ctx.getDocumentSize().getHeight() * 1000f;
  166. float sx = pdfInfo.width / (float)w;
  167. float sy = pdfInfo.height / (float)h;
  168. ctx = null;
  169. builder = null;
  170. /*
  171. * Clip to the svg area.
  172. * Note: To have the svg overlay (under) a text area then use
  173. * an fo:block-container
  174. */
  175. PDFRenderer renderer = (PDFRenderer)context.getRenderer();
  176. renderer.saveGraphicsState();
  177. renderer.setColor(Color.black, false, null);
  178. renderer.setColor(Color.black, true, null);
  179. // transform so that the coordinates (0,0) is from the top left
  180. // and positive is down and to the right. (0,0) is where the
  181. // viewBox puts it.
  182. pdfInfo.currentStream.add(sx + " 0 0 " + sy + " " + xOffset / 1000f + " "
  183. + yOffset / 1000f + " cm\n");
  184. SVGSVGElement svg = ((SVGDocument)doc).getRootElement();
  185. //AffineTransform at = ViewBox.getPreserveAspectRatioTransform(
  186. // svg, w / 1000f, h / 1000f);
  187. AffineTransform at = ViewBox.getPreserveAspectRatioTransform(svg,
  188. pdfInfo.width / 1000f, pdfInfo.height / 1000f);
  189. /*
  190. if (!at.isIdentity()) {
  191. double[] vals = new double[6];
  192. at.getMatrix(vals);
  193. pdfInfo.currentStream.add(CTMHelper.toPDFString(at, false) + " cm\n");
  194. }*/
  195. if (pdfInfo.pdfContext == null) {
  196. pdfInfo.pdfContext = pdfInfo.pdfPage;
  197. }
  198. PDFGraphics2D graphics = new PDFGraphics2D(true, pdfInfo.fi,
  199. pdfInfo.pdfDoc,
  200. pdfInfo.pdfContext, pdfInfo.pdfPage.referencePDF(),
  201. pdfInfo.currentFontName, pdfInfo.currentFontSize);
  202. graphics.setGraphicContext(new org.apache.batik.ext.awt.g2d.GraphicContext());
  203. pdfInfo.pdfState.push();
  204. AffineTransform transform = new AffineTransform();
  205. // scale to viewbox
  206. transform.translate(xOffset / 1000f, yOffset / 1000f);
  207. if (deviceResolution != uaResolution) {
  208. //Scale for higher resolution on-the-fly images from Batik
  209. double s = uaResolution / deviceResolution;
  210. at.scale(s, s);
  211. pdfInfo.currentStream.add("" + PDFNumber.doubleOut(s) + " 0 0 "
  212. + PDFNumber.doubleOut(s) + " 0 0 cm\n");
  213. graphics.scale(1 / s, 1 / s);
  214. }
  215. pdfInfo.pdfState.setTransform(transform);
  216. graphics.setPDFState(pdfInfo.pdfState);
  217. graphics.setOutputStream(pdfInfo.outputStream);
  218. try {
  219. root.paint(graphics);
  220. pdfInfo.currentStream.add(graphics.getString());
  221. } catch (Exception e) {
  222. log.error("svg graphic could not be rendered: "
  223. + e.getMessage(), e);
  224. }
  225. renderer.restoreGraphicsState();
  226. pdfInfo.pdfState.pop();
  227. }
  228. /** @see org.apache.fop.render.XMLHandler#supportsRenderer(org.apache.fop.render.Renderer) */
  229. public boolean supportsRenderer(Renderer renderer) {
  230. return (renderer instanceof PDFRenderer);
  231. }
  232. /** @see org.apache.fop.render.XMLHandler#getNamespace() */
  233. public String getNamespace() {
  234. return SVGDOMImplementation.SVG_NAMESPACE_URI;
  235. }
  236. }