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.

PDFXMLHandler.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. /*
  2. * $Id: PDFXMLHandler.java,v 1.13 2003/03/07 09:46:32 jeremias Exp $
  3. * ============================================================================
  4. * The Apache Software License, Version 1.1
  5. * ============================================================================
  6. *
  7. * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  8. *
  9. * Redistribution and use in source and binary forms, with or without modifica-
  10. * tion, are permitted provided that the following conditions are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright notice,
  13. * this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright notice,
  16. * this list of conditions and the following disclaimer in the documentation
  17. * and/or other materials provided with the distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if any, must
  20. * include the following acknowledgment: "This product includes software
  21. * developed by the Apache Software Foundation (http://www.apache.org/)."
  22. * Alternately, this acknowledgment may appear in the software itself, if
  23. * and wherever such third-party acknowledgments normally appear.
  24. *
  25. * 4. The names "FOP" and "Apache Software Foundation" must not be used to
  26. * endorse or promote products derived from this software without prior
  27. * written permission. For written permission, please contact
  28. * apache@apache.org.
  29. *
  30. * 5. Products derived from this software may not be called "Apache", nor may
  31. * "Apache" appear in their name, without prior written permission of the
  32. * Apache Software Foundation.
  33. *
  34. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
  35. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  36. * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  37. * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  38. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
  39. * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  40. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  41. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  42. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  43. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44. * ============================================================================
  45. *
  46. * This software consists of voluntary contributions made by many individuals
  47. * on behalf of the Apache Software Foundation and was originally created by
  48. * James Tauber <jtauber@jtauber.com>. For more information on the Apache
  49. * Software Foundation, please see <http://www.apache.org/>.
  50. */
  51. package org.apache.fop.render.pdf;
  52. import org.apache.fop.render.XMLHandler;
  53. import org.apache.fop.render.RendererContext;
  54. import org.apache.fop.pdf.PDFDocument;
  55. import org.apache.fop.pdf.PDFPage;
  56. import org.apache.fop.pdf.PDFState;
  57. import org.apache.fop.pdf.PDFStream;
  58. import org.apache.fop.pdf.PDFNumber;
  59. import org.apache.fop.pdf.PDFResourceContext;
  60. import org.apache.fop.svg.PDFTextElementBridge;
  61. import org.apache.fop.svg.PDFAElementBridge;
  62. import org.apache.fop.svg.PDFGraphics2D;
  63. import org.apache.fop.svg.SVGUserAgent;
  64. import org.apache.fop.apps.Document;
  65. /* org.w3c.dom.Document is not imported to avoid conflict with
  66. org.apache.fop.control.Document */
  67. import java.io.OutputStream;
  68. import org.apache.batik.bridge.GVTBuilder;
  69. import org.apache.batik.bridge.BridgeContext;
  70. import org.apache.batik.bridge.ViewBox;
  71. import org.apache.batik.gvt.GraphicsNode;
  72. import org.w3c.dom.svg.SVGDocument;
  73. import org.w3c.dom.svg.SVGSVGElement;
  74. import java.awt.geom.AffineTransform;
  75. import org.apache.fop.apps.*;
  76. /**
  77. * PDF XML handler.
  78. * This handler handles XML for foreign objects when rendering to PDF.
  79. * It renders SVG to the PDF document using the PDFGraphics2D.
  80. * The properties from the PDF renderer are subject to change.
  81. */
  82. public class PDFXMLHandler implements XMLHandler {
  83. /**
  84. * The PDF document that is being drawn into.
  85. */
  86. public static final String PDF_DOCUMENT = "pdfDoc";
  87. /**
  88. * The output stream that the document is being sent to.
  89. */
  90. public static final String OUTPUT_STREAM = "outputStream";
  91. /**
  92. * The current pdf state.
  93. */
  94. public static final String PDF_STATE = "pdfState";
  95. /**
  96. * The current PDF page for page renference and as a resource context.
  97. */
  98. public static final String PDF_PAGE = "pdfPage";
  99. /**
  100. * The current PDF page for page renference and as a resource context.
  101. */
  102. public static final String PDF_CONTEXT = "pdfContext";
  103. /**
  104. * The current PDF stream to draw directly to.
  105. */
  106. public static final String PDF_STREAM = "pdfStream";
  107. /**
  108. * The width of the current pdf page.
  109. */
  110. public static final String PDF_WIDTH = "width";
  111. /**
  112. * The height of the current pdf page.
  113. */
  114. public static final String PDF_HEIGHT = "height";
  115. /**
  116. * The current font information for the pdf renderer.
  117. */
  118. public static final String PDF_FONT_INFO = "fontInfo";
  119. /**
  120. * The current pdf font name.
  121. */
  122. public static final String PDF_FONT_NAME = "fontName";
  123. /**
  124. * The current pdf font size.
  125. */
  126. public static final String PDF_FONT_SIZE = "fontSize";
  127. /**
  128. * The x position that this is being drawn at.
  129. */
  130. public static final String PDF_XPOS = "xpos";
  131. /**
  132. * The y position that this is being drawn at.
  133. */
  134. public static final String PDF_YPOS = "ypos";
  135. /**
  136. * Create a new PDF XML handler for use by the PDF renderer.
  137. */
  138. public PDFXMLHandler() {
  139. }
  140. /**
  141. * Handle the XML.
  142. * This checks the type of XML and handles appropraitely.
  143. *
  144. * @param context the renderer context
  145. * @param doc the XML document to render
  146. * @param ns the namespace of the XML document
  147. * @throws Exception any sort of exception could be thrown and shuld be handled
  148. */
  149. public void handleXML(RendererContext context, org.w3c.dom.Document doc,
  150. String ns) throws Exception {
  151. PDFInfo pdfi = getPDFInfo(context);
  152. String svg = "http://www.w3.org/2000/svg";
  153. if (svg.equals(ns)) {
  154. SVGHandler svghandler = new SVGHandler();
  155. svghandler.renderSVGDocument(context, doc, pdfi);
  156. } else {
  157. }
  158. }
  159. /**
  160. * Get the pdf information from the render context.
  161. *
  162. * @param context the renderer context
  163. * @return the pdf information retrieved from the context
  164. */
  165. public static PDFInfo getPDFInfo(RendererContext context) {
  166. PDFInfo pdfi = new PDFInfo();
  167. pdfi.pdfDoc = (PDFDocument)context.getProperty(PDF_DOCUMENT);
  168. pdfi.outputStream = (OutputStream)context.getProperty(OUTPUT_STREAM);
  169. pdfi.pdfState = (PDFState)context.getProperty(PDF_STATE);
  170. pdfi.pdfPage = (PDFPage)context.getProperty(PDF_PAGE);
  171. pdfi.pdfContext = (PDFResourceContext)context.getProperty(PDF_CONTEXT);
  172. pdfi.currentStream = (PDFStream)context.getProperty(PDF_STREAM);
  173. pdfi.width = ((Integer)context.getProperty(PDF_WIDTH)).intValue();
  174. pdfi.height = ((Integer)context.getProperty(PDF_HEIGHT)).intValue();
  175. pdfi.fi = (Document)context.getProperty(PDF_FONT_INFO);
  176. pdfi.currentFontName = (String)context.getProperty(PDF_FONT_NAME);
  177. pdfi.currentFontSize = ((Integer)context.getProperty(PDF_FONT_SIZE)).intValue();
  178. pdfi.currentXPosition = ((Integer)context.getProperty(PDF_XPOS)).intValue();
  179. pdfi.currentYPosition = ((Integer)context.getProperty(PDF_YPOS)).intValue();
  180. return pdfi;
  181. }
  182. /**
  183. * PDF information structure for drawing the XML document.
  184. */
  185. public static class PDFInfo {
  186. /** see PDF_DOCUMENT */
  187. public PDFDocument pdfDoc;
  188. /** see OUTPUT_STREAM */
  189. public OutputStream outputStream;
  190. /** see PDF_STATE */
  191. public PDFState pdfState;
  192. /** see PDF_PAGE */
  193. public PDFPage pdfPage;
  194. /** see PDF_CONTEXT */
  195. public PDFResourceContext pdfContext;
  196. /** see PDF_STREAM */
  197. public PDFStream currentStream;
  198. /** see PDF_WIDTH */
  199. public int width;
  200. /** see PDF_HEIGHT */
  201. public int height;
  202. /** see PDF_FONT_INFO */
  203. public Document fi;
  204. /** see PDF_FONT_NAME */
  205. public String currentFontName;
  206. /** see PDF_FONT_SIZE */
  207. public int currentFontSize;
  208. /** see PDF_XPOS */
  209. public int currentXPosition;
  210. /** see PDF_YPOS */
  211. public int currentYPosition;
  212. }
  213. /**
  214. * This method is placed in an inner class so that we don't get class
  215. * loading errors if batik is not present.
  216. */
  217. protected class SVGHandler {
  218. /**
  219. * Render the svg document.
  220. * @param context the renderer context
  221. * @param doc the svg document
  222. * @param pdfInfo the pdf information of the current context
  223. */
  224. protected void renderSVGDocument(RendererContext context,
  225. org.w3c.dom.Document doc, PDFInfo pdfInfo) {
  226. int xOffset = pdfInfo.currentXPosition;
  227. int yOffset = pdfInfo.currentYPosition;
  228. SVGUserAgent ua
  229. = new SVGUserAgent(context.getUserAgent(), new AffineTransform());
  230. GVTBuilder builder = new GVTBuilder();
  231. BridgeContext ctx = new BridgeContext(ua);
  232. PDFTextElementBridge tBridge = new PDFTextElementBridge(pdfInfo.fi);
  233. ctx.putBridge(tBridge);
  234. PDFAElementBridge aBridge = new PDFAElementBridge();
  235. // to get the correct transform we need to use the PDFState
  236. AffineTransform transform = pdfInfo.pdfState.getTransform();
  237. transform.translate(xOffset / 1000f, yOffset / 1000f);
  238. aBridge.setCurrentTransform(transform);
  239. ctx.putBridge(aBridge);
  240. GraphicsNode root;
  241. try {
  242. root = builder.build(ctx, doc);
  243. } catch (Exception e) {
  244. context.getUserAgent().getLogger().error("svg graphic could not be built: "
  245. + e.getMessage(), e);
  246. return;
  247. }
  248. // get the 'width' and 'height' attributes of the SVG document
  249. float w = (float)ctx.getDocumentSize().getWidth() * 1000f;
  250. float h = (float)ctx.getDocumentSize().getHeight() * 1000f;
  251. float sx = pdfInfo.width / (float)w;
  252. float sy = pdfInfo.height / (float)h;
  253. ctx = null;
  254. builder = null;
  255. /*
  256. * Clip to the svg area.
  257. * Note: To have the svg overlay (under) a text area then use
  258. * an fo:block-container
  259. */
  260. pdfInfo.currentStream.add("q\n");
  261. // transform so that the coordinates (0,0) is from the top left
  262. // and positive is down and to the right. (0,0) is where the
  263. // viewBox puts it.
  264. pdfInfo.currentStream.add(sx + " 0 0 " + sy + " " + xOffset / 1000f + " "
  265. + yOffset / 1000f + " cm\n");
  266. SVGSVGElement svg = ((SVGDocument)doc).getRootElement();
  267. AffineTransform at = ViewBox.getPreserveAspectRatioTransform(svg, w / 1000f, h / 1000f);
  268. if (!at.isIdentity()) {
  269. double[] vals = new double[6];
  270. at.getMatrix(vals);
  271. pdfInfo.currentStream.add(PDFNumber.doubleOut(vals[0], 5) + " "
  272. + PDFNumber.doubleOut(vals[1], 5) + " "
  273. + PDFNumber.doubleOut(vals[2], 5) + " "
  274. + PDFNumber.doubleOut(vals[3], 5) + " "
  275. + PDFNumber.doubleOut(vals[4]) + " "
  276. + PDFNumber.doubleOut(vals[5]) + " cm\n");
  277. }
  278. if (pdfInfo.pdfContext == null) {
  279. pdfInfo.pdfContext = pdfInfo.pdfPage;
  280. }
  281. PDFGraphics2D graphics = new PDFGraphics2D(true, pdfInfo.fi, pdfInfo.pdfDoc,
  282. pdfInfo.pdfContext, pdfInfo.pdfPage.referencePDF(),
  283. pdfInfo.currentFontName,
  284. pdfInfo.currentFontSize);
  285. graphics.setGraphicContext(new org.apache.batik.ext.awt.g2d.GraphicContext());
  286. pdfInfo.pdfState.push();
  287. transform = new AffineTransform();
  288. // scale to viewbox
  289. transform.translate(xOffset / 1000f, yOffset / 1000f);
  290. pdfInfo.pdfState.setTransform(transform);
  291. graphics.setPDFState(pdfInfo.pdfState);
  292. graphics.setOutputStream(pdfInfo.outputStream);
  293. try {
  294. root.paint(graphics);
  295. pdfInfo.currentStream.add(graphics.getString());
  296. } catch (Exception e) {
  297. context.getUserAgent().getLogger().error("svg graphic could not be rendered: "
  298. + e.getMessage(), e);
  299. }
  300. pdfInfo.currentStream.add("Q\n");
  301. pdfInfo.pdfState.pop();
  302. }
  303. }
  304. }