1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package de.erichseifert.vectorgraphics2d;
23
24 import java.awt.BasicStroke;
25 import java.awt.Color;
26 import java.awt.Image;
27 import java.awt.Shape;
28 import java.awt.geom.AffineTransform;
29 import java.awt.geom.Ellipse2D;
30 import java.awt.geom.Line2D;
31 import java.awt.geom.PathIterator;
32 import java.awt.geom.Rectangle2D;
33 import java.awt.geom.RoundRectangle2D;
34 import java.awt.image.BufferedImage;
35 import java.io.ByteArrayOutputStream;
36 import java.io.IOException;
37 import java.util.Map;
38
39 import javax.imageio.ImageIO;
40 import javax.xml.bind.DatatypeConverter;
41
42
43
44
45
46 public class EMFGraphics2D extends VectorGraphics2D {
47
48 private static final Map<Integer, String> STROKE_ENDCAPS = DataUtils.map(
49 new Integer[] { BasicStroke.CAP_BUTT, BasicStroke.CAP_ROUND, BasicStroke.CAP_SQUARE },
50 new String[] { "butt", "round", "square" }
51 );
52
53
54 private static final Map<Integer, String> STROKE_LINEJOIN = DataUtils.map(
55 new Integer[] { BasicStroke.JOIN_MITER, BasicStroke.JOIN_ROUND, BasicStroke.JOIN_BEVEL },
56 new String[] { "miter", "round", "bevel" }
57 );
58
59
60 private static final String CLIP_PATH_ID = "clip";
61
62 private long clipCounter;
63
64
65
66
67
68 public EMFGraphics2D(double x, double y, double width, double height) {
69 super(x, y, width, height);
70 writeHeader();
71 }
72
73 @Override
74 protected void writeString(String str, double x, double y) {
75
76 str = str.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
77
78 writeln("<text x=\"", x, "\" y=\"", y, "\">", str, "</text>");
79 }
80
81 @Override
82 protected void writeImage(Image img, int imgWidth, int imgHeight, double x,
83 double y, double width, double height) {
84 BufferedImage bufferedImg = GraphicsUtils.toBufferedImage(img);
85 String imgData = getSvg(bufferedImg);
86 write("<image x=\"" , x, "\" y=\"" , y, "\" ",
87 "width=\"" , width, "\" height=\"" , height, "\" ",
88 "xlink:href=\"", imgData, "\" ",
89 "/>");
90 }
91
92 @Override
93 public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) {
94 write("<polygon points=\"");
95 for (int i = 0; i < nPoints; i++) {
96 if (i > 0) {
97 write(" ");
98 }
99 write(xPoints[i], ",", yPoints[i]);
100 }
101 write("\" ");
102 writeClosingDraw();
103 }
104
105 @Override
106 public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) {
107 write("<polyline points=\"");
108 for (int i = 0; i < nPoints; i++) {
109 if (i > 0) {
110 write(" ");
111 }
112 write(xPoints[i], ",", yPoints[i]);
113 }
114 write("\" ");
115 writeClosingDraw();
116 }
117
118 @Override
119 public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) {
120 write("<polygon points=\"");
121 for (int i = 0; i < nPoints; i++) {
122 if (i > 0) {
123 write(" ");
124 }
125 write(xPoints[i], ",", yPoints[i]);
126 }
127 write("\" ");
128 writeClosingFill();
129 }
130
131 @Override
132 public void setClip(Shape clip) {
133 super.setClip(clip);
134 if (getClip() != null) {
135 writeln("<clipPath id=\"", CLIP_PATH_ID, ++clipCounter, "\">");
136 writeShape(getClip());
137 writeln("/>");
138 writeln("</clipPath>");
139 }
140 }
141
142 @Override
143 protected void setAffineTransform(AffineTransform tx) {
144
145 if (isTransformed()) {
146 writeln("</g>");
147 }
148
149 super.setAffineTransform(tx);
150
151 if (isTransformed()) {
152 double[] matrix = new double[6];
153 getTransform().getMatrix(matrix);
154 write("<g transform=\"matrix(", DataUtils.join(" ", matrix), ") \">");
155 }
156 }
157
158 @Override
159 protected void writeHeader() {
160 Rectangle2D bounds = getBounds();
161 double x = bounds.getX();
162 double y = bounds.getY();
163 double w = bounds.getWidth();
164 double h = bounds.getHeight();
165 writeln("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
166 writeln("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ",
167 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">");
168 writeln("<svg version=\"1.2\" xmlns=\"http://www.w3.org/2000/svg\" ",
169 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" ",
170 "x=\"", x, "mm\" y=\"", y, "mm\" ",
171 "width=\"", w, "mm\" height=\"", h, "mm\" " +
172 "viewBox=\"", x, " ", y, " ", w, " ", h, "\"",
173 ">");
174 writeln("<style type=\"text/css\"><![CDATA[");
175 writeln("text { font-family:", getFont().getFamily(), ";font-size:", getFont().getSize2D(), "px; }");
176 writeln("]]></style>");
177 }
178
179
180
181
182 @Override
183 protected void writeClosingDraw() {
184 write("style=\"fill:none;stroke:", getSvg(getColor()));
185 if (getStroke() instanceof BasicStroke) {
186 BasicStroke s = (BasicStroke) getStroke();
187 if (s.getLineWidth() != 1f) {
188 write(";stroke-width:", s.getLineWidth());
189 }
190 if (s.getEndCap() != BasicStroke.CAP_BUTT) {
191 write(";stroke-linecap:", STROKE_ENDCAPS.get(s.getEndCap()));
192 }
193 if (s.getLineJoin() != BasicStroke.JOIN_MITER) {
194 write(";stroke-linejoin:", STROKE_LINEJOIN.get(s.getLineJoin()));
195 }
196
197 if (s.getDashArray() != null && s.getDashArray().length>0) {
198 write(";stroke-dasharray:", DataUtils.join(",", s.getDashArray()));
199 write(";stroke-dashoffset:", s.getDashPhase());
200 }
201 }
202 if (getClip() != null) {
203 write("\" clip-path=\"url(#", CLIP_PATH_ID, clipCounter, ")");
204 }
205 writeln("\" />");
206 }
207
208
209
210
211 @Override
212 protected void writeClosingFill() {
213 write("style=\"fill:", getSvg(getColor()), ";stroke:none");
214 if (getClip() != null) {
215 write("\" clip-path=\"url(#", CLIP_PATH_ID, clipCounter, ")");
216 }
217 writeln("\" />");
218 }
219
220
221
222
223
224
225 @Override
226 protected void writeShape(Shape s) {
227 if (s instanceof Line2D) {
228 Line2D l = (Line2D) s;
229 double x1 = l.getX1();
230 double y1 = l.getY1();
231 double x2 = l.getX2();
232 double y2 = l.getY2();
233 write("<line x1=\"", x1, "\" y1=\"", y1, "\" x2=\"", x2, "\" y2=\"", y2, "\" ");
234 } else if (s instanceof Rectangle2D) {
235 Rectangle2D r = (Rectangle2D) s;
236 double x = r.getX();
237 double y = r.getY();
238 double width = r.getWidth();
239 double height = r.getHeight();
240 write("<rect x=\"", x, "\" y=\"", y, "\" width=\"", width, "\" height=\"", height, "\" ");
241 } else if (s instanceof RoundRectangle2D) {
242 RoundRectangle2D r = (RoundRectangle2D) s;
243 double x = r.getX();
244 double y = r.getY();
245 double width = r.getWidth();
246 double height = r.getHeight();
247 double arcWidth = r.getArcWidth();
248 double arcHeight = r.getArcHeight();
249 write("<rect x=\"", x, "\" y=\"", y, "\" width=\"", width, "\" height=\"", height, "\" rx=\"", arcWidth, "\" ry=\"", arcHeight, "\" ");
250 } else if (s instanceof Ellipse2D) {
251 Ellipse2D e = (Ellipse2D) s;
252 double x = e.getX();
253 double y = e.getY();
254 double rx = e.getWidth()/2.0;
255 double ry = e.getHeight()/2.0;
256 write("<ellipse cx=\"", x+rx, "\" cy=\"", y+ry, "\" rx=\"", rx, "\" ry=\"", ry, "\" ");
257 } else {
258 write("<path d=\"");
259 PathIterator segments = s.getPathIterator(null);
260 double[] coords = new double[6];
261 for (int i = 0; !segments.isDone(); i++, segments.next()) {
262 if (i > 0) {
263 write(" ");
264 }
265 int segmentType = segments.currentSegment(coords);
266 switch (segmentType) {
267 case PathIterator.SEG_MOVETO:
268 write("M", coords[0], ",", coords[1]);
269 break;
270 case PathIterator.SEG_LINETO:
271 write("L", coords[0], ",", coords[1]);
272 break;
273 case PathIterator.SEG_CUBICTO:
274 write("C", coords[0], ",", coords[1], " ", coords[2], ",", coords[3], " ", coords[4], ",", coords[5]);
275 break;
276 case PathIterator.SEG_QUADTO:
277 write("Q", coords[0], ",", coords[1], " ", coords[2], ",", coords[3]);
278 break;
279 case PathIterator.SEG_CLOSE:
280 write("Z");
281 break;
282 }
283 }
284 write("\" ");
285 }
286 }
287
288 private static String getSvg(Color c) {
289 String color = "rgb(" + c.getRed() + "," + c.getGreen() + "," + c.getBlue() + ")";
290 if (c.getAlpha() < 255) {
291 double opacity = c.getAlpha()/255.0;
292 color += ";opacity:" + opacity;
293 }
294 return color;
295 }
296
297 private static String getSvg(BufferedImage bufferedImg) {
298 ByteArrayOutputStream data = new ByteArrayOutputStream();
299 try {
300 ImageIO.write(bufferedImg, "png", data);
301 } catch (IOException e) {
302 return "";
303 }
304 String dataBase64 = DatatypeConverter.printBase64Binary(data.toByteArray());
305 return "data:image/png;base64," + dataBase64;
306 }
307
308 @Override
309 protected String getFooter() {
310 String footer = "";
311
312 if (isTransformed()) {
313 footer += "</g>\n";
314 }
315 footer += "</svg>\n";
316 return footer;
317 }
318
319 @Override
320 public String toString() {
321 String doc = super.toString();
322 doc = doc.replaceAll("<g transform=\"[^\"]*\"></g>\n", "");
323 return doc;
324 }
325 }