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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. /*
  2. * $Id: SVGRenderer.java,v 1.10 2003/03/07 09:46:33 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.svg;
  52. import org.apache.fop.apps.FOPException;
  53. import org.apache.fop.area.PageViewport;
  54. import org.apache.fop.area.Title;
  55. import org.apache.fop.area.inline.ForeignObject;
  56. import org.apache.fop.area.inline.Leader;
  57. import org.apache.fop.area.inline.Word;
  58. import org.apache.fop.svg.SVGUtilities;
  59. import org.apache.fop.apps.Document;
  60. import org.apache.fop.apps.FOUserAgent;
  61. import org.apache.fop.fo.properties.RuleStyle;
  62. import org.apache.fop.fo.FOTreeControl;
  63. import org.w3c.dom.Node;
  64. import org.w3c.dom.svg.SVGSVGElement;
  65. import org.w3c.dom.svg.SVGDocument;
  66. /* org.w3c.dom.Document is not imported to avoid conflict with
  67. org.apache.fop.control.Document */
  68. import org.w3c.dom.Element;
  69. import org.w3c.dom.DOMImplementation;
  70. import org.w3c.dom.Text;
  71. import org.apache.batik.dom.svg.SVGDOMImplementation;
  72. import org.apache.batik.dom.util.XMLSupport;
  73. import org.apache.batik.transcoder.svg2svg.SVGTranscoder;
  74. import org.apache.batik.transcoder.TranscoderInput;
  75. import org.apache.batik.transcoder.TranscoderOutput;
  76. import org.apache.batik.transcoder.TranscoderException;
  77. import org.apache.batik.dom.util.DOMUtilities;
  78. import java.awt.Color;
  79. import java.awt.image.BufferedImage;
  80. import java.awt.geom.Rectangle2D;
  81. import java.util.HashMap;
  82. import java.io.OutputStream;
  83. import java.io.IOException;
  84. import java.io.OutputStreamWriter;
  85. import org.apache.fop.render.AbstractRenderer;
  86. import org.apache.fop.render.XMLHandler;
  87. import org.apache.fop.render.RendererContext;
  88. /**
  89. * This is the SVG renderer.
  90. */
  91. public class SVGRenderer extends AbstractRenderer implements XMLHandler {
  92. /** SVG MIME type */
  93. public static final String SVG_MIME_TYPE = "image/svg+xml";
  94. /** SVG namespace */
  95. public static final String SVG_NAMESPACE = SVGDOMImplementation.SVG_NAMESPACE_URI;
  96. private org.w3c.dom.Document svgDocument;
  97. private Element svgRoot;
  98. private Element currentPageG = null;
  99. private Element lastLink = null;
  100. private String lastViewbox = null;
  101. private Element docDefs = null;
  102. private Element pageDefs = null;
  103. private Element pagesGroup = null;
  104. // first sequence title
  105. private Title docTitle = null;
  106. private RendererContext context;
  107. private OutputStream ostream;
  108. private float totalWidth = 0;
  109. private float totalHeight = 0;
  110. private float sequenceWidth = 0;
  111. private float sequenceHeight = 0;
  112. private float pageWidth = 0;
  113. private float pageHeight = 0;
  114. private int pageNumber = 0;
  115. private HashMap fontNames = new HashMap();
  116. private HashMap fontStyles = new HashMap();
  117. private Color saveColor = null;
  118. /**
  119. * The current (internal) font name
  120. */
  121. private String currentFontName;
  122. /**
  123. * The current font size in millipoints
  124. */
  125. private int currentFontSize;
  126. /**
  127. * The current colour's red, green and blue component
  128. */
  129. private float currentRed = 0;
  130. private float currentGreen = 0;
  131. private float currentBlue = 0;
  132. /**
  133. * Creates a new SVG renderer.
  134. */
  135. public SVGRenderer() {
  136. context = new RendererContext(SVG_MIME_TYPE);
  137. }
  138. /**
  139. * @see org.apache.fop.render.Renderer#setUserAgent(FOUserAgent)
  140. */
  141. public void setUserAgent(FOUserAgent agent) {
  142. super.setUserAgent(agent);
  143. userAgent.setDefaultXMLHandler(SVG_MIME_TYPE, this);
  144. userAgent.addXMLHandler(SVG_MIME_TYPE, SVG_NAMESPACE, this);
  145. }
  146. /**
  147. * @see org.apache.fop.render.Renderer#setupFontInfo(FontInfo)
  148. */
  149. public void setupFontInfo(FOTreeControl foTreeControl) {
  150. // create a temp Image to test font metrics on
  151. BufferedImage fontImage =
  152. new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
  153. org.apache.fop.render.awt.FontSetup.setup((Document)foTreeControl,
  154. fontImage.createGraphics());
  155. }
  156. /**
  157. * @see org.apache.fop.render.Renderer#startRenderer(OutputStream)
  158. */
  159. public void startRenderer(OutputStream outputStream)
  160. throws IOException {
  161. ostream = outputStream;
  162. DOMImplementation impl = SVGDOMImplementation.getDOMImplementation();
  163. svgDocument = impl.createDocument(SVG_NAMESPACE, "svg", null);
  164. svgRoot = svgDocument.getDocumentElement();
  165. /*
  166. ProcessingInstruction pi =
  167. svgDocument.createProcessingInstruction("xml",
  168. " version=\"1.0\" encoding=\"ISO-8859-1\"");
  169. svgDocument.insertBefore(pi, svgRoot);
  170. */
  171. docDefs = svgDocument.createElementNS(SVG_NAMESPACE, "defs");
  172. svgRoot.appendChild(docDefs);
  173. pagesGroup = svgDocument.createElementNS(SVG_NAMESPACE, "g");
  174. pageDefs = svgDocument.createElementNS(SVG_NAMESPACE, "defs");
  175. pagesGroup.appendChild(pageDefs);
  176. svgRoot.appendChild(pagesGroup);
  177. }
  178. /**
  179. * @see org.apache.fop.render.Renderer#stopRenderer()
  180. */
  181. public void stopRenderer() throws IOException {
  182. totalWidth += sequenceWidth;
  183. if (sequenceHeight > totalHeight) {
  184. totalHeight = sequenceHeight;
  185. }
  186. svgRoot.setAttributeNS(null, "width", "" + (totalWidth + 1));
  187. svgRoot.setAttributeNS(null, "height", "" + (totalHeight + 1));
  188. //svgRoot.setAttributeNS(null, "viewBox", "0 0 " + pageWidth + " " + pageHeight);
  189. SVGTranscoder svgT = new SVGTranscoder();
  190. TranscoderInput input = new TranscoderInput(svgDocument);
  191. TranscoderOutput output =
  192. new TranscoderOutput(new OutputStreamWriter(ostream));
  193. try {
  194. svgT.transcode(input, output);
  195. } catch (TranscoderException e) {
  196. getLogger().error("could not write svg file :" + e.getMessage(), e);
  197. }
  198. ostream.flush();
  199. ostream = null;
  200. svgDocument = null;
  201. svgRoot = null;
  202. currentPageG = null;
  203. lastLink = null;
  204. totalWidth = 0;
  205. totalHeight = 0;
  206. pageNumber = 0;
  207. }
  208. /**
  209. * @see org.apache.fop.render.Renderer#startPageSequence(Title)
  210. */
  211. public void startPageSequence(Title seqTitle) {
  212. totalWidth += sequenceWidth;
  213. if (sequenceHeight > totalHeight) {
  214. totalHeight = sequenceHeight;
  215. }
  216. sequenceWidth = 0;
  217. sequenceHeight = 0;
  218. if (seqTitle != null && docTitle == null) {
  219. // convert first title to a string and set for svg document title
  220. docTitle = seqTitle;
  221. String str = convertTitleToString(seqTitle);
  222. Element svgTitle = svgDocument.createElementNS(SVG_NAMESPACE, "title");
  223. Text strNode = svgDocument.createTextNode(str);
  224. svgTitle.appendChild(strNode);
  225. svgRoot.insertBefore(svgTitle, svgRoot.getFirstChild());
  226. }
  227. }
  228. /**
  229. * @see org.apache.fop.render.Renderer#renderPage(PageViewport)
  230. */
  231. public void renderPage(PageViewport page) throws IOException, FOPException {
  232. float lastWidth = pageWidth;
  233. float lastHeight = pageHeight;
  234. Rectangle2D area = page.getViewArea();
  235. pageWidth = (float) area.getWidth() / 1000f;
  236. pageHeight = (float) area.getHeight() / 1000f;
  237. // if there is a link from the last page
  238. if (lastLink != null) {
  239. lastLink.setAttributeNS(null, "xlink:href", "#svgView(viewBox("
  240. + totalWidth + ", "
  241. + sequenceHeight + ", "
  242. + pageWidth + ", "
  243. + pageHeight + "))");
  244. pagesGroup.appendChild(lastLink);
  245. }
  246. currentPageG = svgDocument.createElementNS(SVG_NAMESPACE, "svg");
  247. currentPageG.setAttributeNS(null, "viewbox",
  248. "0 0 " + (int) pageWidth + " " + (int) pageHeight);
  249. currentPageG.setAttributeNS(null, "width",
  250. "" + ((int) pageWidth + 1));
  251. currentPageG.setAttributeNS(null, "height",
  252. "" + ((int) pageHeight + 1));
  253. currentPageG.setAttributeNS(null, "id", "Page-" + pageNumber);
  254. currentPageG.setAttributeNS(null, "style", "font-family:sanserif;font-size:12");
  255. pageDefs.appendChild(currentPageG);
  256. if (pageWidth > sequenceWidth) {
  257. sequenceWidth = pageWidth;
  258. }
  259. sequenceHeight += pageHeight;
  260. Element border =
  261. SVGUtilities.createRect(svgDocument, 0, 0, pageWidth,
  262. pageHeight);
  263. border.setAttributeNS(null, "style", "fill:none;stroke:black");
  264. currentPageG.appendChild(border);
  265. // render the page contents
  266. super.renderPage(page);
  267. Element use = svgDocument.createElementNS(SVG_NAMESPACE, "use");
  268. use.setAttributeNS(null, "xlink:href", "#Page-" + pageNumber);
  269. use.setAttributeNS(null, "x", "" + totalWidth);
  270. use.setAttributeNS(null, "y", "" + (sequenceHeight - pageHeight));
  271. pagesGroup.appendChild(use);
  272. Element lastPageLink = svgDocument.createElementNS(SVG_NAMESPACE, "a");
  273. if (lastLink != null) {
  274. lastPageLink.setAttributeNS(null, "xlink:href", lastViewbox);
  275. } else {
  276. lastPageLink.setAttributeNS(null, "xlink:href",
  277. "#svgView(viewBox("
  278. + totalWidth + ", "
  279. + (sequenceHeight - pageHeight) + ", "
  280. + pageWidth + ", "
  281. + pageHeight + "))");
  282. }
  283. pagesGroup.appendChild(lastPageLink);
  284. // setup a link to the next page, only added when the
  285. // next page is rendered
  286. Element rect = SVGUtilities.createRect(svgDocument, totalWidth,
  287. (sequenceHeight - pageHeight), pageWidth / 2, pageHeight);
  288. rect.setAttributeNS(null, "style", "fill:blue;visibility:hidden");
  289. lastPageLink.appendChild(rect);
  290. lastLink = svgDocument.createElementNS(SVG_NAMESPACE, "a");
  291. rect = SVGUtilities.createRect(svgDocument,
  292. totalWidth + pageWidth / 2,
  293. (sequenceHeight - pageHeight), pageWidth / 2, pageHeight);
  294. rect.setAttributeNS(null, "style", "fill:blue;visibility:hidden");
  295. lastLink.appendChild(rect);
  296. lastViewbox = "#svgView(viewBox("
  297. + totalWidth + ", "
  298. + (sequenceHeight - pageHeight) + ", "
  299. + pageWidth + ", "
  300. + pageHeight + "))";
  301. pageNumber++;
  302. }
  303. /**
  304. * Method renderForeignObject.
  305. * @param fo the foreign object
  306. */
  307. public void renderForeignObject(ForeignObject fo) {
  308. org.w3c.dom.Document doc = fo.getDocument();
  309. String ns = fo.getNameSpace();
  310. userAgent.renderXML(context, doc, ns);
  311. }
  312. /**
  313. * @see org.apache.fop.render.XMLHandler#handleXML(RendererContext, Document, String)
  314. */
  315. public void handleXML(RendererContext context, org.w3c.dom.Document doc,
  316. String ns) throws Exception {
  317. if (SVG_NAMESPACE.equals(ns)) {
  318. if (!(doc instanceof SVGDocument)) {
  319. DOMImplementation impl =
  320. SVGDOMImplementation.getDOMImplementation();
  321. doc = DOMUtilities.deepCloneDocument(doc, impl);
  322. }
  323. SVGSVGElement svg = ((SVGDocument) doc).getRootElement();
  324. Element view = svgDocument.createElementNS(SVG_NAMESPACE, "svg");
  325. Node newsvg = svgDocument.importNode(svg, true);
  326. //view.setAttributeNS(null, "viewBox", "0 0 ");
  327. view.setAttributeNS(null, "x",
  328. "" + currentBlockIPPosition / 1000f);
  329. view.setAttributeNS(null, "y", "" + currentBPPosition / 1000f);
  330. // this fixes a problem where the xmlns is repeated sometimes
  331. Element ele = (Element) newsvg;
  332. ele.setAttributeNS(XMLSupport.XMLNS_NAMESPACE_URI, "xmlns",
  333. SVG_NAMESPACE);
  334. if (ele.hasAttributeNS(null, "xmlns")) {
  335. ele.removeAttributeNS(null, "xmlns");
  336. }
  337. view.appendChild(newsvg);
  338. currentPageG.appendChild(view);
  339. }
  340. }
  341. /**
  342. * @see org.apache.fop.render.Renderer#renderLeader(Leader)
  343. */
  344. public void renderLeader(Leader area) {
  345. String style = "stroke:black;stroke-width:"
  346. + (area.getRuleThickness() / 1000) + ";";
  347. switch (area.getRuleStyle()) {
  348. case RuleStyle.DOTTED:
  349. style += "stroke-dasharray:1,1";
  350. break;
  351. case RuleStyle.DASHED:
  352. style += "stroke-dasharray:5,1";
  353. break;
  354. case RuleStyle.SOLID:
  355. break;
  356. case RuleStyle.DOUBLE:
  357. break;
  358. case RuleStyle.GROOVE:
  359. break;
  360. case RuleStyle.RIDGE:
  361. break;
  362. }
  363. Element line = SVGUtilities.createLine(svgDocument,
  364. currentBlockIPPosition / 1000,
  365. (currentBPPosition + area.getOffset()
  366. - area.getRuleThickness() / 2) / 1000,
  367. (currentBlockIPPosition + area.getWidth()) / 1000,
  368. (currentBPPosition + area.getOffset()
  369. - area.getRuleThickness() / 2) / 1000);
  370. line.setAttributeNS(null, "style", style);
  371. currentPageG.appendChild(line);
  372. super.renderLeader(area);
  373. }
  374. /**
  375. * @see org.apache.fop.render.Renderer#renderWord(Word)
  376. */
  377. public void renderWord(Word word) {
  378. Element text = SVGUtilities.createText(svgDocument,
  379. currentBlockIPPosition / 1000,
  380. (currentBPPosition + word.getOffset()) / 1000,
  381. word.getWord());
  382. currentPageG.appendChild(text);
  383. super.renderWord(word);
  384. }
  385. /**
  386. * @see org.apache.fop.render.Renderer#renderCharacter(Character)
  387. */
  388. public void renderCharacter(org.apache.fop.area.inline.Character ch) {
  389. Element text = SVGUtilities.createText(svgDocument,
  390. currentBlockIPPosition / 1000,
  391. (currentBPPosition + ch.getOffset()) / 1000,
  392. "" + ch.getChar());
  393. currentPageG.appendChild(text);
  394. super.renderCharacter(ch);
  395. }
  396. }