Rev 7293 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
7246 | schaersvoo | 1 | /* |
2 | Sketch Elements: Chemistry molecular diagram drawing tool. |
||
3 | |||
4 | (c) 2008 Dr. Alex M. Clark |
||
5 | |||
6 | Released as GNUware, under the Gnu Public License (GPL) |
||
7 | |||
8 | See www.gnu.org for details. |
||
9 | */ |
||
10 | |||
11 | package WIMSchem; |
||
12 | |||
13 | import java.util.*; |
||
14 | import java.io.*; |
||
15 | import java.text.*; |
||
16 | /* |
||
17 | A drawing container which allows primitives to be composed, and once complete, rendered into an SVG output stream. |
||
18 | */ |
||
19 | |||
20 | public class SVGBuilder |
||
21 | { |
||
7292 | schaersvoo | 22 | DecimalFormat df = new DecimalFormat("#.##",new DecimalFormatSymbols(Locale.US)); |
7246 | schaersvoo | 23 | public final static int NOCOLOUR=-1; |
24 | public final static int TXTSTYLE_NORMAL=0; |
||
25 | public final static int TXTSTYLE_BOLD=0x01; |
||
26 | public final static int TXTSTYLE_ITALIC=0x02; |
||
27 | public final static int TXTALIGN_CENTRE=0; |
||
28 | public final static int TXTALIGN_LEFT=1; |
||
29 | public final static int TXTALIGN_RIGHT=2; |
||
30 | public String g_id = "g_SVG_1000000"; |
||
7293 | schaersvoo | 31 | public String svg_id = "SVG_1000000"; |
7246 | schaersvoo | 32 | |
33 | private boolean fresh=true; |
||
34 | private double lowX=0,lowY=0,highX=0,highY=0; |
||
35 | |||
36 | private boolean[] charMask=new boolean[96]; |
||
37 | |||
38 | private final static int ATOM_LINE=1,ATOM_RECT=2,ATOM_OVAL=3,ATOM_PATH=4,ATOM_TEXT=5; |
||
39 | abstract class Atom {int AtomClass,TypeRef;} |
||
40 | |||
41 | class LineAtom extends Atom {double X1,Y1,X2,Y2;} |
||
42 | class LineType {double Thickness; int Colour;} |
||
43 | |||
44 | class RectAtom extends Atom {double X,Y,W,H;} |
||
45 | class RectType {int EdgeCol,FillCol; double Thickness;} |
||
46 | |||
47 | class OvalAtom extends Atom {double CX,CY,RW,RH;} |
||
48 | class OvalType {int EdgeCol,FillCol; double Thickness;} |
||
49 | |||
50 | class PathAtom extends Atom {int N; double[] X,Y; boolean[] Ctrl; boolean Closed;} |
||
51 | class PathType {int EdgeCol,FillCol; double Thickness; boolean HardEdge;} |
||
52 | |||
53 | class TextAtom extends Atom {double X,Y; String Txt;} |
||
54 | class TextType {double Sz; int Colour,Style,Align;} |
||
55 | |||
56 | private ArrayList<Atom> atoms=new ArrayList<Atom>(); |
||
57 | private ArrayList<LineType> lineTypes=new ArrayList<LineType>(); |
||
58 | private ArrayList<RectType> rectTypes=new ArrayList<RectType>(); |
||
59 | private ArrayList<OvalType> ovalTypes=new ArrayList<OvalType>(); |
||
60 | private ArrayList<PathType> pathTypes=new ArrayList<PathType>(); |
||
61 | private ArrayList<TextType> textTypes=new ArrayList<TextType>(); |
||
62 | |||
63 | // ------------------------------------------------ public functions ------------------------------------------------ |
||
64 | |||
65 | public SVGBuilder() |
||
66 | { |
||
67 | for (int n=0;n<96;n++) charMask[n]=false; |
||
68 | } |
||
69 | |||
70 | // query the boundaries of the drawing, post factum |
||
71 | public double lowX() {return lowX;} |
||
72 | public double lowY() {return lowY;} |
||
73 | public double highX() {return highX;} |
||
74 | public double highY() {return highY;} |
||
75 | |||
76 | // atomic drawing options |
||
77 | public void drawLine(double X1,double Y1,double X2,double Y2,int Colour,double Thickness) |
||
78 | { |
||
79 | updateBounds(X1-Thickness,Y1-Thickness); updateBounds(X1+Thickness,Y1+Thickness); |
||
80 | updateBounds(X2-Thickness,Y2-Thickness); updateBounds(X2+Thickness,Y2+Thickness); |
||
81 | |||
82 | LineType type=new LineType(); |
||
83 | type.Thickness=Thickness; |
||
84 | type.Colour=Colour; |
||
85 | |||
86 | LineAtom atom=new LineAtom(); |
||
87 | atom.AtomClass=ATOM_LINE; |
||
88 | atom.X1=X1; atom.Y1=Y1; atom.X2=X2; atom.Y2=Y2; |
||
89 | atom.TypeRef=registerLineType(type); |
||
90 | atoms.add(atom); |
||
91 | } |
||
92 | public void drawRect(double X,double Y,double W,double H,int EdgeCol,double Thickness,int FillCol) |
||
93 | { |
||
94 | updateBounds(X-Thickness,Y-Thickness); updateBounds(X+W+Thickness,Y+H+Thickness); |
||
95 | |||
96 | RectType type=new RectType(); |
||
97 | type.EdgeCol=EdgeCol; |
||
98 | type.Thickness=Thickness; |
||
99 | type.FillCol=FillCol; |
||
100 | |||
101 | RectAtom atom=new RectAtom(); |
||
102 | atom.AtomClass=ATOM_RECT; |
||
103 | atom.X=X; atom.Y=Y; atom.W=W; atom.H=H; |
||
104 | atom.TypeRef=registerRectType(type); |
||
105 | atoms.add(atom); |
||
106 | } |
||
107 | public void drawOval(double CX,double CY,double RW,double RH,int EdgeCol,double Thickness,int FillCol) |
||
108 | { |
||
109 | updateBounds(CX-RW-Thickness,CY-RH-Thickness); updateBounds(CX+RW+Thickness,CY+RH+Thickness); |
||
110 | |||
111 | OvalType type=new OvalType(); |
||
112 | type.EdgeCol=EdgeCol; |
||
113 | type.Thickness=Thickness; |
||
114 | type.FillCol=FillCol; |
||
115 | |||
116 | OvalAtom atom=new OvalAtom(); |
||
117 | atom.AtomClass=ATOM_OVAL; |
||
118 | atom.CX=CX; atom.CY=CY; atom.RW=RW; atom.RH=RH; |
||
119 | atom.TypeRef=registerOvalType(type); |
||
120 | atoms.add(atom); |
||
121 | } |
||
122 | public void drawPoly(double[] X,double[] Y,int EdgeCol,double Thickness,int FillCol,boolean Closed) |
||
123 | { |
||
124 | PathType type=new PathType(); |
||
125 | type.EdgeCol=EdgeCol; |
||
126 | type.FillCol=FillCol; |
||
127 | type.Thickness=Thickness; |
||
128 | type.HardEdge=true; |
||
129 | |||
130 | PathAtom atom=new PathAtom(); |
||
131 | atom.AtomClass=ATOM_PATH; |
||
132 | atom.N=X.length; |
||
133 | atom.X=new double[atom.N]; |
||
134 | atom.Y=new double[atom.N]; |
||
135 | atom.Ctrl=new boolean[atom.N]; |
||
136 | atom.Closed=Closed; |
||
137 | for (int n=0;n<atom.N;n++) |
||
138 | { |
||
139 | updateBounds(X[n]-Thickness,Y[n]-Thickness); |
||
140 | updateBounds(X[n]+Thickness,Y[n]+Thickness); |
||
141 | atom.X[n]=X[n]; |
||
142 | atom.Y[n]=Y[n]; |
||
143 | atom.Ctrl[n]=false; |
||
144 | } |
||
145 | atom.TypeRef=registerPathType(type); |
||
146 | atoms.add(atom); |
||
147 | } |
||
148 | public void drawCurve(double[] X,double[] Y,boolean[] Ctrl,int EdgeCol,double Thickness,int FillCol,boolean Closed) |
||
149 | { |
||
150 | PathType type=new PathType(); |
||
151 | type.EdgeCol=EdgeCol; |
||
152 | type.FillCol=FillCol; |
||
153 | type.Thickness=Thickness; |
||
154 | type.HardEdge=false; |
||
155 | |||
156 | PathAtom atom=new PathAtom(); |
||
157 | atom.AtomClass=ATOM_PATH; |
||
158 | atom.N=X.length; |
||
159 | atom.X=new double[atom.N]; |
||
160 | atom.Y=new double[atom.N]; |
||
161 | atom.Ctrl=new boolean[atom.N]; |
||
162 | atom.Closed=Closed; |
||
163 | for (int n=0;n<atom.N;n++) |
||
164 | { |
||
165 | // (NOTE: if this is a control point, the boundary could be extended too far, but whatever...) |
||
166 | updateBounds(X[n]-Thickness,Y[n]-Thickness); |
||
167 | updateBounds(X[n]+Thickness,Y[n]+Thickness); |
||
168 | atom.X[n]=X[n]; |
||
169 | atom.Y[n]=Y[n]; |
||
170 | atom.Ctrl[n]=Ctrl[n]; |
||
171 | } |
||
172 | atom.TypeRef=registerPathType(type); |
||
173 | atoms.add(atom); |
||
174 | } |
||
175 | public void drawText(double X,double Y,String Txt,double Sz,int Colour,int Style,int Align) |
||
176 | { |
||
177 | for (int n=0;n<Txt.length();n++) {int i=Txt.charAt(n); if (i>=32 && i<=127) charMask[i-32]=true;} |
||
178 | |||
179 | double[] metrics=measureText(Txt,Sz,Style); |
||
180 | if (Align==TXTALIGN_CENTRE) {updateBounds(X-0.5*metrics[0],Y-metrics[1]); updateBounds(X+0.5*metrics[0],Y+metrics[2]);} |
||
181 | else if (Align==TXTALIGN_LEFT) {updateBounds(X,Y-metrics[1]); updateBounds(X+metrics[0],Y+metrics[2]);} |
||
182 | else if (Align==TXTALIGN_RIGHT) {updateBounds(X-metrics[0],Y-metrics[1]); updateBounds(X,Y+metrics[2]);} |
||
183 | |||
184 | // assumes that HTML tags are not wanted |
||
185 | Txt=Txt.replace("&","&"); |
||
186 | Txt=Txt.replace("<","<"); |
||
187 | Txt=Txt.replace(">",">"); |
||
188 | |||
189 | TextType type=new TextType(); |
||
190 | type.Sz=Sz; |
||
191 | type.Colour=Colour; |
||
192 | type.Style=Style; |
||
193 | type.Align=Align; |
||
194 | |||
195 | TextAtom atom=new TextAtom(); |
||
196 | atom.AtomClass=ATOM_TEXT; |
||
197 | atom.X=X; atom.Y=Y; atom.Txt=Txt; |
||
198 | atom.TypeRef=registerTextType(type); |
||
199 | atoms.add(atom); |
||
200 | } |
||
201 | |||
202 | // measures a text string, at a given size; the return value has to be considered approximate; |
||
203 | // the return array is of the form {width,ascent,descent} |
||
204 | public double[] measureText(String Txt,double Sz,int Style) |
||
205 | { |
||
206 | int w=0; |
||
207 | for (int n=0;n<Txt.length();n++) |
||
208 | { |
||
209 | int i=Txt.charAt(n)-32; |
||
210 | if (i>=0 && i<96) w+=SVGFont.HORIZ_ADV_X[i]; else w+=SVGFont.MISSING_HORZ; |
||
211 | |||
212 | if (n<Txt.length()-1) |
||
213 | { |
||
214 | int j=Txt.charAt(n+1)-32; |
||
215 | for (int k=0;k<SVGFont.KERN_K.length;k++) |
||
216 | if ((SVGFont.KERN_G1[k]==i && SVGFont.KERN_G2[k]==j) || (SVGFont.KERN_G1[k]==j && SVGFont.KERN_G2[k]==i)) |
||
217 | {w+=SVGFont.KERN_K[k]; break;} |
||
218 | } |
||
219 | } |
||
220 | double[] ret=new double[3]; |
||
221 | ret[0]=Sz*w/SVGFont.UNITS_PER_EM; |
||
222 | ret[1]=Sz*SVGFont.ASCENT/SVGFont.UNITS_PER_EM; |
||
223 | ret[2]=Sz*-SVGFont.DESCENT/SVGFont.UNITS_PER_EM; |
||
224 | return ret; |
||
225 | } |
||
226 | |||
227 | // builds the SVG content proper; the output is expected to fit in a box of dimensions (0,0,W,H), which is specified in the parameters; |
||
228 | // the transformations (OX,OY,SW,SH) exist to facilitate fitting the result into this box; OX and OY are in destination units and are |
||
229 | // added after scaling, while SW and SH are fractional scaling factors, where 1==unchanged; anisotropic scaling is not recommended |
||
230 | public void build(PrintWriter Out,int W,int H,double OX,double OY,double SW,double SH) |
||
231 | { |
||
232 | if( MainPanel.appletMode ){ |
||
233 | g_id = MainApplet.g_id; |
||
7293 | schaersvoo | 234 | svg_id = MainApplet.svg_id; |
7296 | schaersvoo | 235 | Out.println("<svg onclick=\"javascript:SVG_zoom('"+svg_id+"','"+g_id+"','"+W+"','"+H+"');\" id=\""+svg_id+"\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\""); |
236 | Out.println(" version=\"1\" x=\"0\" y=\"0\" width=\""+W+"\" height=\""+H+"\" viewBox=\"0 0 "+W+" "+H+"\"><g id=\""+g_id+"\" transform=\"matrix(1 0 0 1 0 0)\">"); |
||
7246 | schaersvoo | 237 | } |
238 | else |
||
239 | { |
||
240 | Out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"); |
||
241 | Out.println("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); |
||
7296 | schaersvoo | 242 | Out.println("<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\""); |
243 | Out.println(" version=\"1\" x=\"0\" y=\"0\" width=\""+W+"\" height=\""+H+"\" viewBox=\"0 0 "+W+" "+H+"\"><g transform=\"matrix(1 0 0 1 0 0)\">"); |
||
7246 | schaersvoo | 244 | } |
245 | Out.println(); |
||
246 | // now write out the font definition |
||
247 | Out.println("<defs><font id=\""+SVGFont.FONT_FAMILY+"\" horiz-adv-x=\""+SVGFont.FONT_ADV+"\">"); |
||
248 | Out.println("<font-face font-family=\""+SVGFont.FONT_FAMILY+"\" units-per-em=\""+SVGFont.UNITS_PER_EM+"\" "+" panose-1=\""+SVGFont.PANOSE_1+"\" ascent=\""+SVGFont.ASCENT+"\" descent=\""+SVGFont.DESCENT+"\" alphabetic=\"0\"/>"); |
||
249 | Out.println("<missing-glyph horiz-adv-x=\""+SVGFont.MISSING_HORZ+"\" d=\""+SVGFont.MISSING_DATA+"\"/>"); |
||
250 | for (int n=0;n<96;n++) if (charMask[n]) |
||
251 | { |
||
252 | Out.print("<glyph unicode=\""+SVGFont.UNICODE[n]+"\" glyph-name=\""+SVGFont.GLYPH_NAME[n]+"\""+" horiz-adv-x=\""+SVGFont.HORIZ_ADV_X[n]+"\""); |
||
253 | |||
254 | if (SVGFont.GLYPH_DATA[n].length()>0) Out.print(" d=\""+SVGFont.GLYPH_DATA[n]+"\""); |
||
255 | Out.println("/>"); |
||
256 | } |
||
257 | for (int n=0;n<SVGFont.KERN_K.length;n++) if (charMask[SVGFont.KERN_G1[n]] && charMask[SVGFont.KERN_G2[n]]) |
||
258 | { |
||
259 | Out.println("<hkern g1=\""+SVGFont.KERN_G1[n]+"\" g2=\""+SVGFont.KERN_G2[n]+"\" k=\""+SVGFont.KERN_K[n]+"\"/>"); |
||
260 | } |
||
261 | |||
262 | Out.println("</font></defs>"); |
||
263 | Out.println(); |
||
264 | |||
265 | // transform everything |
||
266 | |||
267 | for (int n=0;n<atoms.size();n++) |
||
268 | { |
||
269 | Atom a=atoms.get(n); |
||
270 | if (a.AtomClass==ATOM_LINE) |
||
271 | { |
||
272 | LineAtom la=(LineAtom)a; |
||
273 | la.X1=OX+((la.X1-lowX)*SW+lowX); la.Y1=OY+((la.Y1-lowY)*SH+lowY); |
||
274 | la.X2=OX+((la.X2-lowX)*SW+lowX); la.Y2=OY+((la.Y2-lowY)*SH+lowY); |
||
275 | } |
||
276 | else if (a.AtomClass==ATOM_RECT) |
||
277 | { |
||
278 | RectAtom ra=(RectAtom)a; |
||
279 | ra.X=OX+((ra.X-lowX)*SW+lowX); ra.Y=OY+((ra.Y-lowY)*SH+lowY); |
||
280 | ra.W=ra.W*SW; ra.H=ra.H*SH; |
||
281 | } |
||
282 | else if (a.AtomClass==ATOM_OVAL) |
||
283 | { |
||
284 | OvalAtom oa=(OvalAtom)a; |
||
285 | oa.CX=OX+((oa.CX-lowX)*SW+lowX); oa.CY=OY+((oa.CY-lowY)*SH+lowY); |
||
286 | oa.RW*=SW; oa.RH*=SH; |
||
287 | } |
||
288 | else if (a.AtomClass==ATOM_PATH) |
||
289 | { |
||
290 | PathAtom pa=(PathAtom)a; |
||
291 | for (int i=0;i<pa.N;i++) {pa.X[i]=OX+((pa.X[i]-lowX)*SW+lowX); pa.Y[i]=OY+((pa.Y[i]-lowY)*SH+lowY);} |
||
292 | } |
||
293 | else if (a.AtomClass==ATOM_TEXT) |
||
294 | { |
||
295 | TextAtom ta=(TextAtom)a; |
||
296 | // !! ta.X=OX+(ta.X*SW); ta.Y=OY+(ta.Y*SH); |
||
297 | ta.X=OX+((ta.X-lowX)*SW+lowX); ta.Y=OY+((ta.Y-lowY)*SH+lowY); |
||
298 | } |
||
299 | } |
||
300 | double swsh=0.5*(SW+SH); |
||
301 | for (int n=0;n<lineTypes.size();n++) lineTypes.get(n).Thickness*=swsh; |
||
302 | for (int n=0;n<rectTypes.size();n++) rectTypes.get(n).Thickness*=swsh; |
||
303 | for (int n=0;n<ovalTypes.size();n++) ovalTypes.get(n).Thickness*=swsh; |
||
304 | for (int n=0;n<pathTypes.size();n++) pathTypes.get(n).Thickness*=swsh; |
||
305 | for (int n=0;n<textTypes.size();n++) textTypes.get(n).Sz*=swsh; |
||
306 | |||
307 | // emit everything, in singlets or in groups |
||
308 | int p=0; |
||
309 | while (p<atoms.size()) |
||
310 | { |
||
311 | Atom a=atoms.get(p); |
||
312 | int sz=1; |
||
313 | if (a.AtomClass!=ATOM_PATH) // (these are not rendered in groups) |
||
314 | for (int n=p+1;n<atoms.size();n++,sz++) |
||
315 | { |
||
316 | Atom ax=atoms.get(n); |
||
317 | if (a.TypeRef!=ax.TypeRef || a.AtomClass!=ax.AtomClass) break; |
||
318 | } |
||
319 | if (a.AtomClass==ATOM_LINE) {if (sz==1) outputLine1(Out,(LineAtom)a); else outputLineN(Out,(LineAtom)a,p,sz);} |
||
320 | else if (a.AtomClass==ATOM_RECT) {if (sz==1) outputRect1(Out,(RectAtom)a); else outputRectN(Out,(RectAtom)a,p,sz);} |
||
321 | else if (a.AtomClass==ATOM_OVAL) {if (sz==1) outputOval1(Out,(OvalAtom)a); else outputOvalN(Out,(OvalAtom)a,p,sz);} |
||
322 | else if (a.AtomClass==ATOM_PATH) outputPath(Out,(PathAtom)a); |
||
323 | else if (a.AtomClass==ATOM_TEXT) {if (sz==1) outputText1(Out,(TextAtom)a); else outputTextN(Out,(TextAtom)a,p,sz);} |
||
324 | |||
325 | p+=sz; |
||
326 | } |
||
327 | |||
328 | Out.println("</g></svg>"); |
||
329 | Out.flush(); |
||
330 | } |
||
331 | |||
332 | // a conveniently overloaded version which computes the size based on the properties of the drawing itself; the returned value provides |
||
333 | // the calculated width & height |
||
334 | public int[] build(PrintWriter Out) |
||
335 | { |
||
336 | int w=(int)Math.ceil(highX-lowX)+2,h=(int)Math.ceil(highY-lowY)+2; |
||
337 | double ox=1-lowX,oy=1-lowY; |
||
338 | build(Out,w,h,ox,oy,1,1); |
||
339 | return new int[]{w,h}; |
||
340 | } |
||
341 | |||
342 | // ------------------------------------------------ private functions ------------------------------------------------ |
||
343 | |||
344 | private void updateBounds(double X,double Y) |
||
345 | { |
||
346 | if (fresh) {lowX=highX=X; lowY=highY=Y; fresh=false;} |
||
347 | lowX=Math.min(lowX,X); |
||
348 | lowY=Math.min(lowY,Y); |
||
349 | highX=Math.max(highX,X); |
||
350 | highY=Math.max(highY,Y); |
||
351 | } |
||
352 | |||
353 | private int registerLineType(LineType T) |
||
354 | { |
||
355 | for (int n=0;n<lineTypes.size();n++) |
||
356 | { |
||
357 | LineType tx=lineTypes.get(n); |
||
358 | if (Util.dblEqual(T.Thickness,tx.Thickness) && T.Colour==tx.Colour) return n; |
||
359 | } |
||
360 | lineTypes.add(T); |
||
361 | return lineTypes.size()-1; |
||
362 | } |
||
363 | private int registerRectType(RectType T) |
||
364 | { |
||
365 | for (int n=0;n<rectTypes.size();n++) |
||
366 | { |
||
367 | RectType tx=rectTypes.get(n); |
||
368 | if (T.EdgeCol==tx.EdgeCol && Util.dblEqual(T.Thickness,tx.Thickness) && T.FillCol==tx.FillCol) return n; |
||
369 | } |
||
370 | rectTypes.add(T); |
||
371 | return rectTypes.size()-1; |
||
372 | } |
||
373 | private int registerOvalType(OvalType T) |
||
374 | { |
||
375 | for (int n=0;n<ovalTypes.size();n++) |
||
376 | { |
||
377 | OvalType tx=ovalTypes.get(n); |
||
378 | if (T.EdgeCol==tx.EdgeCol && Util.dblEqual(T.Thickness,tx.Thickness) && T.FillCol==tx.FillCol) return n; |
||
379 | } |
||
380 | ovalTypes.add(T); |
||
381 | return ovalTypes.size()-1; |
||
382 | } |
||
383 | private int registerPathType(PathType T) |
||
384 | { |
||
385 | for (int n=0;n<pathTypes.size();n++) |
||
386 | { |
||
387 | PathType tx=pathTypes.get(n); |
||
388 | if (T.EdgeCol==tx.EdgeCol && T.FillCol==tx.FillCol && |
||
389 | Util.dblEqual(T.Thickness,tx.Thickness) && T.HardEdge==tx.HardEdge) return n; |
||
390 | } |
||
391 | pathTypes.add(T); |
||
392 | return pathTypes.size()-1; |
||
393 | } |
||
394 | private int registerTextType(TextType T) |
||
395 | { |
||
396 | for (int n=0;n<textTypes.size();n++) |
||
397 | { |
||
398 | TextType tx=textTypes.get(n); |
||
399 | if (Util.dblEqual(T.Sz,tx.Sz) && T.Colour==tx.Colour && T.Style==tx.Style && T.Align==tx.Align) return n; |
||
400 | } |
||
401 | textTypes.add(T); |
||
402 | return textTypes.size()-1; |
||
403 | } |
||
404 | |||
405 | private void outputLine1(PrintWriter Out,LineAtom A) |
||
406 | { |
||
407 | LineType type=lineTypes.get(A.TypeRef); |
||
408 | Out.println( |
||
409 | "<line x1=\""+df.format(A.X1)+"\" y1=\""+df.format(A.Y1)+"\" x2=\""+df.format(A.X2)+"\" y2=\""+df.format(A.Y2)+"\""+" stroke=\""+Util.colourHTML(type.Colour)+"\" stroke-width=\""+type.Thickness+"\" stroke-linecap=\"round\"/>"); |
||
410 | } |
||
411 | private void outputLineN(PrintWriter Out,LineAtom A,int N,int Sz) |
||
412 | { |
||
413 | LineType type=lineTypes.get(A.TypeRef); |
||
414 | Out.println("<g stroke=\""+Util.colourHTML(type.Colour)+"\" stroke-width=\""+type.Thickness+"\" stroke-linecap=\"round\">"); |
||
415 | for (int n=0;n<Sz;n++) |
||
416 | { |
||
417 | LineAtom a=n==0 ? A : (LineAtom)atoms.get(N+n); |
||
418 | Out.println("<line x1=\""+df.format(a.X1)+"\" y1=\""+df.format(a.Y1)+"\" x2=\""+df.format(a.X2)+"\" y2=\""+df.format(a.Y2)+"\"/>"); |
||
419 | } |
||
420 | Out.println("</g>"); |
||
421 | } |
||
422 | private void outputRect1(PrintWriter Out,RectAtom A) |
||
423 | { |
||
424 | RectType type=rectTypes.get(A.TypeRef); |
||
425 | String edge=type.EdgeCol==NOCOLOUR ? "none" : Util.colourHTML(type.EdgeCol); |
||
426 | String fill=type.FillCol==NOCOLOUR ? "none" : Util.colourHTML(type.FillCol); |
||
427 | |||
428 | Out.println("<rect x=\""+df.format(A.X)+"\" y=\""+df.format(A.Y)+"\" width=\""+df.format(A.W)+"\" height=\""+df.format(A.H)+"\""+" stroke=\""+edge+"\" stroke-width=\""+type.Thickness+"\"fill=\""+fill+"\"/>"); |
||
429 | } |
||
430 | private void outputRectN(PrintWriter Out,RectAtom A,int N,int Sz) |
||
431 | { |
||
432 | RectType type=rectTypes.get(A.TypeRef); |
||
433 | String edge=type.EdgeCol==NOCOLOUR ? "none" : Util.colourHTML(type.EdgeCol); |
||
434 | String fill=type.FillCol==NOCOLOUR ? "none" : Util.colourHTML(type.FillCol); |
||
435 | |||
436 | Out.println("<g stroke=\""+edge+"\" stroke-width=\""+type.Thickness+"\" fill=\""+fill+"\">"); |
||
437 | for (int n=0;n<Sz;n++) |
||
438 | { |
||
439 | RectAtom a=n==0 ? A : (RectAtom)atoms.get(N+n); |
||
440 | Out.println("<rect x=\""+df.format(a.X)+"\" y=\""+df.format(a.Y)+"\" width=\""+df.format(a.W)+"\" height=\""+df.format(a.H)+"\"/>"); |
||
441 | } |
||
442 | Out.println("</g>"); |
||
443 | } |
||
444 | private void outputOval1(PrintWriter Out,OvalAtom A) |
||
445 | { |
||
446 | OvalType type=ovalTypes.get(A.TypeRef); |
||
447 | String edge=type.EdgeCol==NOCOLOUR ? "none" : Util.colourHTML(type.EdgeCol); |
||
448 | String fill=type.FillCol==NOCOLOUR ? "none" : Util.colourHTML(type.FillCol); |
||
449 | |||
450 | Out.println("<ellipse cx=\""+df.format(A.CX)+"\" cy=\""+df.format(A.CY)+"\" rx=\""+df.format(A.RW)+"\" ry=\""+df.format(A.RH)+"\""+" stroke=\""+edge+"\" stroke-width=\""+type.Thickness+"\" fill=\""+fill+"\"/>"); |
||
451 | } |
||
452 | private void outputOvalN(PrintWriter Out,OvalAtom A,int N,int Sz) |
||
453 | { |
||
454 | OvalType type=ovalTypes.get(A.TypeRef); |
||
455 | String edge=type.EdgeCol==NOCOLOUR ? "none" : Util.colourHTML(type.EdgeCol); |
||
456 | String fill=type.FillCol==NOCOLOUR ? "none" : Util.colourHTML(type.FillCol); |
||
457 | |||
458 | Out.println("<g stroke=\""+edge+"\" stroke-width=\""+type.Thickness+"\" fill=\""+fill+"\">"); |
||
459 | for (int n=0;n<Sz;n++) |
||
460 | { |
||
461 | OvalAtom a=n==0 ? A : (OvalAtom)atoms.get(N+n); |
||
462 | Out.println("<ellipse cx=\""+df.format(a.CX)+"\" cy=\""+df.format(a.CY)+"\" rx=\""+df.format(a.RW)+"\" ry=\""+df.format(a.RH)+"\"/>"); |
||
463 | } |
||
464 | Out.println("</g>"); |
||
465 | } |
||
466 | private void outputPath(PrintWriter Out,PathAtom A) |
||
467 | { |
||
468 | PathType type=pathTypes.get(A.TypeRef); |
||
469 | String edge=type.EdgeCol==NOCOLOUR ? "none" : Util.colourHTML(type.EdgeCol); |
||
470 | String fill=type.FillCol==NOCOLOUR ? "none" : Util.colourHTML(type.FillCol); |
||
471 | String join=type.HardEdge ? "miter" : "round",cap=type.HardEdge ? "square" : "round"; |
||
472 | String shape="M "+A.X[0]+" "+A.Y[0]; |
||
473 | int n=1; |
||
474 | while (n<A.N) |
||
475 | { |
||
476 | if (!A.Ctrl[n]) {shape+=" L "+A.X[n]+" "+A.Y[n]; n++;} |
||
477 | else if (A.Ctrl[n] && n<A.N-1 && !A.Ctrl[n+1]) |
||
478 | { |
||
479 | shape+=" Q "+A.X[n]+" "+A.Y[n]+" "+A.X[n+1]+" "+A.Y[n+1]; |
||
480 | n+=2; |
||
481 | } |
||
482 | else if (A.Ctrl[n] && n<A.N-2 && A.Ctrl[n+1] && !A.Ctrl[n+2]) |
||
483 | { |
||
484 | shape+=" C "+A.X[n]+" "+A.Y[n]+" "+A.X[n+1]+" "+A.Y[n+1]+" "+A.X[n+2]+" "+A.Y[n+2]; |
||
485 | n+=3; |
||
486 | } |
||
487 | else n++; // (dunno, so skip) |
||
488 | } |
||
489 | if (A.Closed) shape+=" Z"; |
||
490 | |||
491 | Out.println("<path d=\""+shape+"\" stroke=\""+edge+"\" fill=\""+fill+"\" stroke-width=\""+type.Thickness+"\""+" stroke-linejoin=\""+join+"\" stroke-linecap=\""+cap+"\"/>"); |
||
492 | } |
||
493 | private void outputText1(PrintWriter Out,TextAtom A) |
||
494 | { |
||
495 | TextType type=textTypes.get(A.TypeRef); |
||
496 | String anchor=type.Align==TXTALIGN_LEFT ? "start" : type.Align==TXTALIGN_RIGHT ? "end" : "middle"; |
||
497 | |||
498 | // !! don't forget style... |
||
499 | |||
500 | Out.println( |
||
501 | "<text x=\""+df.format(A.X)+"\" y=\""+df.format(A.Y)+"\" font-family=\"Verdana\" font-size=\""+type.Sz+"\""+" text-anchor=\""+anchor+"\" fill=\""+Util.colourHTML(type.Colour)+"\">"+A.Txt+"</text>"); |
||
502 | } |
||
503 | private void outputTextN(PrintWriter Out,TextAtom A,int N,int Sz) |
||
504 | { |
||
505 | TextType type=textTypes.get(A.TypeRef); |
||
506 | String anchor=type.Align==TXTALIGN_LEFT ? "start" : type.Align==TXTALIGN_RIGHT ? "end" : "middle"; |
||
507 | |||
508 | // !! don't forget style... |
||
509 | |||
510 | Out.println( |
||
511 | "<g font-family=\"Verdana\" font-size=\""+type.Sz+"\""+" text-anchor=\""+anchor+"\" fill=\""+Util.colourHTML(type.Colour)+"\">"); |
||
512 | for (int n=0;n<Sz;n++) |
||
513 | { |
||
514 | TextAtom a=n==0 ? A : (TextAtom)atoms.get(N+n); |
||
515 | Out.println("<text x=\""+df.format(a.X)+"\" y=\""+df.format(a.Y)+"\">"+a.Txt+"</text>"); |
||
516 | } |
||
517 | Out.println("</g>"); |
||
518 | } |
||
519 | } |