1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.vectomatic.common.format;
19
20 import java.util.HashMap;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24
25 import org.vectomatic.common.model.Attribute;
26 import org.vectomatic.common.model.CloneShapeVisitor;
27 import org.vectomatic.common.model.FloatAttributeValue;
28 import org.vectomatic.common.model.IShapeVisitor;
29 import org.vectomatic.common.model.IStyleVisitor;
30 import org.vectomatic.common.model.Shape;
31 import org.vectomatic.common.model.geometry.BezierSegment;
32 import org.vectomatic.common.model.geometry.BoundingBox;
33 import org.vectomatic.common.model.geometry.Ellipse;
34 import org.vectomatic.common.model.geometry.LineSegment;
35 import org.vectomatic.common.model.geometry.Path;
36 import org.vectomatic.common.model.geometry.Point;
37 import org.vectomatic.common.model.geometry.Polyline;
38 import org.vectomatic.common.model.geometry.Rect;
39 import org.vectomatic.common.model.geometry.Segment;
40 import org.vectomatic.common.model.geometry.ShapeGroup;
41 import org.vectomatic.common.model.geometry.TransformMatrix;
42 import org.vectomatic.common.model.style.Color;
43 import org.vectomatic.common.model.style.IStyle;
44 import org.vectomatic.common.model.style.NoneStyle;
45 import org.vectomatic.common.model.style.Palette;
46 import org.vectomatic.common.model.style.PaletteList;
47
48
49
50
51 public class SVG11Visitor implements IShapeVisitor, ISVGExporter {
52 private class Context {
53 TransformMatrix m;
54 Shape shape;
55 public Context() {
56 m = new TransformMatrix();
57 }
58 }
59 private IStyleVisitor _defStyleVisitor = new IStyleVisitor() {
60 public void visitColor(Color color) {
61 String style = color.toString();
62 _attributes.clear();
63 _attributes.put(ATT_SOLIDCOLOR, style);
64 writeStartElement(ELT_SOLIDCOLOR, _attributes, true);
65 }
66
67 public void visitNoneStyle(NoneStyle none) {
68 _attributes.clear();
69 _attributes.put(ATT_SOLIDCOLOR, "rgb(0,0,0)");
70 _attributes.put(ATT_SOLIDOPACITY, "0");
71 _attributes.put(ATT_ID, "none");
72 writeStartElement(ELT_SOLIDCOLOR, _attributes, true);
73 }
74 };
75 private IStyleVisitor _strokeStyleVisitor = new IStyleVisitor() {
76 public void visitColor(Color color) {
77 _attributes.put(ATT_STROKE, color.toString());
78 }
79
80 public void visitNoneStyle(NoneStyle none) {
81 _attributes.put(ATT_STROKE, "url(#none)");
82 }
83 };
84
85 private IStyleVisitor _fillStyleVisitor = new IStyleVisitor() {
86 public void visitColor(Color color) {
87 _attributes.put(ATT_FILL, color.toString());
88 }
89
90 public void visitNoneStyle(NoneStyle none) {
91 _attributes.put(ATT_FILL, "url(#none)");
92 }
93 };
94
95 private Context[] _stack;
96 private int _index;
97 private IOutputStream _stream;
98 private Map<String, String> _attributes;
99 private CloneShapeVisitor _cloner;
100 private Point _p0, _p1;
101
102 public SVG11Visitor() {
103 _stack = new Context[8];
104 _attributes = new HashMap<String, String>();
105 _cloner = new CloneShapeVisitor();
106 _p0 = new Point();
107 _p1 = new Point();
108 }
109
110
111
112 public TransformMatrix pushShape(Shape shape, boolean isTailShape) {
113
114 if (_index >= _stack.length) {
115 Context[] stack = new Context[2 * _stack.length];
116 for (int i = 0; i < _stack.length; i++) {
117 stack[i] = _stack[i];
118 }
119 _stack = stack;
120 }
121
122
123 if (_stack[_index] == null) {
124 _stack[_index] = new Context();
125 }
126
127
128 TransformMatrix m;
129 if (_index == 0) {
130 m = _stack[_index].m = shape.getTransform();
131 } else {
132 m = shape.getTransform().preMultiply(_stack[_index - 1].m, _stack[_index].m);
133 }
134 _stack[_index].shape = shape;
135 _index++;
136
137 if (isTailShape) {
138 _attributes.clear();
139 IStyle strokeStyle = ((IStyle)getAttribute(Attribute.LINE_STYLE));
140 if (strokeStyle != null) {
141 strokeStyle.acceptVisitor(_strokeStyleVisitor);
142 float opacity = ((FloatAttributeValue)getAttribute(Attribute.LINE_OPACITY)).getValue();
143 if (opacity < 1.0f) {
144 _attributes.put(ATT_STROKEOPACITY, Float.toString(opacity));
145 }
146 _attributes.put(ATT_STROKEWIDTH, getAttribute(Attribute.LINE_WIDTH).toString());
147 }
148 IStyle fillStyle = ((IStyle)getAttribute(Attribute.FILL_STYLE));
149 if (fillStyle != null) {
150 fillStyle.acceptVisitor(_fillStyleVisitor);
151 float opacity = ((FloatAttributeValue)getAttribute(Attribute.FILL_OPACITY)).getValue();
152 if (opacity < 1.0f) {
153 _attributes.put(ATT_FILLOPACITY, Float.toString(opacity));
154 }
155 }
156
157
158
159
160
161
162 shape.acceptVisitor(_cloner);
163 Shape tmp = _cloner.getClone();
164 tmp.setTransform(m);
165 TransformMatrix r = new TransformMatrix().rotation(tmp.getRotation());
166 TransformMatrix T = new TransformMatrix().translation(tmp.getTranslation(new Point()));
167 TransformMatrix m1 = r.preMultiply(T, new TransformMatrix());
168 TransformMatrix s = new TransformMatrix().scaling(tmp.getScaling(new Point()));
169 TransformMatrix t = new TransformMatrix().translation(tmp.getBoundingBox().getPoint(BoundingBox.PT_C, new Point()).negate());
170 TransformMatrix m2 = t.preMultiply(s, new TransformMatrix());
171
172 StringBuffer buffer = new StringBuffer();
173 buffer.append("matrix(");
174 buffer.append(m1.m11);
175 buffer.append(" ");
176 buffer.append(m1.m21);
177 buffer.append(" ");
178 buffer.append(m1.m12);
179 buffer.append(" ");
180 buffer.append(m1.m22);
181 buffer.append(" ");
182 buffer.append(m1.m13);
183 buffer.append(" ");
184 buffer.append(m1.m23);
185 buffer.append(")");
186 _attributes.put(ATT_TRANSFORM, buffer.toString());
187 return m2;
188 }
189 return m;
190 }
191
192
193 private void popShape() {
194 _index--;
195 }
196
197 private Object getAttribute(Attribute attr) {
198 Object attrValue = null;
199 for (int i = 0; i < _index; i++) {
200 if (_stack[i].shape.definesAttribute(attr)) {
201 attrValue = _stack[i].shape.getAttribute(attr);
202 break;
203 }
204 }
205 return attrValue;
206 }
207
208
209
210
211 public void writeSVG(IOutputStream stream, Shape[] shapes, PaletteList paletteList, int width, int height) {
212 _stream = stream;
213 _stream.write("<?xml version=\"1.0\"?>\n");
214
215 _attributes.clear();
216 _attributes.put(ATT_WIDTH, width + "px");
217 _attributes.put(ATT_HEIGHT, height + "px");
218 _attributes.put("xmlns", NS);
219 writeStartElement(ELT_SVG, _attributes, false);
220
221
222 writeStartElement(ELT_DESC, new HashMap<String,String>(), false);
223 writeCharacters("SVG 1.1 export generated by vectomatic.org");
224 writeEndElement(ELT_DESC);
225
226
227 writeStartElement(ELT_DEFS, new HashMap<String,String>(), false);
228 if (paletteList != null) {
229 for (int i = 0, isize = paletteList.size(); i < isize; i++) {
230 writeStartElement(ELT_G, new HashMap<String,String>(), false);
231 Palette palette = paletteList.getPalette(i);
232 writeStartElement(ELT_DESC, new HashMap<String,String>(), false);
233 writeCharacters(palette.getName());
234 writeEndElement(ELT_DESC);
235 for (int j = 0, jsize = palette.getSize(); j < jsize; j++) {
236 palette.getColor(j).acceptVisitor(_defStyleVisitor);
237 }
238 writeEndElement(ELT_G);
239 }
240 }
241
242
243 NoneStyle.NONE.acceptVisitor(_defStyleVisitor);
244 writeEndElement(ELT_DEFS);
245
246
247 if (shapes != null) {
248 for (int i = 0; i < shapes.length; i++) {
249 shapes[i].acceptVisitor(this);
250 }
251 }
252 writeEndElement(ELT_SVG);
253 }
254
255 public void visitEllipse(Ellipse ellipse) {
256 TransformMatrix m = pushShape(ellipse, true);
257 ellipse.getBoundingBox().getPoint(BoundingBox.PT_C, _p0).transform(m);
258 ellipse.getBoundingBox().getPoint(BoundingBox.PT_SE, _p1).transform(m);
259 _attributes.put(ATT_CX, Float.toString(_p0.x));
260 _attributes.put(ATT_CY, Float.toString(_p0.y));
261 _attributes.put(ATT_RX, Float.toString(Math.abs(_p1.x - _p0.x)));
262 _attributes.put(ATT_RY, Float.toString(Math.abs(_p1.y - _p0.y)));
263 writeStartElement(ELT_ELLIPSE, _attributes, true);
264 popShape();
265 }
266
267 public void visitPolyline(Polyline polyline) {
268 TransformMatrix m = pushShape(polyline, true);
269 Point[] pts = polyline.getVertices();
270 StringBuffer vertexBuffer = new StringBuffer();
271 for (int i = 0, size = pts.length - (polyline.isClosed() ? 1 : 0); i < size; i++) {
272 if (i > 0) {
273 vertexBuffer.append(" ");
274 }
275 pts[i].transform(m, _p0);
276 vertexBuffer.append(_p0.x);
277 vertexBuffer.append(",");
278 vertexBuffer.append(_p0.y);
279 }
280 _attributes.put(ATT_POINTS, vertexBuffer.toString());
281 if (polyline.isClosed()) {
282 writeStartElement(ELT_POLYGON, _attributes, true);
283 } else {
284 writeStartElement(ELT_POLYLINE, _attributes, true);
285 }
286 popShape();
287 }
288
289 public void visitRect(Rect rect) {
290 TransformMatrix m = pushShape(rect, true);
291 rect.getBoundingBox().getPoint(BoundingBox.PT_NW, _p0).transform(m);
292 rect.getBoundingBox().getPoint(BoundingBox.PT_SE, _p1).transform(m);
293 _attributes.put(ATT_X, Float.toString(Math.min(_p0.x, _p1.x)));
294 _attributes.put(ATT_Y, Float.toString(Math.min(_p0.y, _p1.y)));
295 _attributes.put(ATT_WIDTH, Float.toString(Math.abs(_p1.x - _p0.x)));
296 _attributes.put(ATT_HEIGHT, Float.toString(Math.abs(_p1.y - _p0.y)));
297 writeStartElement(ELT_RECT, _attributes, true);
298 popShape();
299 }
300
301 public void visitPath(Path path) {
302 TransformMatrix m = pushShape(path, true);
303
304 StringBuffer dValue = new StringBuffer();
305 List<Segment> segments = path.getSegments();
306 for (int i = 0, size = segments.size(); i < size; i++) {
307 Segment segment = segments.get(i);
308 if (i == 0) {
309 segment.getStartPoint().transform(m, _p0);
310 dValue.append(VAL_MOVE_TO);
311 dValue.append(" ");
312 dValue.append(_p0.x);
313 dValue.append(" ");
314 dValue.append(_p0.y);
315 }
316 dValue.append(" ");
317 if (segment instanceof LineSegment) {
318 segment.getEndPoint().transform(m, _p0);
319 dValue.append(VAL_LINE_TO);
320 dValue.append(" ");
321 dValue.append(_p0.x);
322 dValue.append(" ");
323 dValue.append(_p0.y);
324 } else {
325 BezierSegment bezierSegment = (BezierSegment)segment;
326 dValue.append(VAL_CURVE_TO);
327 dValue.append(" ");
328 bezierSegment.getStartControlPoint().transform(m, _p0);
329 dValue.append(_p0.x);
330 dValue.append(" ");
331 dValue.append(_p0.y);
332 dValue.append(" ");
333 bezierSegment.getEndControlPoint().transform(m, _p0);
334 dValue.append(_p0.x);
335 dValue.append(" ");
336 dValue.append(_p0.y);
337 dValue.append(" ");
338 bezierSegment.getEndPoint().transform(m, _p0);
339 dValue.append(_p0.x);
340 dValue.append(" ");
341 dValue.append(_p0.y);
342 }
343 }
344 _attributes.put(ATT_D, dValue.toString());
345
346 if (!path.isClosed()) {
347 _attributes.put(ATT_FILL, VAL_NONE);
348 _attributes.remove(ATT_FILLOPACITY);
349 }
350
351 writeStartElement(ELT_PATH, _attributes, true);
352 popShape();
353
354 }
355
356 public void visitShapeGroup(ShapeGroup group) {
357 pushShape(group, false);
358 List<Shape> shapes = group.getShapes();
359 for (int i = 0, size = shapes.size(); i < size; i++) {
360 Shape shape = shapes.get(i);
361 shape.acceptVisitor(this);
362 }
363 popShape();
364 }
365
366
367 private void writeStartElement(String name, Map<String, String> attributes, boolean empty) {
368 _stream.write("<");
369 _stream.write(name);
370 Iterator<Map.Entry<String, String>> iterator = attributes.entrySet().iterator();
371 while(iterator.hasNext()) {
372 Map.Entry<String, String> entry = iterator.next();
373 String attName = entry.getKey();
374 String attValue = entry.getValue();
375 _stream.write(" ");
376 _stream.write(attName);
377 _stream.write("=\"");
378 _stream.write(attValue);
379 _stream.write("\"");
380 }
381 if (empty) {
382 _stream.write("/");
383 }
384 _stream.write(">");
385 if (empty) {
386 _stream.write("\n");
387 }
388 }
389
390 private void writeEndElement(String name) {
391 _stream.write("</");
392 _stream.write(name);
393 _stream.write(">\n");
394 }
395
396 private void writeCharacters(String str) {
397 _stream.write(str.replaceAll("&", "&").replaceAll("<", "<"));
398 }
399 }