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.

PSSVGHandler.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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.ps;
  18. // Java
  19. import java.awt.geom.AffineTransform;
  20. import java.io.IOException;
  21. // DOM
  22. import org.w3c.dom.Document;
  23. import org.w3c.dom.svg.SVGDocument;
  24. import org.w3c.dom.svg.SVGSVGElement;
  25. // Batik
  26. import org.apache.avalon.framework.configuration.Configuration;
  27. import org.apache.batik.bridge.GVTBuilder;
  28. import org.apache.batik.bridge.BridgeContext;
  29. import org.apache.batik.bridge.ViewBox;
  30. import org.apache.batik.dom.svg.SVGDOMImplementation;
  31. import org.apache.batik.gvt.GraphicsNode;
  32. // FOP
  33. import org.apache.fop.fonts.FontInfo;
  34. import org.apache.fop.render.Renderer;
  35. import org.apache.fop.render.XMLHandler;
  36. import org.apache.fop.render.RendererContext;
  37. import org.apache.fop.svg.SVGUserAgent;
  38. // Commons-Logging
  39. import org.apache.commons.logging.Log;
  40. import org.apache.commons.logging.LogFactory;
  41. /**
  42. * PostScript XML handler for SVG. Uses Apache Batik for SVG processing.
  43. * This handler handles XML for foreign objects when rendering to PostScript.
  44. * It renders SVG to the PostScript document using the PSGraphics2D.
  45. * The properties from the PostScript renderer are subject to change.
  46. *
  47. * @version $Id$
  48. */
  49. public class PSSVGHandler implements XMLHandler, PSRendererContextConstants {
  50. /** logging instance */
  51. private static Log log = LogFactory.getLog(PSSVGHandler.class);
  52. /**
  53. * Create a new PostScript XML handler for use by the PostScript renderer.
  54. */
  55. public PSSVGHandler() {
  56. }
  57. /** @see org.apache.fop.render.XMLHandler */
  58. public void handleXML(RendererContext context,
  59. Document doc, String ns) throws Exception {
  60. PSInfo psi = getPSInfo(context);
  61. if (SVGDOMImplementation.SVG_NAMESPACE_URI.equals(ns)) {
  62. renderSVGDocument(context, doc, psi);
  63. }
  64. }
  65. /**
  66. * Get the pdf information from the render context.
  67. *
  68. * @param context the renderer context
  69. * @return the pdf information retrieved from the context
  70. */
  71. public static PSInfo getPSInfo(RendererContext context) {
  72. PSInfo psi = new PSInfo();
  73. psi.psGenerator = (PSGenerator)context.getProperty(PS_GENERATOR);
  74. psi.fontInfo = (org.apache.fop.fonts.FontInfo) context.getProperty(PS_FONT_INFO);
  75. psi.width = ((Integer)context.getProperty(WIDTH)).intValue();
  76. psi.height = ((Integer)context.getProperty(HEIGHT)).intValue();
  77. psi.currentXPosition = ((Integer)context.getProperty(XPOS)).intValue();
  78. psi.currentYPosition = ((Integer)context.getProperty(YPOS)).intValue();
  79. psi.cfg = (Configuration)context.getProperty(HANDLER_CONFIGURATION);
  80. return psi;
  81. }
  82. /**
  83. * PostScript information structure for drawing the XML document.
  84. */
  85. public static class PSInfo {
  86. /** see PS_GENERATOR */
  87. private PSGenerator psGenerator;
  88. /** see PS_FONT_INFO */
  89. private org.apache.fop.fonts.FontInfo fontInfo;
  90. /** see WIDTH */
  91. private int width;
  92. /** see HEIGHT */
  93. private int height;
  94. /** see XPOS */
  95. private int currentXPosition;
  96. /** see YPOS */
  97. private int currentYPosition;
  98. /** see HANDLER_CONFIGURATION */
  99. private Configuration cfg;
  100. /**
  101. * Returns the PSGenerator.
  102. * @return PSGenerator
  103. */
  104. public PSGenerator getPSGenerator() {
  105. return psGenerator;
  106. }
  107. /**
  108. * Sets the PSGenerator.
  109. * @param psGenerator The PSGenerator to set
  110. */
  111. public void setPsGenerator(PSGenerator psGenerator) {
  112. this.psGenerator = psGenerator;
  113. }
  114. /**
  115. * Returns the fontInfo.
  116. * @return FontInfo
  117. */
  118. public FontInfo getFontInfo() {
  119. return fontInfo;
  120. }
  121. /**
  122. * Sets the fontInfo.
  123. * @param fontInfo The fontInfo to set
  124. */
  125. public void setFontInfo(FontInfo fontInfo) {
  126. this.fontInfo = fontInfo;
  127. }
  128. /**
  129. * Returns the currentXPosition.
  130. * @return int
  131. */
  132. public int getCurrentXPosition() {
  133. return currentXPosition;
  134. }
  135. /**
  136. * Sets the currentXPosition.
  137. * @param currentXPosition The currentXPosition to set
  138. */
  139. public void setCurrentXPosition(int currentXPosition) {
  140. this.currentXPosition = currentXPosition;
  141. }
  142. /**
  143. * Returns the currentYPosition.
  144. * @return int
  145. */
  146. public int getCurrentYPosition() {
  147. return currentYPosition;
  148. }
  149. /**
  150. * Sets the currentYPosition.
  151. * @param currentYPosition The currentYPosition to set
  152. */
  153. public void setCurrentYPosition(int currentYPosition) {
  154. this.currentYPosition = currentYPosition;
  155. }
  156. /**
  157. * Returns the width.
  158. * @return int
  159. */
  160. public int getWidth() {
  161. return width;
  162. }
  163. /**
  164. * Sets the width.
  165. * @param width The pageWidth to set
  166. */
  167. public void setWidth(int width) {
  168. this.width = width;
  169. }
  170. /**
  171. * Returns the height.
  172. * @return int
  173. */
  174. public int getHeight() {
  175. return height;
  176. }
  177. /**
  178. * Sets the height.
  179. * @param height The height to set
  180. */
  181. public void setHeight(int height) {
  182. this.height = height;
  183. }
  184. /**
  185. * Returns the height.
  186. * @return int
  187. */
  188. public Configuration getHandlerConfiguration() {
  189. return this.cfg;
  190. }
  191. /**
  192. * Sets the handler configuration.
  193. * @param cfg the configuration object
  194. */
  195. public void setHeight(Configuration cfg) {
  196. this.cfg = cfg;
  197. }
  198. }
  199. /**
  200. * Render the svg document.
  201. * @param context the renderer context
  202. * @param doc the svg document
  203. * @param psInfo the pdf information of the current context
  204. */
  205. protected void renderSVGDocument(RendererContext context,
  206. Document doc, PSInfo psInfo) {
  207. int xOffset = psInfo.currentXPosition;
  208. int yOffset = psInfo.currentYPosition;
  209. PSGenerator gen = psInfo.psGenerator;
  210. //Controls whether text painted by Batik is generated using text or path operations
  211. boolean strokeText = false;
  212. Configuration cfg = psInfo.getHandlerConfiguration();
  213. if (cfg != null) {
  214. strokeText = cfg.getChild("stroke-text", true).getValueAsBoolean(strokeText);
  215. }
  216. SVGUserAgent ua
  217. = new SVGUserAgent(
  218. context.getUserAgent().getSourcePixelUnitToMillimeter(),
  219. new AffineTransform());
  220. GVTBuilder builder = new GVTBuilder();
  221. BridgeContext ctx = new BridgeContext(ua);
  222. if (!strokeText) {
  223. PSTextPainter textPainter = new PSTextPainter(psInfo.getFontInfo());
  224. ctx.setTextPainter(textPainter);
  225. PSTextElementBridge tBridge = new PSTextElementBridge(textPainter);
  226. ctx.putBridge(tBridge);
  227. }
  228. GraphicsNode root;
  229. try {
  230. root = builder.build(ctx, doc);
  231. } catch (Exception e) {
  232. log.error("SVG graphic could not be built: "
  233. + e.getMessage(), e);
  234. return;
  235. }
  236. // get the 'width' and 'height' attributes of the SVG document
  237. float w = (float)ctx.getDocumentSize().getWidth() * 1000f;
  238. float h = (float)ctx.getDocumentSize().getHeight() * 1000f;
  239. float sx = psInfo.getWidth() / (float)w;
  240. float sy = psInfo.getHeight() / (float)h;
  241. ctx = null;
  242. builder = null;
  243. try {
  244. gen.commentln("%FOPBeginSVG");
  245. gen.saveGraphicsState();
  246. /*
  247. * Clip to the svg area.
  248. * Note: To have the svg overlay (under) a text area then use
  249. * an fo:block-container
  250. */
  251. gen.writeln("newpath");
  252. gen.defineRect(xOffset / 1000f, yOffset / 1000f,
  253. psInfo.getWidth() / 1000f, psInfo.getHeight() / 1000f);
  254. gen.writeln("clip");
  255. // transform so that the coordinates (0,0) is from the top left
  256. // and positive is down and to the right. (0,0) is where the
  257. // viewBox puts it.
  258. gen.concatMatrix(sx, 0, 0, sy, xOffset / 1000f, yOffset / 1000f);
  259. SVGSVGElement svg = ((SVGDocument)doc).getRootElement();
  260. AffineTransform at = ViewBox.getPreserveAspectRatioTransform(svg,
  261. psInfo.getWidth() / 1000f, psInfo.getHeight() / 1000f);
  262. /*
  263. if (!at.isIdentity()) {
  264. double[] vals = new double[6];
  265. at.getMatrix(vals);
  266. gen.concatMatrix(vals);
  267. }*/
  268. final boolean textAsShapes = false;
  269. PSGraphics2D graphics = new PSGraphics2D(textAsShapes, gen);
  270. graphics.setGraphicContext(new org.apache.batik.ext.awt.g2d.GraphicContext());
  271. AffineTransform transform = new AffineTransform();
  272. // scale to viewbox
  273. transform.translate(xOffset, yOffset);
  274. gen.getCurrentState().concatMatrix(transform);
  275. try {
  276. root.paint(graphics);
  277. } catch (Exception e) {
  278. log.error("SVG graphic could not be rendered: "
  279. + e.getMessage(), e);
  280. }
  281. gen.restoreGraphicsState();
  282. gen.commentln("%FOPEndSVG");
  283. } catch (IOException ioe) {
  284. log.error("SVG graphic could not be rendered: "
  285. + ioe.getMessage(), ioe);
  286. }
  287. }
  288. /** @see org.apache.fop.render.XMLHandler#supportsRenderer(org.apache.fop.render.Renderer) */
  289. public boolean supportsRenderer(Renderer renderer) {
  290. return (renderer instanceof PSRenderer);
  291. }
  292. /** @see org.apache.fop.render.XMLHandler#getNamespace() */
  293. public String getNamespace() {
  294. return SVGDOMImplementation.SVG_NAMESPACE_URI;
  295. }
  296. }