1 package org.simantics.diagram.svg.export;
3 import java.awt.Graphics2D;
5 import java.awt.RenderingHints;
6 import java.awt.geom.AffineTransform;
7 import java.awt.geom.Rectangle2D;
8 import java.io.StringWriter;
11 import org.apache.batik.dom.GenericDOMImplementation;
12 import org.apache.batik.svggen.SVGGraphics2D;
13 import org.simantics.g2d.canvas.ICanvasContext;
14 import org.simantics.g2d.diagram.DiagramHints;
15 import org.simantics.g2d.diagram.DiagramUtils;
16 import org.simantics.g2d.diagram.IDiagram;
17 import org.simantics.g2d.participant.TransformUtil;
18 import org.simantics.scenegraph.g2d.G2DRenderingHints;
19 import org.simantics.scenegraph.utils.QualityHints;
20 import org.simantics.utils.page.MarginUtils;
21 import org.simantics.utils.page.MarginUtils.Margins;
22 import org.w3c.dom.DOMImplementation;
23 import org.w3c.dom.Document;
24 import org.w3c.dom.Element;
28 * org.simantics.diagram.export.ImageBuilder with SVG support
30 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
33 public class SVGBuilder {
41 * @param file File to write the image (optional)
42 * @param dpi Dots Per Inch
43 * @param size Image size in pixels
44 * @param margin percentage of image width for margins. 0.05 is 5%.
46 public SVGBuilder(Double dpi, Point size, double margin) {
49 this.margin = Math.max(margin,0.0);
55 * @param file File to write the image (optional)
56 * @param dpi Dots Per Inch
57 * @param size Image size in pixels
58 * @param margins Margins
60 public SVGBuilder(Double dpi, Point size, Margins margins) {
64 this.margins = margins;
67 public static SVGGraphics2D defaultSVGGenerator() {
68 DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
70 // Create an instance of org.w3c.dom.Document.
71 String svgNS = "http://www.w3.org/2000/svg";
72 Document document = domImpl.createDocument(svgNS, "svg", null);
74 // Create an instance of the SVG Generator.
75 SVGGraphics2D svgGenerator = new SVGGraphics2D(document);
76 svgGenerator.getGeneratorContext().setIDGenerator(new UniqueIDGenerator(UUID.randomUUID().toString()));
81 * @param canvasContext
82 * the canvas context to paint
84 * <code>true</code> to actually write the resulting Image to the file.
86 public String paint(ICanvasContext canvasContext) throws Exception {
87 return paint(canvasContext, defaultSVGGenerator());
91 * @param canvasContext
92 * the canvas context to paint
94 * the svg generator to use
96 * <code>true</code> to actually write the resulting Image to the file.
98 public String paint(ICanvasContext canvasContext, SVGGraphics2D svgGenerator) throws Exception {
100 Graphics2D g2 = svgGenerator;
102 IDiagram diagram = canvasContext.getDefaultHintContext().getHint(DiagramHints.KEY_DIAGRAM);
103 Rectangle2D diagramRect = DiagramUtils.getContentRect(diagram);
105 if (diagramRect == null)
106 diagramRect = new Rectangle2D.Double(0, 0, 100, 100);
108 // add margins to content.
109 double off = Math.max(diagramRect.getWidth(), diagramRect.getHeight()) * margin*0.5;
110 diagramRect = new Rectangle2D.Double(diagramRect.getX() - off, diagramRect.getY() - off, diagramRect.getWidth() + off * 2.0, diagramRect.getHeight() + off * 2.0);
112 if (margins != null) {
113 diagramRect = org.simantics.scenegraph.utils.GeometryUtils.expandRectangle(diagramRect,
114 margins.top.diagramAbsolute,
115 margins.bottom.diagramAbsolute,
116 margins.left.diagramAbsolute,
117 margins.right.diagramAbsolute
121 // Make sure the transformation is reset.
122 AffineTransform tr = new AffineTransform();
124 Rectangle2D controlArea;
126 double mmToInch = 1.0 / 25.4;
127 controlArea = new Rectangle2D.Double(0, 0, diagramRect.getWidth() * mmToInch * dpi, diagramRect.getHeight() * mmToInch * dpi);
129 if (margins != null) {
130 double w = controlArea.getWidth();
131 double h = controlArea.getHeight();
132 controlArea = org.simantics.scenegraph.utils.GeometryUtils.expandRectangle(controlArea,
133 margins.top.controlAbsolute + margins.top.controlRelative * h,
134 margins.bottom.controlAbsolute + margins.bottom.controlRelative * h,
135 margins.left.controlAbsolute + margins.left.controlRelative * w,
136 margins.right.controlAbsolute + margins.right.controlRelative * w
140 controlArea = new Rectangle2D.Double(0, 0, size.getX(), size.getY());
141 if (margins != null) {
142 double w = controlArea.getWidth();
143 double h = controlArea.getHeight();
144 controlArea = org.simantics.scenegraph.utils.GeometryUtils.expandRectangle(controlArea,
145 -(margins.top.controlAbsolute + margins.top.controlRelative * h),
146 -(margins.bottom.controlAbsolute + margins.bottom.controlRelative * h),
147 -(margins.left.controlAbsolute + margins.left.controlRelative * w),
148 -(margins.right.controlAbsolute + margins.right.controlRelative * w)
153 canvasContext.getSingleItem(TransformUtil.class).fitArea(controlArea, diagramRect, MarginUtils.NO_MARGINS);
155 float width = (float)controlArea.getWidth();
156 float height = (float)controlArea.getHeight();
159 QualityHints.HIGH_QUALITY_HINTS.setQuality(g2);
160 g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
164 g2.setClip(new Rectangle2D.Double(0, 0, controlArea.getWidth(), controlArea.getHeight()));
166 g2.setRenderingHint(G2DRenderingHints.KEY_CONTROL_BOUNDS, new Rectangle2D.Double(0, 0, controlArea.getWidth(), controlArea.getHeight()));
168 if (canvasContext.isLocked())
169 throw new IllegalStateException("cannot render image, canvas context is locked: " + canvasContext);
171 canvasContext.getSceneGraph().render(g2);
175 Element root = svgGenerator.getRoot();
176 root.setAttributeNS(null, "viewBox", "0 0 " + width + " " + height);
177 root.setAttributeNS(null, "height", Float.toString(height));
178 root.setAttributeNS(null, "width", Float.toString(width));
180 // Finally, stream out SVG to the standard output using
182 boolean useCSS = false; // we want to use CSS style attributes
183 StringWriter writer = new StringWriter();
184 //svgGenerator.stream(writer, useCSS);
185 svgGenerator.stream(root,writer, useCSS, false);
186 return writer.toString();