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 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. /*
  2. * Copyright 1999-2004 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 org.apache.fop.render.XMLHandler;
  19. import org.apache.fop.render.RendererContext;
  20. import org.apache.fop.pdf.PDFDocument;
  21. import org.apache.fop.pdf.PDFPage;
  22. import org.apache.fop.pdf.PDFState;
  23. import org.apache.fop.pdf.PDFStream;
  24. import org.apache.fop.pdf.PDFNumber;
  25. import org.apache.fop.pdf.PDFResourceContext;
  26. import org.apache.fop.svg.PDFTextElementBridge;
  27. import org.apache.fop.svg.PDFAElementBridge;
  28. import org.apache.fop.svg.PDFGraphics2D;
  29. import org.apache.fop.svg.SVGUserAgent;
  30. import org.apache.fop.apps.Document;
  31. /* org.w3c.dom.Document is not imported to avoid conflict with
  32. org.apache.fop.control.Document */
  33. import java.io.OutputStream;
  34. import org.apache.batik.bridge.GVTBuilder;
  35. import org.apache.batik.bridge.BridgeContext;
  36. import org.apache.batik.bridge.ViewBox;
  37. import org.apache.batik.gvt.GraphicsNode;
  38. import org.w3c.dom.svg.SVGDocument;
  39. import org.w3c.dom.svg.SVGSVGElement;
  40. import java.awt.geom.AffineTransform;
  41. /**
  42. * PDF XML handler.
  43. * This handler handles XML for foreign objects when rendering to PDF.
  44. * It renders SVG to the PDF document using the PDFGraphics2D.
  45. * The properties from the PDF renderer are subject to change.
  46. */
  47. public class PDFXMLHandler implements XMLHandler {
  48. /**
  49. * The PDF document that is being drawn into.
  50. */
  51. public static final String PDF_DOCUMENT = "pdfDoc";
  52. /**
  53. * The output stream that the document is being sent to.
  54. */
  55. public static final String OUTPUT_STREAM = "outputStream";
  56. /**
  57. * The current pdf state.
  58. */
  59. public static final String PDF_STATE = "pdfState";
  60. /**
  61. * The current PDF page for page renference and as a resource context.
  62. */
  63. public static final String PDF_PAGE = "pdfPage";
  64. /**
  65. * The current PDF page for page renference and as a resource context.
  66. */
  67. public static final String PDF_CONTEXT = "pdfContext";
  68. /**
  69. * The current PDF stream to draw directly to.
  70. */
  71. public static final String PDF_STREAM = "pdfStream";
  72. /**
  73. * The width of the current pdf page.
  74. */
  75. public static final String PDF_WIDTH = "width";
  76. /**
  77. * The height of the current pdf page.
  78. */
  79. public static final String PDF_HEIGHT = "height";
  80. /**
  81. * The current font information for the pdf renderer.
  82. */
  83. public static final String PDF_FONT_INFO = "fontInfo";
  84. /**
  85. * The current pdf font name.
  86. */
  87. public static final String PDF_FONT_NAME = "fontName";
  88. /**
  89. * The current pdf font size.
  90. */
  91. public static final String PDF_FONT_SIZE = "fontSize";
  92. /**
  93. * The x position that this is being drawn at.
  94. */
  95. public static final String PDF_XPOS = "xpos";
  96. /**
  97. * The y position that this is being drawn at.
  98. */
  99. public static final String PDF_YPOS = "ypos";
  100. /**
  101. * Create a new PDF XML handler for use by the PDF renderer.
  102. */
  103. public PDFXMLHandler() {
  104. }
  105. /**
  106. * Handle the XML.
  107. * This checks the type of XML and handles appropraitely.
  108. *
  109. * @param context the renderer context
  110. * @param doc the XML document to render
  111. * @param ns the namespace of the XML document
  112. * @throws Exception any sort of exception could be thrown and shuld be handled
  113. */
  114. public void handleXML(RendererContext context, org.w3c.dom.Document doc,
  115. String ns) throws Exception {
  116. PDFInfo pdfi = getPDFInfo(context);
  117. String svg = "http://www.w3.org/2000/svg";
  118. if (svg.equals(ns)) {
  119. SVGHandler svghandler = new SVGHandler();
  120. svghandler.renderSVGDocument(context, doc, pdfi);
  121. } else {
  122. }
  123. }
  124. /**
  125. * Get the pdf information from the render context.
  126. *
  127. * @param context the renderer context
  128. * @return the pdf information retrieved from the context
  129. */
  130. public static PDFInfo getPDFInfo(RendererContext context) {
  131. PDFInfo pdfi = new PDFInfo();
  132. pdfi.pdfDoc = (PDFDocument)context.getProperty(PDF_DOCUMENT);
  133. pdfi.outputStream = (OutputStream)context.getProperty(OUTPUT_STREAM);
  134. pdfi.pdfState = (PDFState)context.getProperty(PDF_STATE);
  135. pdfi.pdfPage = (PDFPage)context.getProperty(PDF_PAGE);
  136. pdfi.pdfContext = (PDFResourceContext)context.getProperty(PDF_CONTEXT);
  137. pdfi.currentStream = (PDFStream)context.getProperty(PDF_STREAM);
  138. pdfi.width = ((Integer)context.getProperty(PDF_WIDTH)).intValue();
  139. pdfi.height = ((Integer)context.getProperty(PDF_HEIGHT)).intValue();
  140. pdfi.fi = (Document)context.getProperty(PDF_FONT_INFO);
  141. pdfi.currentFontName = (String)context.getProperty(PDF_FONT_NAME);
  142. pdfi.currentFontSize = ((Integer)context.getProperty(PDF_FONT_SIZE)).intValue();
  143. pdfi.currentXPosition = ((Integer)context.getProperty(PDF_XPOS)).intValue();
  144. pdfi.currentYPosition = ((Integer)context.getProperty(PDF_YPOS)).intValue();
  145. return pdfi;
  146. }
  147. /**
  148. * PDF information structure for drawing the XML document.
  149. */
  150. public static class PDFInfo {
  151. /** see PDF_DOCUMENT */
  152. public PDFDocument pdfDoc;
  153. /** see OUTPUT_STREAM */
  154. public OutputStream outputStream;
  155. /** see PDF_STATE */
  156. public PDFState pdfState;
  157. /** see PDF_PAGE */
  158. public PDFPage pdfPage;
  159. /** see PDF_CONTEXT */
  160. public PDFResourceContext pdfContext;
  161. /** see PDF_STREAM */
  162. public PDFStream currentStream;
  163. /** see PDF_WIDTH */
  164. public int width;
  165. /** see PDF_HEIGHT */
  166. public int height;
  167. /** see PDF_FONT_INFO */
  168. public Document fi;
  169. /** see PDF_FONT_NAME */
  170. public String currentFontName;
  171. /** see PDF_FONT_SIZE */
  172. public int currentFontSize;
  173. /** see PDF_XPOS */
  174. public int currentXPosition;
  175. /** see PDF_YPOS */
  176. public int currentYPosition;
  177. }
  178. /**
  179. * This method is placed in an inner class so that we don't get class
  180. * loading errors if batik is not present.
  181. */
  182. protected class SVGHandler {
  183. /**
  184. * Render the svg document.
  185. * @param context the renderer context
  186. * @param doc the svg document
  187. * @param pdfInfo the pdf information of the current context
  188. */
  189. protected void renderSVGDocument(RendererContext context,
  190. org.w3c.dom.Document doc, PDFInfo pdfInfo) {
  191. int xOffset = pdfInfo.currentXPosition;
  192. int yOffset = pdfInfo.currentYPosition;
  193. SVGUserAgent ua
  194. = new SVGUserAgent(context.getUserAgent().getLogger(),
  195. context.getUserAgent().getPixelUnitToMillimeter(),
  196. new AffineTransform());
  197. GVTBuilder builder = new GVTBuilder();
  198. BridgeContext ctx = new BridgeContext(ua);
  199. PDFTextElementBridge tBridge = new PDFTextElementBridge(pdfInfo.fi);
  200. ctx.putBridge(tBridge);
  201. PDFAElementBridge aBridge = new PDFAElementBridge();
  202. // to get the correct transform we need to use the PDFState
  203. AffineTransform transform = pdfInfo.pdfState.getTransform();
  204. transform.translate(xOffset / 1000f, yOffset / 1000f);
  205. aBridge.setCurrentTransform(transform);
  206. ctx.putBridge(aBridge);
  207. GraphicsNode root;
  208. try {
  209. root = builder.build(ctx, doc);
  210. } catch (Exception e) {
  211. context.getUserAgent().getLogger().error("svg graphic could not be built: "
  212. + e.getMessage(), e);
  213. return;
  214. }
  215. // get the 'width' and 'height' attributes of the SVG document
  216. float w = (float)ctx.getDocumentSize().getWidth() * 1000f;
  217. float h = (float)ctx.getDocumentSize().getHeight() * 1000f;
  218. float sx = pdfInfo.width / (float)w;
  219. float sy = pdfInfo.height / (float)h;
  220. ctx = null;
  221. builder = null;
  222. /*
  223. * Clip to the svg area.
  224. * Note: To have the svg overlay (under) a text area then use
  225. * an fo:block-container
  226. */
  227. pdfInfo.currentStream.add("q\n");
  228. // transform so that the coordinates (0,0) is from the top left
  229. // and positive is down and to the right. (0,0) is where the
  230. // viewBox puts it.
  231. pdfInfo.currentStream.add(sx + " 0 0 " + sy + " " + xOffset / 1000f + " "
  232. + yOffset / 1000f + " cm\n");
  233. SVGSVGElement svg = ((SVGDocument)doc).getRootElement();
  234. AffineTransform at = ViewBox.getPreserveAspectRatioTransform(svg, w / 1000f, h / 1000f);
  235. if (!at.isIdentity()) {
  236. double[] vals = new double[6];
  237. at.getMatrix(vals);
  238. pdfInfo.currentStream.add(PDFNumber.doubleOut(vals[0], 5) + " "
  239. + PDFNumber.doubleOut(vals[1], 5) + " "
  240. + PDFNumber.doubleOut(vals[2], 5) + " "
  241. + PDFNumber.doubleOut(vals[3], 5) + " "
  242. + PDFNumber.doubleOut(vals[4]) + " "
  243. + PDFNumber.doubleOut(vals[5]) + " cm\n");
  244. }
  245. if (pdfInfo.pdfContext == null) {
  246. pdfInfo.pdfContext = pdfInfo.pdfPage;
  247. }
  248. PDFGraphics2D graphics = new PDFGraphics2D(true, pdfInfo.fi, pdfInfo.pdfDoc,
  249. pdfInfo.pdfContext, pdfInfo.pdfPage.referencePDF(),
  250. pdfInfo.currentFontName,
  251. pdfInfo.currentFontSize);
  252. graphics.setGraphicContext(new org.apache.batik.ext.awt.g2d.GraphicContext());
  253. pdfInfo.pdfState.push();
  254. transform = new AffineTransform();
  255. // scale to viewbox
  256. transform.translate(xOffset / 1000f, yOffset / 1000f);
  257. pdfInfo.pdfState.setTransform(transform);
  258. graphics.setPDFState(pdfInfo.pdfState);
  259. graphics.setOutputStream(pdfInfo.outputStream);
  260. try {
  261. root.paint(graphics);
  262. pdfInfo.currentStream.add(graphics.getString());
  263. } catch (Exception e) {
  264. context.getUserAgent().getLogger().error("svg graphic could not be rendered: "
  265. + e.getMessage(), e);
  266. }
  267. pdfInfo.currentStream.add("Q\n");
  268. pdfInfo.pdfState.pop();
  269. }
  270. }
  271. }