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 import java.util.Stack;
25
26 import org.vectomatic.common.model.Attribute;
27 import org.vectomatic.common.model.FloatAttributeValue;
28 import org.vectomatic.common.model.IAttributeValue;
29 import org.vectomatic.common.model.IShapeVisitor;
30 import org.vectomatic.common.model.IStyleVisitor;
31 import org.vectomatic.common.model.Shape;
32 import org.vectomatic.common.model.geometry.BezierSegment;
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 SVG12Visitor implements IShapeVisitor, ISVGExporter {
52 private Stack<Shape> _stack;
53 private IOutputStream _stream;
54 private Map<String, String> _attributes;
55 private IStyleVisitor _defStyleVisitor = new IStyleVisitor() {
56 public void visitColor(Color color) {
57 String style = color.toString();
58 _attributes.clear();
59 _attributes.put(ATT_SOLIDCOLOR, style);
60 writeStartElement(ELT_SOLIDCOLOR, _attributes, true);
61 }
62
63 public void visitNoneStyle(NoneStyle none) {
64 _attributes.clear();
65 _attributes.put(ATT_SOLIDCOLOR, "rgb(0,0,0)");
66 _attributes.put(ATT_SOLIDOPACITY, "0");
67 _attributes.put(ATT_ID, "none");
68 writeStartElement(ELT_SOLIDCOLOR, _attributes, true);
69 }
70 };
71
72 private IStyleVisitor _strokeStyleVisitor = new IStyleVisitor() {
73 public void visitColor(Color color) {
74 _attributes.put(ATT_STROKE, color.toString());
75 }
76
77 public void visitNoneStyle(NoneStyle none) {
78 _attributes.put(ATT_STROKE, "url(#none)");
79 }
80 };
81
82 private IStyleVisitor _fillStyleVisitor = new IStyleVisitor() {
83 public void visitColor(Color color) {
84 _attributes.put(ATT_FILL, color.toString());
85 }
86
87 public void visitNoneStyle(NoneStyle none) {
88 _attributes.put(ATT_FILL, "url(#none)");
89 }
90 };
91
92 public SVG12Visitor() {
93 _stack = new Stack<Shape>();
94 _attributes = new HashMap<String,String>();
95 }
96
97 public void writeSVG(IOutputStream stream, Shape[] shapes, PaletteList paletteList, int width, int height) {
98 _stream = stream;
99 _stream.write("<?xml version=\"1.0\"?>\n");
100
101 _attributes.clear();
102 _attributes.put(ATT_WIDTH, width + "px");
103 _attributes.put(ATT_HEIGHT, height + "px");
104 _attributes.put(ATT_VERSION, "1.2");
105 _attributes.put(ATT_BASEPROFILE, "tiny");
106 _attributes.put("xmlns", NS);
107 writeStartElement(ELT_SVG, _attributes, false);
108
109
110 writeStartElement(ELT_DESC, new HashMap<String,String>(), false);
111 writeCharacters("SVG 1.2 export generated by vectomatic.org");
112 writeEndElement(ELT_DESC);
113
114
115 writeStartElement(ELT_DEFS, new HashMap<String,String>(), false);
116 if (paletteList != null) {
117 for (int i = 0, isize = paletteList.size(); i < isize; i++) {
118 writeStartElement(ELT_G, new HashMap<String,String>(), false);
119 Palette palette = paletteList.getPalette(i);
120 writeStartElement(ELT_DESC, new HashMap<String,String>(), false);
121 writeCharacters(palette.getName());
122 writeEndElement(ELT_DESC);
123 for (int j = 0, jsize = palette.getSize(); j < jsize; j++) {
124 palette.getColor(j).acceptVisitor(_defStyleVisitor);
125 }
126 writeEndElement(ELT_G);
127 }
128 }
129
130
131 NoneStyle.NONE.acceptVisitor(_defStyleVisitor);
132 writeEndElement(ELT_DEFS);
133
134
135 if (shapes != null) {
136 for (int i = 0; i < shapes.length; i++) {
137 shapes[i].acceptVisitor(this);
138 }
139 }
140 writeEndElement(ELT_SVG);
141 }
142
143 public void visitEllipse(Ellipse ellipse) {
144 pushShape(ellipse);
145 _attributes.put(ATT_CX, "0");
146 _attributes.put(ATT_CY, "0");
147 _attributes.put(ATT_RX, "1");
148 _attributes.put(ATT_RY, "1");
149 writeStartElement(ELT_ELLIPSE, _attributes, true);
150 popShape();
151 }
152
153 public void visitPolyline(Polyline polyline) {
154 pushShape(polyline);
155 Point[] pts = polyline.getVertices();
156 StringBuffer vertexBuffer = new StringBuffer();
157 for (int i = 0, size = pts.length - (polyline.isClosed() ? 1 : 0); i < size; i++) {
158 if (i > 0) {
159 vertexBuffer.append(" ");
160 }
161 vertexBuffer.append(pts[i].x);
162 vertexBuffer.append(",");
163 vertexBuffer.append(pts[i].y);
164 }
165 _attributes.put(ATT_POINTS, vertexBuffer.toString());
166 if (polyline.isClosed()) {
167 writeStartElement(ELT_POLYGON, _attributes, true);
168 } else {
169 writeStartElement(ELT_POLYLINE, _attributes, true);
170 }
171 popShape();
172 }
173
174 public void visitRect(Rect rect) {
175 pushShape(rect);
176 _attributes.put(ATT_X, "-1");
177 _attributes.put(ATT_Y, "-1");
178 _attributes.put(ATT_WIDTH, "2");
179 _attributes.put(ATT_HEIGHT, "2");
180 writeStartElement(ELT_RECT, _attributes, true);
181 popShape();
182 }
183
184 public void visitPath(Path path) {
185 pushShape(path);
186
187 StringBuffer dValue = new StringBuffer();
188 List<Segment> segments = path.getSegments();
189 for (int i = 0, size = segments.size(); i < size; i++) {
190 Segment segment = segments.get(i);
191 if (i == 0) {
192 dValue.append(VAL_MOVE_TO);
193 dValue.append(" ");
194 dValue.append(segment.getStartPoint().x);
195 dValue.append(" ");
196 dValue.append(segment.getStartPoint().y);
197 }
198 dValue.append(" ");
199 if (segment instanceof LineSegment) {
200 dValue.append(VAL_LINE_TO);
201 dValue.append(" ");
202 dValue.append(segment.getEndPoint().x);
203 dValue.append(" ");
204 dValue.append(segment.getEndPoint().y);
205 } else {
206 BezierSegment bezierSegment = (BezierSegment)segment;
207 dValue.append(VAL_CURVE_TO);
208 dValue.append(" ");
209 dValue.append(bezierSegment.getStartControlPoint().x);
210 dValue.append(" ");
211 dValue.append(bezierSegment.getStartControlPoint().y);
212 dValue.append(" ");
213 dValue.append(bezierSegment.getEndControlPoint().x);
214 dValue.append(" ");
215 dValue.append(bezierSegment.getEndControlPoint().y);
216 dValue.append(" ");
217 dValue.append(bezierSegment.getEndPoint().x);
218 dValue.append(" ");
219 dValue.append(bezierSegment.getEndPoint().y);
220 }
221 }
222 _attributes.put(ATT_D, dValue.toString());
223
224 if (!path.isClosed()) {
225 _attributes.put(ATT_FILL, VAL_NONE);
226 _attributes.remove(ATT_FILLOPACITY);
227 }
228
229 writeStartElement(ELT_PATH, _attributes, true);
230 popShape();
231
232 }
233
234 public void visitShapeGroup(ShapeGroup group) {
235 pushShape(group);
236 writeStartElement(ELT_G, _attributes, false);
237 List<Shape> shapes = group.getShapes();
238 for (int i = 0, size = shapes.size(); i < size; i++) {
239 Shape shape = shapes.get(i);
240 shape.acceptVisitor(this);
241 }
242 writeEndElement(ELT_G);
243 popShape();
244 }
245
246 private String getTransform(Shape shape) {
247 StringBuffer buffer = new StringBuffer();
248 TransformMatrix m = shape.getTransform();
249 buffer.append("matrix(");
250 buffer.append(m.m11);
251 buffer.append(" ");
252 buffer.append(m.m21);
253 buffer.append(" ");
254 buffer.append(m.m12);
255 buffer.append(" ");
256 buffer.append(m.m22);
257 buffer.append(" ");
258 buffer.append(m.m13);
259 buffer.append(" ");
260 buffer.append(m.m23);
261 buffer.append(")");
262 return buffer.toString();
263 }
264
265 private void pushShape(Shape shape) {
266 _attributes.clear();
267 _attributes.put(ATT_TRANSFORM, getTransform(shape));
268 _attributes.put(ATT_VECTOREFFECT, VAL_NON_SCALING_STROKE);
269 _stack.push(shape);
270 IStyle strokeStyle = ((IStyle)getAttribute(Attribute.LINE_STYLE));
271 if (strokeStyle != null) {
272 strokeStyle.acceptVisitor(_strokeStyleVisitor);
273 float opacity = ((FloatAttributeValue)getAttribute(Attribute.LINE_OPACITY)).getValue();
274 if (opacity < 1.0f) {
275 _attributes.put(ATT_STROKEOPACITY, Float.toString(opacity));
276 }
277 _attributes.put(ATT_STROKEWIDTH, getAttribute(Attribute.LINE_WIDTH).toString());
278 }
279 IStyle fillStyle = ((IStyle)getAttribute(Attribute.FILL_STYLE));
280 if (fillStyle != null) {
281 fillStyle.acceptVisitor(_fillStyleVisitor);
282 float opacity = ((FloatAttributeValue)getAttribute(Attribute.FILL_OPACITY)).getValue();
283 if (opacity < 1.0f) {
284 _attributes.put(ATT_FILLOPACITY, Float.toString(opacity));
285 }
286 }
287 }
288
289 private void popShape() {
290 _stack.pop();
291 }
292
293 private IAttributeValue getAttribute(Attribute attr) {
294 IAttributeValue attrValue = null;
295 for (int i = 0; i < _stack.size(); i++) {
296 if (_stack.get(i).definesAttribute(attr)) {
297 attrValue = _stack.get(i).getAttribute(attr);
298 break;
299 }
300 }
301 return attrValue;
302 }
303
304 private void writeStartElement(String name, Map<String,String> attributes, boolean empty) {
305 _stream.write("<");
306 _stream.write(name);
307 Iterator<Map.Entry<String,String>> iterator = attributes.entrySet().iterator();
308 while(iterator.hasNext()) {
309 Map.Entry<String,String> entry = iterator.next();
310 String attName = entry.getKey();
311 String attValue = entry.getValue();
312 _stream.write(" ");
313 _stream.write(attName);
314 _stream.write("=\"");
315 _stream.write(attValue);
316 _stream.write("\"");
317 }
318 if (empty) {
319 _stream.write("/");
320 }
321 _stream.write(">");
322 if (empty) {
323 _stream.write("\n");
324 }
325 }
326
327 private void writeEndElement(String name) {
328 _stream.write("</");
329 _stream.write(name);
330 _stream.write(">\n");
331 }
332
333 private void writeCharacters(String str) {
334 _stream.write(str.replaceAll("&", "&").replaceAll("<", "<"));
335 }
336 }