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.

SVGRenderer.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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.svg;
  18. import org.apache.fop.apps.FOPException;
  19. import org.apache.fop.area.CTM;
  20. import org.apache.fop.area.PageViewport;
  21. import org.apache.fop.area.LineArea;
  22. import org.apache.fop.area.inline.ForeignObject;
  23. import org.apache.fop.area.inline.Leader;
  24. import org.apache.fop.area.inline.TextArea;
  25. import org.apache.fop.svg.SVGUtilities;
  26. import org.apache.fop.fonts.FontInfo;
  27. import org.apache.fop.apps.FOUserAgent;
  28. /* org.w3c.dom.Document is not imported to avoid conflict with
  29. org.apache.fop.control.Document */
  30. import org.w3c.dom.DOMImplementation;
  31. import org.w3c.dom.Document;
  32. import org.w3c.dom.Element;
  33. import org.w3c.dom.Text;
  34. import org.apache.batik.dom.svg.SVGDOMImplementation;
  35. import org.apache.batik.transcoder.svg2svg.SVGTranscoder;
  36. import org.apache.batik.transcoder.TranscoderInput;
  37. import org.apache.batik.transcoder.TranscoderOutput;
  38. import org.apache.batik.transcoder.TranscoderException;
  39. import java.awt.Color;
  40. import java.awt.image.BufferedImage;
  41. import java.awt.geom.Rectangle2D;
  42. import java.util.HashMap;
  43. import java.io.OutputStream;
  44. import java.io.IOException;
  45. import java.io.OutputStreamWriter;
  46. import org.apache.fop.render.AbstractRenderer;
  47. import org.apache.fop.render.XMLHandler;
  48. import org.apache.fop.render.RendererContext;
  49. /**
  50. * This is the SVG renderer.
  51. */
  52. public class SVGRenderer extends AbstractRenderer {
  53. /** SVG MIME type */
  54. public static final String SVG_MIME_TYPE = "image/svg+xml";
  55. /** SVG namespace */
  56. public static final String SVG_NAMESPACE = SVGDOMImplementation.SVG_NAMESPACE_URI;
  57. private org.w3c.dom.Document svgDocument;
  58. private Element svgRoot;
  59. private Element currentPageG = null;
  60. private Element lastLink = null;
  61. private String lastViewbox = null;
  62. private Element docDefs = null;
  63. private Element pageDefs = null;
  64. private Element pagesGroup = null;
  65. // first sequence title
  66. private LineArea docTitle = null;
  67. private OutputStream ostream;
  68. private float totalWidth = 0;
  69. private float totalHeight = 0;
  70. private float sequenceWidth = 0;
  71. private float sequenceHeight = 0;
  72. private float pageWidth = 0;
  73. private float pageHeight = 0;
  74. private int pageNumber = 0;
  75. private HashMap fontNames = new HashMap();
  76. private HashMap fontStyles = new HashMap();
  77. private Color saveColor = null;
  78. /**
  79. * The current (internal) font name
  80. */
  81. private String currentFontName;
  82. /**
  83. * The current font size in millipoints
  84. */
  85. private int currentFontSize;
  86. /**
  87. * The current colour's red, green and blue component
  88. */
  89. private float currentRed = 0;
  90. private float currentGreen = 0;
  91. private float currentBlue = 0;
  92. /**
  93. * Creates a new SVG renderer.
  94. */
  95. public SVGRenderer() {
  96. }
  97. /**
  98. * @see org.apache.fop.render.Renderer#setUserAgent(FOUserAgent)
  99. */
  100. public void setUserAgent(FOUserAgent agent) {
  101. super.setUserAgent(agent);
  102. //Note: This is done here as having two service lookup files in the same IDE project
  103. //will end up with one overwriting the other when all sources are compiled in to the
  104. //same target directory. Remove this code and add an entry in the XMLHandler resource
  105. //file when this renderer exits the sandbox.
  106. XMLHandler handler = agent.getXMLHandlerRegistry().getXMLHandler(this, SVG_NAMESPACE);
  107. if (handler == null) {
  108. agent.getXMLHandlerRegistry().addXMLHandler("org.apache.fop.render.svg.SVGSVGHandler");
  109. }
  110. }
  111. /**
  112. * @see org.apache.fop.render.Renderer#setupFontInfo(FontInfo)
  113. */
  114. public void setupFontInfo(FontInfo fontInfo) {
  115. // create a temp Image to test font metrics on
  116. BufferedImage fontImage =
  117. new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
  118. org.apache.fop.render.java2d.FontSetup.setup(fontInfo,
  119. fontImage.createGraphics());
  120. }
  121. /**
  122. * @see org.apache.fop.render.Renderer#startRenderer(OutputStream)
  123. */
  124. public void startRenderer(OutputStream outputStream)
  125. throws IOException {
  126. ostream = outputStream;
  127. DOMImplementation impl = SVGDOMImplementation.getDOMImplementation();
  128. svgDocument = impl.createDocument(SVG_NAMESPACE, "svg", null);
  129. svgRoot = svgDocument.getDocumentElement();
  130. /*
  131. ProcessingInstruction pi =
  132. svgDocument.createProcessingInstruction("xml",
  133. " version=\"1.0\" encoding=\"ISO-8859-1\"");
  134. svgDocument.insertBefore(pi, svgRoot);
  135. */
  136. docDefs = svgDocument.createElementNS(SVG_NAMESPACE, "defs");
  137. svgRoot.appendChild(docDefs);
  138. pagesGroup = svgDocument.createElementNS(SVG_NAMESPACE, "g");
  139. pageDefs = svgDocument.createElementNS(SVG_NAMESPACE, "defs");
  140. pagesGroup.appendChild(pageDefs);
  141. svgRoot.appendChild(pagesGroup);
  142. }
  143. /**
  144. * @see org.apache.fop.render.Renderer#stopRenderer()
  145. */
  146. public void stopRenderer() throws IOException {
  147. totalWidth += sequenceWidth;
  148. if (sequenceHeight > totalHeight) {
  149. totalHeight = sequenceHeight;
  150. }
  151. svgRoot.setAttributeNS(null, "width", "" + (totalWidth + 1));
  152. svgRoot.setAttributeNS(null, "height", "" + (totalHeight + 1));
  153. //svgRoot.setAttributeNS(null, "viewBox", "0 0 " + pageWidth + " " + pageHeight);
  154. SVGTranscoder svgT = new SVGTranscoder();
  155. TranscoderInput input = new TranscoderInput(svgDocument);
  156. TranscoderOutput output =
  157. new TranscoderOutput(new OutputStreamWriter(ostream));
  158. try {
  159. svgT.transcode(input, output);
  160. } catch (TranscoderException e) {
  161. log.error("could not write svg file :" + e.getMessage(), e);
  162. }
  163. ostream.flush();
  164. ostream = null;
  165. svgDocument = null;
  166. svgRoot = null;
  167. currentPageG = null;
  168. lastLink = null;
  169. totalWidth = 0;
  170. totalHeight = 0;
  171. pageNumber = 0;
  172. }
  173. /**
  174. * @see org.apache.fop.render.Renderer#startPageSequence(LineArea)
  175. */
  176. public void startPageSequence(LineArea seqTitle) {
  177. totalWidth += sequenceWidth;
  178. if (sequenceHeight > totalHeight) {
  179. totalHeight = sequenceHeight;
  180. }
  181. sequenceWidth = 0;
  182. sequenceHeight = 0;
  183. if (seqTitle != null && docTitle == null) {
  184. // convert first title to a string and set for svg document title
  185. docTitle = seqTitle;
  186. String str = convertTitleToString(seqTitle);
  187. Element svgTitle = svgDocument.createElementNS(SVG_NAMESPACE, "title");
  188. Text strNode = svgDocument.createTextNode(str);
  189. svgTitle.appendChild(strNode);
  190. svgRoot.insertBefore(svgTitle, svgRoot.getFirstChild());
  191. }
  192. }
  193. /**
  194. * @see org.apache.fop.render.Renderer#renderPage(PageViewport)
  195. */
  196. public void renderPage(PageViewport page) throws IOException, FOPException {
  197. float lastWidth = pageWidth;
  198. float lastHeight = pageHeight;
  199. Rectangle2D area = page.getViewArea();
  200. pageWidth = (float) area.getWidth() / 1000f;
  201. pageHeight = (float) area.getHeight() / 1000f;
  202. // if there is a link from the last page
  203. if (lastLink != null) {
  204. lastLink.setAttributeNS(null, "xlink:href", "#svgView(viewBox("
  205. + totalWidth + ", "
  206. + sequenceHeight + ", "
  207. + pageWidth + ", "
  208. + pageHeight + "))");
  209. pagesGroup.appendChild(lastLink);
  210. }
  211. currentPageG = svgDocument.createElementNS(SVG_NAMESPACE, "svg");
  212. currentPageG.setAttributeNS(null, "viewbox",
  213. "0 0 " + (int) pageWidth + " " + (int) pageHeight);
  214. currentPageG.setAttributeNS(null, "width",
  215. "" + ((int) pageWidth + 1));
  216. currentPageG.setAttributeNS(null, "height",
  217. "" + ((int) pageHeight + 1));
  218. currentPageG.setAttributeNS(null, "id", "Page-" + pageNumber);
  219. currentPageG.setAttributeNS(null, "style", "font-family:sanserif;font-size:12");
  220. pageDefs.appendChild(currentPageG);
  221. if (pageWidth > sequenceWidth) {
  222. sequenceWidth = pageWidth;
  223. }
  224. sequenceHeight += pageHeight;
  225. Element border =
  226. SVGUtilities.createRect(svgDocument, 0, 0, pageWidth,
  227. pageHeight);
  228. border.setAttributeNS(null, "style", "fill:none;stroke:black");
  229. currentPageG.appendChild(border);
  230. // render the page contents
  231. super.renderPage(page);
  232. Element use = svgDocument.createElementNS(SVG_NAMESPACE, "use");
  233. use.setAttributeNS(null, "xlink:href", "#Page-" + pageNumber);
  234. use.setAttributeNS(null, "x", "" + totalWidth);
  235. use.setAttributeNS(null, "y", "" + (sequenceHeight - pageHeight));
  236. pagesGroup.appendChild(use);
  237. Element lastPageLink = svgDocument.createElementNS(SVG_NAMESPACE, "a");
  238. if (lastLink != null) {
  239. lastPageLink.setAttributeNS(null, "xlink:href", lastViewbox);
  240. } else {
  241. lastPageLink.setAttributeNS(null, "xlink:href",
  242. "#svgView(viewBox("
  243. + totalWidth + ", "
  244. + (sequenceHeight - pageHeight) + ", "
  245. + pageWidth + ", "
  246. + pageHeight + "))");
  247. }
  248. pagesGroup.appendChild(lastPageLink);
  249. // setup a link to the next page, only added when the
  250. // next page is rendered
  251. Element rect = SVGUtilities.createRect(svgDocument, totalWidth,
  252. (sequenceHeight - pageHeight), pageWidth / 2, pageHeight);
  253. rect.setAttributeNS(null, "style", "fill:blue;visibility:hidden");
  254. lastPageLink.appendChild(rect);
  255. lastLink = svgDocument.createElementNS(SVG_NAMESPACE, "a");
  256. rect = SVGUtilities.createRect(svgDocument,
  257. totalWidth + pageWidth / 2,
  258. (sequenceHeight - pageHeight), pageWidth / 2, pageHeight);
  259. rect.setAttributeNS(null, "style", "fill:blue;visibility:hidden");
  260. lastLink.appendChild(rect);
  261. lastViewbox = "#svgView(viewBox("
  262. + totalWidth + ", "
  263. + (sequenceHeight - pageHeight) + ", "
  264. + pageWidth + ", "
  265. + pageHeight + "))";
  266. pageNumber++;
  267. }
  268. /**
  269. * Method renderForeignObject.
  270. * @param fo the foreign object
  271. */
  272. public void renderForeignObject(ForeignObject fo, Rectangle2D pos) {
  273. org.w3c.dom.Document doc = fo.getDocument();
  274. String ns = fo.getNameSpace();
  275. renderDocument(doc, ns, pos);
  276. }
  277. /**
  278. * Renders an XML document (SVG for example).
  279. *
  280. * @param doc DOM document representing the XML document
  281. * @param ns Namespace for the document
  282. * @param pos Position on the page
  283. */
  284. public void renderDocument(Document doc, String ns, Rectangle2D pos) {
  285. RendererContext context;
  286. context = new RendererContext(this, getMimeType());
  287. context.setUserAgent(userAgent);
  288. context.setProperty(SVGRendererContextConstants.SVG_DOCUMENT, svgDocument);
  289. context.setProperty(SVGRendererContextConstants.SVG_PAGE_G, currentPageG);
  290. context.setProperty(SVGRendererContextConstants.XPOS,
  291. new Integer(currentIPPosition + (int)pos.getX()));
  292. context.setProperty(SVGRendererContextConstants.YPOS,
  293. new Integer(currentBPPosition + (int)pos.getY()));
  294. context.setProperty(SVGRendererContextConstants.WIDTH,
  295. new Integer((int)pos.getWidth()));
  296. context.setProperty(SVGRendererContextConstants.HEIGHT,
  297. new Integer((int) pos.getHeight()));
  298. renderXML(context, doc, ns);
  299. }
  300. /**
  301. * @see org.apache.fop.render.AbstractRenderer#renderLeader(Leader)
  302. */
  303. public void renderLeader(Leader area) {
  304. String style = "stroke:black;stroke-width:"
  305. + (area.getRuleThickness() / 1000) + ";";
  306. switch (area.getRuleStyle()) {
  307. case EN_DOTTED:
  308. style += "stroke-dasharray:1,1";
  309. break;
  310. case EN_DASHED:
  311. style += "stroke-dasharray:5,1";
  312. break;
  313. case EN_SOLID:
  314. break;
  315. case EN_DOUBLE:
  316. break;
  317. case EN_GROOVE:
  318. break;
  319. case EN_RIDGE:
  320. break;
  321. }
  322. Element line = SVGUtilities.createLine(svgDocument,
  323. currentIPPosition / 1000,
  324. (currentBPPosition + area.getOffset()
  325. - area.getRuleThickness() / 2) / 1000,
  326. (currentIPPosition + area.getIPD()) / 1000,
  327. (currentBPPosition + area.getOffset()
  328. - area.getRuleThickness() / 2) / 1000);
  329. line.setAttributeNS(null, "style", style);
  330. currentPageG.appendChild(line);
  331. super.renderLeader(area);
  332. }
  333. /**
  334. * @see org.apache.fop.render.AbstractRenderer#renderText(TextArea)
  335. */
  336. public void renderText(TextArea text) {
  337. Element textElement = SVGUtilities.createText(svgDocument,
  338. currentIPPosition / 1000,
  339. (currentBPPosition + text.getOffset()
  340. + text.getBaselineOffset()) / 1000,
  341. text.getText());
  342. currentPageG.appendChild(textElement);
  343. super.renderText(text);
  344. }
  345. /**
  346. * @see org.apache.fop.render.AbstractRenderer#renderCharacter(Character)
  347. */
  348. public void renderCharacter(org.apache.fop.area.inline.Character ch) {
  349. Element text = SVGUtilities.createText(svgDocument,
  350. currentIPPosition / 1000,
  351. (currentBPPosition + ch.getOffset()
  352. + ch.getBaselineOffset()) / 1000,
  353. "" + ch.getChar());
  354. currentPageG.appendChild(text);
  355. super.renderCharacter(ch);
  356. }
  357. /** @see org.apache.fop.render.AbstractRenderer */
  358. public String getMimeType() {
  359. return SVG_MIME_TYPE;
  360. }
  361. /**
  362. * @see org.apache.fop.render.AbstractRenderer#startVParea(CTM, Rectangle2D)
  363. */
  364. protected void startVParea(CTM ctm, Rectangle2D clippingRect) {
  365. // TODO Auto-generated method stub
  366. }
  367. /**
  368. * @see org.apache.fop.render.AbstractRenderer#endVParea()
  369. */
  370. protected void endVParea() {
  371. // TODO Auto-generated method stub
  372. }
  373. }