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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  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 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.fonts.FontInfo;
  31. // Commons-Logging
  32. import org.apache.commons.logging.Log;
  33. import org.apache.commons.logging.LogFactory;
  34. /* org.w3c.dom.Document is not imported to avoid conflict with
  35. org.apache.fop.apps.Document */
  36. import java.io.OutputStream;
  37. import org.apache.batik.bridge.GVTBuilder;
  38. import org.apache.batik.bridge.BridgeContext;
  39. import org.apache.batik.bridge.ViewBox;
  40. import org.apache.batik.dom.svg.SVGDOMImplementation;
  41. import org.apache.batik.gvt.GraphicsNode;
  42. import org.w3c.dom.svg.SVGDocument;
  43. import org.w3c.dom.svg.SVGSVGElement;
  44. import java.awt.Color;
  45. import java.awt.geom.AffineTransform;
  46. /**
  47. * PDF XML handler.
  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 PDFXMLHandler implements XMLHandler {
  53. /**
  54. * logging instance
  55. */
  56. private Log log = LogFactory.getLog(PDFXMLHandler.class);
  57. /**
  58. * The PDF document that is being drawn into.
  59. */
  60. public static final String PDF_DOCUMENT = "pdfDoc";
  61. /**
  62. * The output stream that the document is being sent to.
  63. */
  64. public static final String OUTPUT_STREAM = "outputStream";
  65. /**
  66. * The current pdf state.
  67. */
  68. public static final String PDF_STATE = "pdfState";
  69. /**
  70. * The current PDF page for page renference and as a resource context.
  71. */
  72. public static final String PDF_PAGE = "pdfPage";
  73. /**
  74. * The current PDF page for page renference and as a resource context.
  75. */
  76. public static final String PDF_CONTEXT = "pdfContext";
  77. /**
  78. * The current PDF stream to draw directly to.
  79. */
  80. public static final String PDF_STREAM = "pdfStream";
  81. /**
  82. * The width of the current pdf page.
  83. */
  84. public static final String PDF_WIDTH = "width";
  85. /**
  86. * The height of the current pdf page.
  87. */
  88. public static final String PDF_HEIGHT = "height";
  89. /**
  90. * The current font information for the pdf renderer.
  91. */
  92. public static final String PDF_FONT_INFO = "fontInfo";
  93. /**
  94. * The current pdf font name.
  95. */
  96. public static final String PDF_FONT_NAME = "fontName";
  97. /**
  98. * The current pdf font size.
  99. */
  100. public static final String PDF_FONT_SIZE = "fontSize";
  101. /**
  102. * The x position that this is being drawn at.
  103. */
  104. public static final String PDF_XPOS = "xpos";
  105. /**
  106. * The y position that this is being drawn at.
  107. */
  108. public static final String PDF_YPOS = "ypos";
  109. /**
  110. * Create a new PDF XML handler for use by the PDF renderer.
  111. */
  112. public PDFXMLHandler() {
  113. }
  114. /** @see org.apache.fop.render.XMLHandler */
  115. public void handleXML(RendererContext context,
  116. org.w3c.dom.Document doc, String ns) throws Exception {
  117. PDFInfo pdfi = getPDFInfo(context);
  118. String svg = "http://www.w3.org/2000/svg";
  119. if (svg.equals(ns)) {
  120. SVGHandler svghandler = new SVGHandler();
  121. svghandler.renderSVGDocument(context, doc, pdfi);
  122. } else {
  123. }
  124. }
  125. /**
  126. * Get the pdf information from the render context.
  127. *
  128. * @param context the renderer context
  129. * @return the pdf information retrieved from the context
  130. */
  131. public static PDFInfo getPDFInfo(RendererContext context) {
  132. PDFInfo pdfi = new PDFInfo();
  133. pdfi.pdfDoc = (PDFDocument)context.getProperty(PDF_DOCUMENT);
  134. pdfi.outputStream = (OutputStream)context.getProperty(OUTPUT_STREAM);
  135. pdfi.pdfState = (PDFState)context.getProperty(PDF_STATE);
  136. pdfi.pdfPage = (PDFPage)context.getProperty(PDF_PAGE);
  137. pdfi.pdfContext = (PDFResourceContext)context.getProperty(PDF_CONTEXT);
  138. pdfi.currentStream = (PDFStream)context.getProperty(PDF_STREAM);
  139. pdfi.width = ((Integer)context.getProperty(PDF_WIDTH)).intValue();
  140. pdfi.height = ((Integer)context.getProperty(PDF_HEIGHT)).intValue();
  141. pdfi.fi = (FontInfo) context.getProperty(PDF_FONT_INFO);
  142. pdfi.currentFontName = (String)context.getProperty(PDF_FONT_NAME);
  143. pdfi.currentFontSize = ((Integer)context.getProperty(PDF_FONT_SIZE)).intValue();
  144. pdfi.currentXPosition = ((Integer)context.getProperty(PDF_XPOS)).intValue();
  145. pdfi.currentYPosition = ((Integer)context.getProperty(PDF_YPOS)).intValue();
  146. return pdfi;
  147. }
  148. /**
  149. * PDF information structure for drawing the XML document.
  150. */
  151. public static class PDFInfo {
  152. /** see PDF_DOCUMENT */
  153. public PDFDocument pdfDoc;
  154. /** see OUTPUT_STREAM */
  155. public OutputStream outputStream;
  156. /** see PDF_STATE */
  157. public PDFState pdfState;
  158. /** see PDF_PAGE */
  159. public PDFPage pdfPage;
  160. /** see PDF_CONTEXT */
  161. public PDFResourceContext pdfContext;
  162. /** see PDF_STREAM */
  163. public PDFStream currentStream;
  164. /** see PDF_WIDTH */
  165. public int width;
  166. /** see PDF_HEIGHT */
  167. public int height;
  168. /** see PDF_FONT_INFO */
  169. public FontInfo fi;
  170. /** see PDF_FONT_NAME */
  171. public String currentFontName;
  172. /** see PDF_FONT_SIZE */
  173. public int currentFontSize;
  174. /** see PDF_XPOS */
  175. public int currentXPosition;
  176. /** see PDF_YPOS */
  177. public int currentYPosition;
  178. }
  179. /**
  180. * This method is placed in an inner class so that we don't get class
  181. * loading errors if batik is not present.
  182. */
  183. protected class SVGHandler {
  184. /**
  185. * Render the svg document.
  186. * @param context the renderer context
  187. * @param doc the svg document
  188. * @param pdfInfo the pdf information of the current context
  189. */
  190. protected void renderSVGDocument(RendererContext context,
  191. org.w3c.dom.Document doc, PDFInfo pdfInfo) {
  192. int xOffset = pdfInfo.currentXPosition;
  193. int yOffset = pdfInfo.currentYPosition;
  194. SVGUserAgent ua
  195. = new SVGUserAgent(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. log.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. PDFRenderer renderer = (PDFRenderer)context.getRenderer();
  228. renderer.saveGraphicsState();
  229. //pdfInfo.currentStream.add("q\n");
  230. renderer.setColor(Color.BLACK, false, null);
  231. renderer.setColor(Color.BLACK, true, null);
  232. // transform so that the coordinates (0,0) is from the top left
  233. // and positive is down and to the right. (0,0) is where the
  234. // viewBox puts it.
  235. pdfInfo.currentStream.add(sx + " 0 0 " + sy + " " + xOffset / 1000f + " "
  236. + yOffset / 1000f + " cm\n");
  237. SVGSVGElement svg = ((SVGDocument)doc).getRootElement();
  238. AffineTransform at = ViewBox.getPreserveAspectRatioTransform(svg, w / 1000f, h / 1000f);
  239. if (false && !at.isIdentity()) {
  240. double[] vals = new double[6];
  241. at.getMatrix(vals);
  242. pdfInfo.currentStream.add(PDFNumber.doubleOut(vals[0], 5) + " "
  243. + PDFNumber.doubleOut(vals[1], 5) + " "
  244. + PDFNumber.doubleOut(vals[2], 5) + " "
  245. + PDFNumber.doubleOut(vals[3], 5) + " "
  246. + PDFNumber.doubleOut(vals[4]) + " "
  247. + PDFNumber.doubleOut(vals[5]) + " cm\n");
  248. }
  249. if (pdfInfo.pdfContext == null) {
  250. pdfInfo.pdfContext = pdfInfo.pdfPage;
  251. }
  252. PDFGraphics2D graphics = new PDFGraphics2D(true, pdfInfo.fi, pdfInfo.pdfDoc,
  253. pdfInfo.pdfContext, pdfInfo.pdfPage.referencePDF(),
  254. pdfInfo.currentFontName,
  255. pdfInfo.currentFontSize);
  256. graphics.setGraphicContext(new org.apache.batik.ext.awt.g2d.GraphicContext());
  257. pdfInfo.pdfState.push();
  258. transform = new AffineTransform();
  259. // scale to viewbox
  260. transform.translate(xOffset / 1000f, yOffset / 1000f);
  261. pdfInfo.pdfState.setTransform(transform);
  262. graphics.setPDFState(pdfInfo.pdfState);
  263. graphics.setOutputStream(pdfInfo.outputStream);
  264. try {
  265. root.paint(graphics);
  266. pdfInfo.currentStream.add(graphics.getString());
  267. } catch (Exception e) {
  268. log.error("svg graphic could not be rendered: "
  269. + e.getMessage(), e);
  270. }
  271. //pdfInfo.currentStream.add("Q\n");
  272. renderer.restoreGraphicsState();
  273. pdfInfo.pdfState.pop();
  274. }
  275. }
  276. /** @see org.apache.fop.render.XMLHandler#getMimeType() */
  277. public String getMimeType() {
  278. return PDFRenderer.MIME_TYPE;
  279. }
  280. /** @see org.apache.fop.render.XMLHandler#getNamespace() */
  281. public String getNamespace() {
  282. return SVGDOMImplementation.SVG_NAMESPACE_URI;
  283. }
  284. }