Source code: com/xerox/VTM/svg/SVGReader.java
1 /* FILE: SVGReader.java
2 * DATE OF CREATION: Oct 16 2001
3 * AUTHOR : Emmanuel Pietriga (emmanuel.pietriga@xrce.xerox.com)
4 * MODIF: Wed Aug 06 09:06:23 2003 by Emmanuel Pietriga (emmanuel@w3.org, emmanuel@claribole.net)
5 * Copyright (c) Xerox Corporation, XRCE/Contextual Computing, 2002. All Rights Reserved
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * For full terms see the file COPYING.
18 */
19
20 package com.xerox.VTM.svg;
21
22 import java.util.Vector;
23 import java.util.StringTokenizer;
24 import java.awt.Color;
25 import java.awt.Point;
26 import java.awt.Font;
27 import com.xerox.VTM.engine.*;
28 import com.xerox.VTM.glyphs.*;
29
30 import org.w3c.dom.Element;
31 import org.w3c.dom.Document;
32 import org.w3c.dom.NodeList;
33 import org.w3c.dom.Node;
34
35 /**
36 *An SVG interpreter for VTM - for now it covers a <i><b>very</b></i> limited subset of the specification (just enough to interpret GraphViz/Dot SVG output (Ellipse, Text, Path and Rectangle + limited support for Polygon)).
37 *@author Emmanuel Pietriga
38 */
39
40 public class SVGReader {
41
42 public static String _polyline="polyline";
43 public static String _line="line";
44 public static String _rect="rect";
45 public static String _image="image";
46 public static String _ellipse="ellipse";
47 public static String _circle="circle";
48 public static String _path="path";
49 public static String _text="text";
50 public static String _polygon="polygon";
51 public static String _g="g";
52 public static String _a="a";
53 public static String _title="title";
54 public static String _fill="fill:";
55 public static String _stroke="stroke:";
56 public static String _strokewidth="stroke-width:";
57 public static String _fillopacity="fill-opacity:";
58 public static String _fontfamily="font-family:";
59 public static String _fontsize="font-size:";
60 public static String _fontweight="font-weight:";
61 public static String _fontstyle="font-style:";
62 public static String _style="style";
63 public static String _cx="cx";
64 public static String _cy="cy";
65 public static String _rx="rx";
66 public static String _ry="ry";
67 public static String _r="r";
68 public static String _d="d";
69 public static String _x="x";
70 public static String _y="y";
71 public static String _width="width";
72 public static String _height="height";
73 public static String _points="points";
74 public static String _textanchor="text-anchor";
75 public static String _start="start";
76 public static String _middle="middle";
77 public static String _end="end";
78 public static String _inherit="inherit";
79
80
81 static long xoffset=0;
82 static long yoffset=0;
83
84 public static float RRARCR=0.4f;
85
86 /**When this is set to something different than 0, all SVG objects will be translated by (dx,dy) in their VTM virtual space. This can be useful if you do not want all objects of the SVG file to all be in the south-east quadrant of the virtual space (SVG files often use positive coordinates only, and their coordinate system is inversed (vertically) w.r.t VTM's coordinate system)*/
87 public static void setPositionOffset(long dx,long dy){
88 xoffset=dx;
89 yoffset=dy;
90 }
91
92 /**get the position offset (0,0 means no offset)*/
93 public static LongPoint getPositionOffset(){
94 return new LongPoint(xoffset,yoffset);
95 }
96
97 /**check that an SVG path value is supported by the VTM. This does not test for well-formedness of the expression. It just verifies that all SVG commands are actually supported by the VTM (M,m,L,l,H,h,V,v,C,c,Q,q)*/
98 public static boolean checkSVGPath(String svg){
99 boolean res=true;
100 byte[] chrs=svg.getBytes();
101 for (int i=0;i<chrs.length;i++){
102 if (!((chrs[i]==32) || (chrs[i]==45) || ((chrs[i]>=48) && (chrs[i]<=57)) || (chrs[i]==67) || (chrs[i]==72) || (chrs[i]==76) || (chrs[i]==77) || (chrs[i]==81) || (chrs[i]==86) || (chrs[i]==99) || (chrs[i]==104) || (chrs[i]==108) || (chrs[i]==109) || (chrs[i]==113) || (chrs[i]==118) || (chrs[i]==44))){res=false;System.err.println("SVG Path: char '"+svg.substring(i,i+1)+"' not supported");break;}
103 }
104 return res;
105 }
106
107 private static void processNextSVGPathCommand(StringBuffer svg,VPath ph,StringBuffer lastCommand){
108 if (svg.length()>0) {
109 switch (svg.charAt(0)){
110 case 'M':{
111 svg.deleteCharAt(0);
112 long x=getNextNumber(svg)+xoffset;
113 //seekSecondCoord(svg);
114 long y=getNextNumber(svg)+yoffset;
115 ph.jump(x,-y,true);
116 lastCommand.setCharAt(0,'M');
117 break;
118 }
119 case 'm':{
120 svg.deleteCharAt(0);
121 long x=getNextNumber(svg)+xoffset;
122 //seekSecondCoord(svg);
123 long y=getNextNumber(svg)+yoffset;
124 ph.jump(x,-y,false);
125 lastCommand.setCharAt(0,'m');
126 break;
127 }
128 case 'L':{
129 svg.deleteCharAt(0);
130 long x=getNextNumber(svg)+xoffset;
131 //seekSecondCoord(svg);
132 long y=getNextNumber(svg)+yoffset;
133 ph.addSegment(x,-y,true);
134 lastCommand.setCharAt(0,'L');
135 break;
136 }
137 case 'l':{
138 svg.deleteCharAt(0);
139 long x=getNextNumber(svg)+xoffset;
140 //seekSecondCoord(svg);
141 long y=getNextNumber(svg)+yoffset;
142 ph.addSegment(x,-y,false);
143 lastCommand.setCharAt(0,'l');
144 break;
145 }
146 case 'H':{
147 svg.deleteCharAt(0);
148 long x=getNextNumber(svg)+xoffset;
149 ph.addSegment(x,0,true);
150 lastCommand.setCharAt(0,'H');
151 break;
152 }
153 case 'h':{
154 svg.deleteCharAt(0);
155 long x=getNextNumber(svg)+xoffset;
156 ph.addSegment(x,0,false);
157 lastCommand.setCharAt(0,'h');
158 break;
159 }
160 case 'V':{
161 svg.deleteCharAt(0);
162 long y=getNextNumber(svg)+yoffset;
163 ph.addSegment(0,-y,true);
164 lastCommand.setCharAt(0,'V');
165 break;
166 }
167 case 'v':{
168 svg.deleteCharAt(0);
169 long y=getNextNumber(svg)+yoffset;
170 ph.addSegment(0,-y,false);
171 lastCommand.setCharAt(0,'v');
172 break;
173 }
174 case 'C':{
175 svg.deleteCharAt(0);
176 long x1=getNextNumber(svg)+xoffset;
177 //seekSecondCoord(svg);
178 long y1=getNextNumber(svg)+yoffset;
179 long x2=getNextNumber(svg)+xoffset;
180 //seekSecondCoord(svg);
181 long y2=getNextNumber(svg)+yoffset;
182 long x=getNextNumber(svg)+xoffset;
183 //seekSecondCoord(svg);
184 long y=getNextNumber(svg)+yoffset;
185 ph.addCbCurve(x,-y,x1,-y1,x2,-y2,true);
186 lastCommand.setCharAt(0,'C');
187 break;
188 }
189 case 'c':{
190 svg.deleteCharAt(0);
191 long x1=getNextNumber(svg)+xoffset;
192 //seekSecondCoord(svg);
193 long y1=getNextNumber(svg)+yoffset;
194 long x2=getNextNumber(svg)+xoffset;
195 //seekSecondCoord(svg);
196 long y2=getNextNumber(svg)+yoffset;
197 long x=getNextNumber(svg)+xoffset;
198 //seekSecondCoord(svg);
199 long y=getNextNumber(svg)+yoffset;
200 ph.addCbCurve(x,-y,x1,-y1,x2,-y2,false);
201 lastCommand.setCharAt(0,'c');
202 break;
203 }
204 case 'Q':{
205 svg.deleteCharAt(0);
206 long x1=getNextNumber(svg)+xoffset;
207 //seekSecondCoord(svg);
208 long y1=getNextNumber(svg)+yoffset;
209 long x=getNextNumber(svg)+xoffset;
210 //seekSecondCoord(svg);
211 long y=getNextNumber(svg)+yoffset;
212 ph.addQdCurve(x,-y,x1,-y1,true);
213 lastCommand.setCharAt(0,'Q');
214 break;
215 }
216 case 'q':{
217 svg.deleteCharAt(0);
218 long x1=getNextNumber(svg)+xoffset;
219 //seekSecondCoord(svg);
220 long y1=getNextNumber(svg)+yoffset;
221 long x=getNextNumber(svg)+xoffset;
222 //seekSecondCoord(svg);
223 long y=getNextNumber(svg)+yoffset;
224 ph.addQdCurve(x,-y,x1,-y1,false);
225 lastCommand.setCharAt(0,'q');
226 break;
227 }
228 default:{//same command as previous point
229 //the string has been checked by checkSVGPath, therefore only possible chars are digits 1234567890 and -
230 switch (lastCommand.charAt(0)){
231 case 'M':{
232 long x=getNextNumber(svg)+xoffset;
233 //seekSecondCoord(svg);
234 long y=getNextNumber(svg)+yoffset;
235 ph.jump(x,-y,true);
236 break;
237 }
238 case 'm':{
239 long x=getNextNumber(svg)+xoffset;
240 //seekSecondCoord(svg);
241 long y=getNextNumber(svg)+yoffset;
242 ph.jump(x,-y,false);
243 break;
244 }
245 case 'L':{
246 long x=getNextNumber(svg)+xoffset;
247 //seekSecondCoord(svg);
248 long y=getNextNumber(svg)+yoffset;
249 ph.addSegment(x,-y,true);
250 break;
251 }
252 case 'l':{
253 long x=getNextNumber(svg)+xoffset;
254 //seekSecondCoord(svg);
255 long y=getNextNumber(svg)+yoffset;
256 ph.addSegment(x,-y,false);
257 break;
258 }
259 case 'H':{
260 long x=getNextNumber(svg)+xoffset;
261 ph.addSegment(x,0,true);
262 break;
263 }
264 case 'h':{
265 long x=getNextNumber(svg)+xoffset;
266 ph.addSegment(x,0,false);
267 break;
268 }
269 case 'V':{
270 long y=getNextNumber(svg)+yoffset;
271 ph.addSegment(0,-y,true);
272 break;
273 }
274 case 'v':{
275 long y=getNextNumber(svg)+yoffset;
276 ph.addSegment(0,-y,false);
277 break;
278 }
279 case 'C':{
280 long x1=getNextNumber(svg)+xoffset;
281 //seekSecondCoord(svg);
282 long y1=getNextNumber(svg)+yoffset;
283 long x2=getNextNumber(svg)+xoffset;
284 //seekSecondCoord(svg);
285 long y2=getNextNumber(svg)+yoffset;
286 long x=getNextNumber(svg)+xoffset;
287 //seekSecondCoord(svg);
288 long y=getNextNumber(svg)+yoffset;
289 ph.addCbCurve(x,-y,x1,-y1,x2,-y2,true);
290 break;
291 }
292 case 'c':{
293 long x1=getNextNumber(svg)+xoffset;
294 //seekSecondCoord(svg);
295 long y1=getNextNumber(svg)+yoffset;
296 long x2=getNextNumber(svg)+xoffset;
297 //seekSecondCoord(svg);
298 long y2=getNextNumber(svg)+yoffset;
299 long x=getNextNumber(svg)+xoffset;
300 //seekSecondCoord(svg);
301 long y=getNextNumber(svg)+yoffset;
302 ph.addCbCurve(x,-y,x1,-y1,x2,-y2,false);
303 break;
304 }
305 case 'Q':{
306 long x1=getNextNumber(svg)+xoffset;
307 //seekSecondCoord(svg);
308 long y1=getNextNumber(svg)+yoffset;
309 long x=getNextNumber(svg)+xoffset;
310 //seekSecondCoord(svg);
311 long y=getNextNumber(svg)+yoffset;
312 ph.addQdCurve(x,-y,x1,-y1,true);
313 break;
314 }
315 case 'q':{
316 long x1=getNextNumber(svg)+xoffset;
317 //seekSecondCoord(svg);
318 long y1=getNextNumber(svg)+yoffset;
319 long x=getNextNumber(svg)+xoffset;
320 //seekSecondCoord(svg);
321 long y=getNextNumber(svg)+yoffset;
322 ph.addQdCurve(x,-y,x1,-y1,false);
323 break;
324 }
325 }
326 }
327 }
328 }
329 }
330
331 /**
332 *@param s string representation of a color (as an SVG style attribute)
333 */
334 public static Color getColor(String s){
335 try {
336 if (s.startsWith("rgb(")){//color expressed as rgb(R,G,B)
337 //ar should be of length 3
338 StringTokenizer st=new StringTokenizer(s.substring(4,s.length()-1),",");
339 String[] ar=new String[st.countTokens()];
340 int i=0;
341 while (st.hasMoreTokens()){
342 ar[i++]=st.nextToken();
343 }
344 if (ar[0].endsWith("%")){//format is (x%,y%,z%) with x,y and z between 0 and 100
345 float r=(new Float(ar[0].substring(0,ar[0].length()-1))).floatValue()/100.0f;
346 float g=(new Float(ar[1].substring(0,ar[1].length()-1))).floatValue()/100.0f;
347 float b=(new Float(ar[2].substring(0,ar[2].length()-1))).floatValue()/100.0f;
348 return new Color(r,g,b);
349 }
350 else {//format is (x,y,z) with x,y and z between 0 and 255
351 int r=(new Float(ar[0])).intValue();
352 int g=(new Float(ar[1])).intValue();
353 int b=(new Float(ar[2])).intValue();
354 return new Color(r,g,b);
355 }
356 }
357 else if (s.startsWith("#")){
358 String s2;
359 if (s.length()==4){//#FB0 is transformed into #FFBB00 according to http://www.w3.org/TR/SVG/types.html#DataTypeColor
360 s2=s.substring(1,2)+s.substring(1,2)+s.substring(2,3)+s.substring(2,3)+s.substring(3,4)+s.substring(3,4);
361 }
362 else s2=s.substring(1,s.length());
363 int r=Integer.parseInt(s2.substring(0,2),16); //hexadecimal base (radix 16)
364 int g=Integer.parseInt(s2.substring(2,4),16);
365 int b=Integer.parseInt(s2.substring(4,6),16);
366 //System.err.println(r+" "+g+" "+b);
367 return new Color(r,g,b);
368 }
369 else if (s.startsWith("none")){
370 return null;
371 }
372 else {
373 Color c;
374 if ((c=Utilities.getColorByKeyword(s))!=null){return c;}
375 else return null;
376 }
377 }
378 catch (Exception ex){System.err.println("Error: SVGReader.getColor(): "+ex);return Color.white;}
379 }
380
381 /**
382 *@param s the value of an SVG style attribute
383 *@return styling attributes which can be interpreted by the VTM (color, transparency)
384 */
385 public static SVGStyle getStyle(String s){
386 //Vector ar=Utilities.getSepElements(s,";");
387 String[] ar=null;
388 if (s!=null){
389 StringTokenizer st=new StringTokenizer(s,";");
390 ar=new String[st.countTokens()];
391 int i=0;
392 while (st.hasMoreTokens()){
393 ar[i++]=st.nextToken();
394 }
395 }
396 if (ar!=null){
397 SVGStyle ss=new SVGStyle();
398 for (int i=0;i<ar.length;i++){
399 if (ar[i].startsWith(_fill)){ss.setFillColor(getColor(ar[i].substring(5,ar[i].length())));}
400 else if (ar[i].startsWith(_stroke)){ss.setBorderColor(getColor(ar[i].substring(7,ar[i].length())));}
401 else if (ar[i].startsWith(_fillopacity)){ss.setAlphaTransparencyValue(new Float(ar[i].substring(13,ar[i].length())));}
402 }
403 return ss;
404 }
405 else return null;
406 }
407
408 /**Translate an SVG polygon coordinates from the SVG space to the VTM space, taking position offset into account
409 *@param s the SVG list of coordinates (value of attribute <i>points</i> in <i>polygon</i> elements)
410 *@param res a Vector in which the result will be stored (this will be a vector of VTM LongPoint)
411 */
412 public static void translateSVGPolygon(String s,Vector res){
413 StringBuffer svg=new StringBuffer(s);
414 while (svg.length()>0){
415 Utilities.delLeadingSpaces(svg);
416 processNextSVGCoords(svg,res);
417 }
418 }
419
420 //seek and consume next pair of numerical coordinates in a string
421 private static void processNextSVGCoords(StringBuffer svg,Vector res){
422 if (svg.length()>0){
423 long x=getNextNumber(svg)+xoffset;
424 //seekSecondCoord(svg);
425 long y=getNextNumber(svg)+yoffset;
426 res.add(new LongPoint(x,y));
427 }
428 }
429
430 //utility method used by processNextSVGCoords()
431 private static void seekSecondCoord(StringBuffer sb){
432 Utilities.delLeadingSpaces(sb);
433 while ((sb.length()>0) && ((Character.isWhitespace(sb.charAt(0))) || (sb.charAt(0)==','))){
434 sb.deleteCharAt(0);
435 }
436 }
437
438 //utility method used by processNextSVGCoords()
439 static int getNextNumber(StringBuffer sb){
440 int res=0;
441 //Utilities.delLeadingSpaces(sb);
442 seekSecondCoord(sb);
443 StringBuffer dgb=new StringBuffer();
444 while ((sb.length()>0) && ((Character.isDigit(sb.charAt(0))) || (sb.charAt(0)=='-'))){
445 dgb.append(sb.charAt(0));
446 sb.deleteCharAt(0);
447 }
448 if (dgb.length()>0){res=(Integer.valueOf(dgb.toString())).intValue();}
449 return res;
450 }
451
452 /**
453 *@param v a vector of 4 LongPoint. For this to return true, points 2 & 3 and points 1 & 4 have to be horizontally aligned. Moreother, points 1 & 2 and points 3 & 4 have to be vertically aligned.
454 */
455 public static boolean isRectangle(Vector v){
456 if (v.size()==4 || v.size()==5){ //should be 5, since the polygon has to be closed (first_point==last_point)
457 LongPoint p1=(LongPoint)v.elementAt(0);
458 LongPoint p2=(LongPoint)v.elementAt(1);
459 LongPoint p3=(LongPoint)v.elementAt(2);
460 LongPoint p4=(LongPoint)v.elementAt(3);
461 if (((p2.x==p3.x) && (p1.y==p2.y) && (p3.y==p4.y) && (p1.x==p4.x)) || ((p2.y==p3.y) && (p1.x==p2.x) && (p3.x==p4.x) && (p1.y==p4.y))){return true;}
462 else {return false;}
463 }
464 else return false;
465 }
466
467 /**create a VEllipse from an SVG ellipse element
468 *@param e an SVG ellipse as a DOM element (org.w3c.dom.Element)
469 */
470 public static VEllipse createEllipse(Element e){
471 return createEllipse(e,null);
472 }
473
474 /**create a VEllipse from an SVG ellipse element
475 *@param e an SVG ellipse as a DOM element (org.w3c.dom.Element)
476 *@param ctx used to propagate contextual style information (put null if none)
477 */
478 public static VEllipse createEllipse(Element e,Context ctx){
479 long x=(Long.valueOf(e.getAttribute(_cx))).longValue()+xoffset;
480 long y=(Long.valueOf(e.getAttribute(_cy))).longValue()+yoffset;
481 long w=(Long.valueOf(e.getAttribute(_rx))).longValue();
482 long h=(Long.valueOf(e.getAttribute(_ry))).longValue();
483 VEllipse res;
484 if (e.hasAttribute(_style)){
485 SVGStyle ss=getStyle(e.getAttribute(_style));
486 if (ss.hasTransparencyInformation()){
487 if (ss.getFillColor()==null){res=new VEllipseST(x,-y,0,w,h,Color.white);res.setFill(false);}
488 else {res=new VEllipseST(x,-y,0,w,h,ss.getFillColor());}
489 ((Transparent)res).setTransparencyValue(ss.getAlphaTransparencyValue());
490 }
491 else {
492 if (ss.getFillColor()==null){res=new VEllipse(x,-y,0,w,h,Color.white);res.setFill(false);}
493 else {res=new VEllipse(x,-y,0,w,h,ss.getFillColor());}
494 }
495 Color border=ss.getBorderColor();
496 if (border!=null){
497 float[] hsv=Color.RGBtoHSB(border.getRed(),border.getGreen(),border.getBlue(),new float[3]);
498 res.setHSVbColor(hsv[0],hsv[1],hsv[2]);
499 }
500 }
501 else if (ctx!=null){
502 if (ctx.hasTransparencyInformation()){
503 if (ctx.getFillColor()==null){res=new VEllipseST(x,-y,0,w,h,Color.white);res.setFill(false);}
504 else {res=new VEllipseST(x,-y,0,w,h,ctx.getFillColor());}
505 ((Transparent)res).setTransparencyValue(ctx.getAlphaTransparencyValue());
506 }
507 else {
508 if (ctx.getFillColor()==null){res=new VEllipse(x,-y,0,w,h,Color.white);res.setFill(false);}
509 else {res=new VEllipse(x,-y,0,w,h,ctx.getFillColor());}
510 }
511 Color border=ctx.getBorderColor();
512 if (border!=null){
513 float[] hsv=Color.RGBtoHSB(border.getRed(),border.getGreen(),border.getBlue(),new float[3]);
514 res.setHSVbColor(hsv[0],hsv[1],hsv[2]);
515 }
516 }
517 else {res=new VEllipse(x,-y,0,w,h,Color.white);}
518 return res;
519 }
520
521 /**create a VCircle from an SVG circle element
522 *@param e an SVG circle as a DOM element (org.w3c.dom.Element)
523 */
524 public static VCircle createCircle(Element e){
525 return createCircle(e,null);
526 }
527
528 /**create a VCircle from an SVG circle element
529 *@param e an SVG circle as a DOM element (org.w3c.dom.Element)
530 *@param ctx used to propagate contextual style information (put null if none)
531 */
532 public static VCircle createCircle(Element e,Context ctx){
533 long x=(Long.valueOf(e.getAttribute(_cx))).longValue()+xoffset;
534 long y=(Long.valueOf(e.getAttribute(_cy))).longValue()+yoffset;
535 long r=(Long.valueOf(e.getAttribute(_r))).longValue();
536 VCircle res;
537 if (e.hasAttribute(_style)){
538 SVGStyle ss=getStyle(e.getAttribute(_style));
539 if (ss.hasTransparencyInformation()){
540 if (ss.getFillColor()==null){res=new VCircleST(x,-y,0,r,Color.white);res.setFill(false);}
541 else {res=new VCircleST(x,-y,0,r,ss.getFillColor());}
542 ((Transparent)res).setTransparencyValue(ss.getAlphaTransparencyValue());
543 }
544 else {
545 if (ss.getFillColor()==null){res=new VCircle(x,-y,0,r,Color.white);res.setFill(false);}
546 else {res=new VCircle(x,-y,0,r,ss.getFillColor());}
547 }
548 Color border=ss.getBorderColor();
549 float[] hsv=Color.RGBtoHSB(border.getRed(),border.getGreen(),border.getBlue(),new float[3]);
550 res.setHSVbColor(hsv[0],hsv[1],hsv[2]);
551 }
552 else {res=new VCircle(x,-y,0,r,Color.white);}
553 return res;
554 }
555
556 /**create a VText from an SVG text element - warning if text uses attribute text-anchor and has a value different from start, it will not be taken into account (it is up to you to place the text correctly, as it requires information about the View's graphicscontext to compute the string's width/height)
557 *@param e an SVG text as a DOM element (org.w3c.dom.Element)
558 *@param vsm the virtual space manager (to get some font information)
559 */
560 public static VText createText(Element e,VirtualSpaceManager vsm){
561 return createText(e,null,vsm);
562 }
563
564 /**create a VText from an SVG text element - warning if text uses attribute text-anchor and has a value different from start, it will not be taken into account (it is up to you to place the text correctly, as it requires information about the View's graphicscontext to compute the string's width/height)
565 *@param e an SVG text as a DOM element (org.w3c.dom.Element)
566 *@param ctx used to propagate contextual style information (put null if none)
567 *@param vsm the virtual space manager (to get some font information)
568 */
569 public static VText createText(Element e,Context ctx,VirtualSpaceManager vsm){
570 String tx=(e.getFirstChild()==null) ? "" : e.getFirstChild().getNodeValue();
571 long x=(Integer.valueOf(e.getAttribute(_x))).longValue()+xoffset;
572 long y=(Integer.valueOf(e.getAttribute(_y))).longValue()+yoffset;
573 VText res;
574 short ta=VText.TEXT_ANCHOR_START;
575 if (e.hasAttribute(_textanchor)){
576 String tas=e.getAttribute(_textanchor);
577 if (tas.equals(_middle)){ta=VText.TEXT_ANCHOR_MIDDLE;}
578 else if (tas.equals(_end)){ta=VText.TEXT_ANCHOR_END;}
579 else if (tas.equals(_inherit)){System.err.println("SVGReader::'inherit' value for text-anchor attribute not supported yet");}
580 }
581 if (e.hasAttribute(_style)){
582 SVGStyle ss=getStyle(e.getAttribute(_style));
583 if (ss.getBorderColor()==null){
584 if (ss.getFillColor()!=null){
585 res=new VText(x,-y,0,ss.getFillColor(),tx,ta);
586 }
587 else {res=new VText(x,-y,0,Color.black,tx,ta);}
588 }
589 else {res=new VText(x,-y,0,ss.getBorderColor(),tx,ta);}
590 }
591 else {res=new VText(x,-y,0,Color.black,tx,ta);}
592 Font f;
593 if (ctx!=null && specialFont(f=ctx.getDefinedFont(),vsm.getMainFont())){
594 res.setSpecialFont(ctx.getDefinedFont());
595 }
596 return res;
597 }
598
599 /**create a VRectangle from an SVG polygon element (after checking this is actually a rectangle - returns null if not)
600 *@param e an SVG polygon as a DOM element (org.w3c.dom.Element)
601 */
602 public static VRectangleOr createRectangleFromPolygon(Element e){
603 return createRectangleFromPolygon(e,null);
604 }
605
606 /**create a VRectangle from an SVG polygon element (after checking this is actually a rectangle - returns null if not)
607 *@param e an SVG polygon as a DOM element (org.w3c.dom.Element)
608 *@param ctx used to propagate contextual style information (put null if none)
609 */
610 public static VRectangleOr createRectangleFromPolygon(Element e,Context ctx){
611 Vector coords=new Vector();
612 translateSVGPolygon(e.getAttribute(_points),coords);
613 if (isRectangle(coords)){
614 LongPoint p1=(LongPoint)coords.elementAt(0);
615 LongPoint p2=(LongPoint)coords.elementAt(1);
616 LongPoint p3=(LongPoint)coords.elementAt(2);
617 LongPoint p4=(LongPoint)coords.elementAt(3);
618 //the ordering of points may vary, so we must identify what point stands for what corner
619 LongPoint pNW,pNE,pSE,pSW;
620 long l=Math.min(p1.x,Math.min(p2.x,Math.min(p3.x,p4.x)));
621 long u=Math.max(p1.y,Math.max(p2.y,Math.max(p3.y,p4.y)));
622 long r=Math.max(p1.x,Math.max(p2.x,Math.max(p3.x,p4.x)));
623 long d=Math.min(p1.y,Math.min(p2.y,Math.min(p3.y,p4.y)));
624 pNW=new LongPoint(l,u);
625 pNE=new LongPoint(r,u);
626 pSE=new LongPoint(r,d);
627 pSW=new LongPoint(l,d);
628 long h=Math.abs(pSE.y-pNE.y);
629 long w=Math.abs(pNW.x-pNE.x);
630 long x=pNE.x-w/2;
631 long y=pNE.y-h/2;
632 VRectangleOr res;
633 if (e.hasAttribute(_style)){
634 SVGStyle ss=getStyle(e.getAttribute(_style));
635 if (ss.hasTransparencyInformation()){
636 if (ss.getFillColor()==null){res=new VRectangleOrST(x,-y,0,w/2,h/2,Color.white,0);res.setFill(false);}
637 else {res=new VRectangleOrST(x,-y,0,w/2,h/2,ss.getFillColor(),0);}
638 ((Transparent)res).setTransparencyValue(ss.getAlphaTransparencyValue());
639 }
640 else {
641 if (ss.getFillColor()==null){res=new VRectangleOr(x,-y,0,w/2,h/2,Color.white,0);res.setFill(false);}
642 else {res=new VRectangleOr(x,-y,0,w/2,h/2,ss.getFillColor(),0);}
643 }
644 Color border=ss.getBorderColor();
645 float[] hsv=Color.RGBtoHSB(border.getRed(),border.getGreen(),border.getBlue(),new float[3]);
646 res.setHSVbColor(hsv[0],hsv[1],hsv[2]);
647 }
648 else if (ctx!=null){
649 if (ctx.hasTransparencyInformation()){
650 if (ctx.getFillColor()==null){res=new VRectangleOrST(x,-y,0,w/2,h/2,Color.white,0);res.setFill(false);}
651 else {res=new VRectangleOrST(x,-y,0,w/2,h/2,ctx.getFillColor(),0);}
652 ((Transparent)res).setTransparencyValue(ctx.getAlphaTransparencyValue());
653 }
654 else {
655 if (ctx.getFillColor()==null){res=new VRectangleOr(x,-y,0,w/2,h/2,Color.white,0);res.setFill(false);}
656 else {res=new VRectangleOr(x,-y,0,w/2,h/2,ctx.getFillColor(),0);}
657 }
658 Color border=ctx.getBorderColor();
659 if (border!=null){
660 float[] hsv=Color.RGBtoHSB(border.getRed(),border.getGreen(),border.getBlue(),new float[3]);
661 res.setHSVbColor(hsv[0],hsv[1],hsv[2]);
662 }
663 }
664 else {res=new VRectangleOr(x,-y,0,w/2,h/2,Color.white,0);}
665 return res;
666 }
667 else return null;
668 }
669
670 /**create a VRoundRect from an SVG polygon element (after checking this is actually a rectangle - returns null if not)
671 *@param e an SVG polygon as a DOM element (org.w3c.dom.Element)
672 */
673 public static VRoundRect createRoundRectFromPolygon(Element e){
674 return createRoundRectFromPolygon(e,null);
675 }
676
677 /**create a VRoundRect from an SVG polygon element (after checking this is actually a rectangle - returns null if not)
678 *@param e an SVG polygon as a DOM element (org.w3c.dom.Element)
679 *@param ctx used to propagate contextual style information (put null if none)
680 */
681 public static VRoundRect createRoundRectFromPolygon(Element e,Context ctx){
682 Vector coords=new Vector();
683 translateSVGPolygon(e.getAttribute(_points),coords);
684 if (isRectangle(coords)){
685 LongPoint p1=(LongPoint)coords.elementAt(0);
686 LongPoint p2=(LongPoint)coords.elementAt(1);
687 LongPoint p3=(LongPoint)coords.elementAt(2);
688 LongPoint p4=(LongPoint)coords.elementAt(3);
689 //the ordering of points may vary, so we must identify what point stands for what corner
690 LongPoint pNW,pNE,pSE,pSW;
691 long l=Math.min(p1.x,Math.min(p2.x,Math.min(p3.x,p4.x)));
692 long u=Math.max(p1.y,Math.max(p2.y,Math.max(p3.y,p4.y)));
693 long r=Math.max(p1.x,Math.max(p2.x,Math.max(p3.x,p4.x)));
694 long d=Math.min(p1.y,Math.min(p2.y,Math.min(p3.y,p4.y)));
695 pNW=new LongPoint(l,u);
696 pNE=new LongPoint(r,u);
697 pSE=new LongPoint(r,d);
698 pSW=new LongPoint(l,d);
699 long h=Math.abs(pSE.y-pNE.y);
700 long w=Math.abs(pNW.x-pNE.x);
701 long x=pNE.x-w/2;
702 long y=pNE.y-h/2;
703 VRoundRect res;
704 if (e.hasAttribute(_style)){
705 SVGStyle ss=getStyle(e.getAttribute(_style));
706 if (ss.hasTransparencyInformation()){
707 if (ss.getFillColor()==null){res=new VRoundRectST(x,-y,0,w/2,h/2,Color.white,Math.round(RRARCR*Math.min(w,h)),Math.round(RRARCR*Math.min(w,h)));res.setFill(false);}
708 else {res=new VRoundRectST(x,-y,0,w/2,h/2,ss.getFillColor(),Math.round(RRARCR*Math.min(w,h)),Math.round(RRARCR*Math.min(w,h)));}
709 ((Transparent)res).setTransparencyValue(ss.getAlphaTransparencyValue());
710 }
711 else {
712 if (ss.getFillColor()==null){res=new VRoundRect(x,-y,0,w/2,h/2,Color.white,Math.round(RRARCR*Math.min(w,h)),Math.round(RRARCR*Math.min(w,h)));res.setFill(false);}
713 else {res=new VRoundRect(x,-y,0,w/2,h/2,ss.getFillColor(),Math.round(RRARCR*Math.min(w,h)),Math.round(RRARCR*Math.min(w,h)));}
714 }
715 Color border=ss.getBorderColor();
716 float[] hsv=Color.RGBtoHSB(border.getRed(),border.getGreen(),border.getBlue(),new float[3]);
717 res.setHSVbColor(hsv[0],hsv[1],hsv[2]);
718 }
719 else if (ctx!=null){
720 if (ctx.hasTransparencyInformation()){
721 if (ctx.getFillColor()==null){res=new VRoundRectST(x,-y,0,w/2,h/2,Color.white,Math.round(RRARCR*Math.min(w,h)),Math.round(RRARCR*Math.min(w,h)));res.setFill(false);}
722 else {res=new VRoundRectST(x,-y,0,w/2,h/2,ctx.getFillColor(),Math.round(RRARCR*Math.min(w,h)),Math.round(RRARCR*Math.min(w,h)));}
723 ((Transparent)res).setTransparencyValue(ctx.getAlphaTransparencyValue());
724 }
725 else {
726 if (ctx.getFillColor()==null){res=new VRoundRect(x,-y,0,w/2,h/2,Color.white,Math.round(RRARCR*Math.min(w,h)),Math.round(RRARCR*Math.min(w,h)));res.setFill(false);}
727 else {res=new VRoundRect(x,-y,0,w/2,h/2,ctx.getFillColor(),Math.round(RRARCR*Math.min(w,h)),Math.round(RRARCR*Math.min(w,h)));}
728 }
729 Color border=ctx.getBorderColor();
730 if (border!=null){
731 float[] hsv=Color.RGBtoHSB(border.getRed(),border.getGreen(),border.getBlue(),new float[3]);
732 res.setHSVbColor(hsv[0],hsv[1],hsv[2]);
733 }
734 }
735 else {res=new VRoundRect(x,-y,0,w/2,h/2,Color.white,Math.round(RRARCR*Math.min(w,h)),Math.round(RRARCR*Math.min(w,h)));}
736 return res;
737 }
738 else return null;
739 }
740
741 /**create a VRectangle from an SVG rect element
742 *@param e an SVG rect(angle) as a DOM element (org.w3c.dom.Element)
743 */
744 public static VRectangleOr createRectangle(Element e){
745 return createRectangle(e,null);
746 }
747
748 /**create a VRectangle from an SVG rect element
749 *@param e an SVG rect(angle) as a DOM element (org.w3c.dom.Element)
750 *@param ctx used to propagate contextual style information (put null if none)
751 */
752 public static VRectangleOr createRectangle(Element e,Context ctx){
753 long x=(Long.valueOf(e.getAttribute(_x))).longValue()+xoffset;
754 long y=(Long.valueOf(e.getAttribute(_y))).longValue()+yoffset;
755 long w=(Long.valueOf(e.getAttribute(_width))).longValue()/2;
756 long h=(Long.valueOf(e.getAttribute(_height))).longValue()/2;
757 VRectangleOr res;
758 if (e.hasAttribute(_style)){
759 SVGStyle ss=getStyle(e.getAttribute(_style));
760 if (ss.hasTransparencyInformation()){
761 if (ss.getFillColor()==null){res=new VRectangleOrST(x+w,-y-h,0,w,h,Color.white,0);res.setFill(false);}
762 else {res=new VRectangleOrST(x+w,-y-h,0,w,h,ss.getFillColor(),0);}
763 ((Transparent)res).setTransparencyValue(ss.getAlphaTransparencyValue());
764 }
765 else {
766 if (ss.getFillColor()==null){res=new VRectangleOr(x+w,-y-h,0,w,h,Color.white,0);res.setFill(false);}
767 else {res=new VRectangleOr(x+w,-y-h,0,w,h,ss.getFillColor(),0);}
768 }
769 Color border=ss.getBorderColor();
770 float[] hsv=Color.RGBtoHSB(border.getRed(),border.getGreen(),border.getBlue(),new float[3]);
771 res.setHSVbColor(hsv[0],hsv[1],hsv[2]);
772 }
773 else if (ctx!=null){
774 if (ctx.hasTransparencyInformation()){
775 if (ctx.getFillColor()==null){res=new VRectangleOrST(x+w,-y-h,0,w,h,Color.white,0);res.setFill(false);}
776 else {res=new VRectangleOrST(x+w,-y-h,0,w,h,ctx.getFillColor(),0);}
777 ((Transparent)res).setTransparencyValue(ctx.getAlphaTransparencyValue());
778 }
779 else {
780 if (ctx.getFillColor()==null){res=new VRectangleOr(x+w,-y-h,0,w,h,Color.white,0);res.setFill(false);}
781 else {res=new VRectangleOr(x+w,-y-h,0,w,h,ctx.getFillColor(),0);}
782 }
783 Color border=ctx.getBorderColor();
784 if (border!=null){
785 float[] hsv=Color.RGBtoHSB(border.getRed(),border.getGreen(),border.getBlue(),new float[3]);
786 res.setHSVbColor(hsv[0],hsv[1],hsv[2]);
787 }
788 }
789 else {res=new VRectangleOr(x+w,-y-h,0,w,h,Color.white,0);}
790 return res;
791 }
792
793 /**create a VPolygon from an SVG polygon element
794 *@param e an SVG polygon as a DOM element (org.w3c.dom.Element)
795 */
796 public static VPolygon createPolygon(Element e){
797 return createPolygon(e,null);
798 }
799
800 /**create a VPolygon from an SVG polygon element
801 *@param e an SVG polygon as a DOM element (org.w3c.dom.Element)
802 *@param ctx used to propagate contextual style information (put null if none)
803 */
804 public static VPolygon createPolygon(Element e,Context ctx){
805 Vector coords=new Vector();
806 translateSVGPolygon(e.getAttribute(_points),coords);
807 LongPoint[] coords2=new LongPoint[coords.size()];
808 LongPoint lp;
809 for (int i=0;i<coords2.length;i++){
810 lp=(LongPoint)coords.elementAt(i);
811 coords2[i]=new LongPoint(lp.x,-lp.y);
812 }
813 VPolygon res;
814 if (e.hasAttribute(_style)){
815 SVGStyle ss=getStyle(e.getAttribute(_style));
816 if (ss.hasTransparencyInformation()){
817 if (ss.getFillColor()==null){res=new VPolygonST(coords2,Color.white);res.setFill(false);}
818 else {res=new VPolygonST(coords2,ss.getFillColor());}
819 ((Transparent)res).setTransparencyValue(ss.getAlphaTransparencyValue());
820 }
821 else {
822 if (ss.getFillColor()==null){res=new VPolygon(coords2,Color.white);res.setFill(false);}
823 else {res=new VPolygon(coords2,ss.getFillColor());}
824 }
825 Color border=ss.getBorderColor();
826 float[] hsv=Color.RGBtoHSB(border.getRed(),border.getGreen(),border.getBlue(),new float[3]);
827 res.setHSVbColor(hsv[0],hsv[1],hsv[2]);
828 }
829 else if (ctx!=null){
830 if (ctx.hasTransparencyInformation()){
831 if (ctx.getFillColor()==null){res=new VPolygonST(coords2,Color.white);res.setFill(false);}
832 else {res=new VPolygonST(coords2,ctx.getFillColor());}
833 ((Transparent)res).setTransparencyValue(ctx.getAlphaTransparencyValue());
834 }
835 else {
836 if (ctx.getFillColor()==null){res=new VPolygon(coords2,Color.white);res.setFill(false);}
837 else {res=new VPolygon(coords2,ctx.getFillColor());}
838 }
839 Color border=ctx.getBorderColor();
840 if (border!=null){
841 float[] hsv=Color.RGBtoHSB(border.getRed(),border.getGreen(),border.getBlue(),new float[3]);
842 res.setHSVbColor(hsv[0],hsv[1],hsv[2]);
843 }
844 }
845 else {res=new VPolygon(coords2,Color.white);}
846 return res;
847 }
848
849 /**create a set of VSegments from an SVG polyline element
850 *@param e an SVG polyline as a DOM element (org.w3c.dom.Element)
851 */
852 public static VSegment[] createPolyline(Element e){
853 return createPolyline(e,null);
854 }
855
856 /**create a set of VSegments from an SVG polyline element
857 *@param e an SVG polyline as a DOM element (org.w3c.dom.Element)
858 *@param ctx used to propagate contextual style information (put null if none)
859 */
860 public static VSegment[] createPolyline(Element e,Context ctx){
861 Vector coords=new Vector();
862 translateSVGPolygon(e.getAttribute(_points),coords);
863 VSegment[] res=new VSegment[coords.size()-1];
864 Color border=Color.black;
865 if (e.hasAttribute(_style)){
866 SVGStyle ss=getStyle(e.getAttribute(_style));
867 if ((border=ss.getBorderColor())==null){border=Color.black;}
868 }
869 else if (ctx!=null && ctx.getBorderColor()!=null){
870 border=ctx.getBorderColor();
871 }
872 LongPoint lp1,lp2;
873 for (int i=0;i<coords.size()-1;i++){
874 lp1=(LongPoint)coords.elementAt(i);
875 lp2=(LongPoint)coords.elementAt(i+1);
876 res[i]=new VSegment((lp1.x+lp2.x)/2,-(lp1.y+lp2.y)/2,0,(lp2.x-lp1.x)/2,(lp2.y-lp1.y)/2,border);
877 }
878 return res;
879 }
880
881 /**create a VPath from an SVG text element
882 *@param e an SVG path as a DOM element (org.w3c.dom.Element)
883 *@param ph a VPath that is going to be modified to match the coordinates provided as first argument (you can simply use <i>new VPath()</i>)
884 */
885 public static VPath createPath(Element e,VPath ph){
886 return createPath(e,ph,null);
887 }
888
889 /**create a VPath from an SVG text element
890 *@param e an SVG path as a DOM element (org.w3c.dom.Element)
891 *@param ph a VPath that is going to be modified to match the coordinates provided as first argument (you can simply use <i>new VPath()</i>)
892 *@param ctx used to propagate contextual style information (put null if none)
893 */
894 public static VPath createPath(Element e,VPath ph,Context ctx){
895 StringBuffer svg=new StringBuffer(e.getAttribute(_d));
896 if (checkSVGPath(svg.toString())){
897 StringBuffer lastCommand=new StringBuffer("M");
898 while (svg.length()>0){
899 Utilities.delLeadingSpaces(svg);
900 processNextSVGPathCommand(svg,ph,lastCommand);
901 }
902 if (e.hasAttribute(_style)){
903 SVGStyle ss=getStyle(e.getAttribute(_style));
904 Color border=ss.getBorderColor();
905 if (border!=null){
906 float[] hsv=Color.RGBtoHSB(border.getRed(),border.getGreen(),border.getBlue(),new float[3]);
907 ph.setHSVColor(hsv[0],hsv[1],hsv[2]);
908 }
909 }
910 else if (ctx!=null && ctx.getBorderColor()!=null){
911 Color border=ctx.getBorderColor();
912 if (border!=null){
913 float[] hsv=Color.RGBtoHSB(border.getRed(),border.getGreen(),border.getBlue(),new float[3]);
914 ph.setHSVColor(hsv[0],hsv[1],hsv[2]);
915 }
916 }
917 return ph;
918 }
919 else return null;
920 }
921
922 /**create a VPath from an SVG text element
923 *@param d the <i>d</i> attribute value of an SVG path
924 *@param ph a VPath that is going to be modified to match the coordinates provided as first argument (you can just use <i>new VPath()</i>)
925 */
926 public static VPath createPath(String d,VPath ph){
927 return createPath(d,ph,null);
928 }
929
930 /**create a VPath from an SVG text element
931 *@param d the <i>d</i> attribute value of an SVG path
932 *@param ph a VPath that is going to be modified to match the coordinates provided as first argument (you can just use <i>new VPath()</i>)
933 *@param ctx used to propagate contextual style information (put null if none)
934 */
935 public static VPath createPath(String d,VPath ph,Context ctx){
936 StringBuffer svg=new StringBuffer(d);
937 if (checkSVGPath(svg.toString())){
938 StringBuffer lastCommand=new StringBuffer("M");
939 while (svg.length()>0){
940 Utilities.delLeadingSpaces(svg);
941 processNextSVGPathCommand(svg,ph,lastCommand);
942 }
943 return ph;
944 }
945 else return null;
946 }
947
948 /**
949 *Load a DOM-parsed SVG document d in VirtualSpace vs
950 *@param d SVG document as a DOM tree
951 *@param vsm VTM virtual space manager owning the virtual space
952 *@param vs name of the virtual space
953 */
954 public static void load(Document d,VirtualSpaceManager vsm,String vs){
955 Element svgRoot=d.getDocumentElement();
956 NodeList objects=svgRoot.getChildNodes();
957 for (int i=0;i<objects.getLength();i++){
958 Node obj=objects.item(i);
959 if (obj.getNodeType()==Node.ELEMENT_NODE){processNode((Element)obj,vsm,vs,null,false);}
960 }
961 }
962
963
964 /*e id a DOM element, vs is the name of the virtual space where the new glyph(s) are put*/
965 private static void processNode(Element e,VirtualSpaceManager vsm,String vs,Context ctx,boolean mainFontSet){
966 String tagName=e.getTagName();
967 if (tagName.equals(_rect)){
968 vsm.addGlyph(createRectangle(e,ctx),vs);
969 }
970 else if (tagName.equals(_ellipse)){
971 vsm.addGlyph(createEllipse(e,ctx),vs);
972 }
973 else if (tagName.equals(_circle)){
974 vsm.addGlyph(createCircle(e,ctx),vs);
975 }
976 else if (tagName.equals(_path)){
977 vsm.addGlyph(createPath(e,new VPath(),ctx),vs);
978 }
979 else if (tagName.equals(_text)){
980 vsm.addGlyph(createText(e,ctx,vsm),vs);
981 }
982 else if (tagName.equals(_polygon)){
983 Glyph g=createRectangleFromPolygon(e,ctx);
984 if (g!=null){vsm.addGlyph(g,vs);} //if e does not describe a rectangle
985 else {vsm.addGlyph(createPolygon(e,ctx),vs);} //create a VPolygon
986 }
987 else if (tagName.equals(_polyline)){
988 Glyph[] segments=createPolyline(e,ctx);
989 for (int i=0;i<segments.length;i++){
990 vsm.addGlyph(segments[i],vs);
991 }
992 }
993 else if (tagName.equals(_g)){
994 NodeList objects=e.getChildNodes();
995 boolean setAFont=false;
996 if (e.hasAttribute(SVGReader._style)){
997 if (ctx!=null){ctx.add(e.getAttribute(SVGReader._style));}
998 else {ctx=new Context(e.getAttribute(SVGReader._style));}
999 if (!mainFontSet){
1000 Font f;
1001 if ((f=ctx.getDefinedFont())!=null){
1002 vsm.setMainFont(f);
1003 setAFont=true;
1004 }
1005 }
1006 else {setAFont=true;}
1007 }
1008 for (int i=0;i<objects.getLength();i++){
1009 Node obj=objects.item(i);
1010 if (obj.getNodeType()==Node.ELEMENT_NODE){processNode((Element)obj,vsm,vs,ctx,setAFont);}
1011 }
1012 }
1013 else if (tagName.equals(_a)){
1014 NodeList objects=e.getChildNodes();
1015 boolean setAFont=false;
1016 if (e.hasAttribute(SVGReader._style)){
1017 if (ctx!=null){ctx.add(e.getAttribute(SVGReader._style));}
1018 else {ctx=new Context(e.getAttribute(SVGReader._style));}
1019 if (!mainFontSet){
1020 Font f;
1021 if ((f=ctx.getDefinedFont())!=null){
1022 vsm.setMainFont(f);
1023 setAFont=true;
1024 }
1025 }
1026 else {setAFont=true;}
1027 }
1028 for (int i=0;i<objects.getLength();i++){
1029 Node obj=objects.item(i);
1030 if (obj.getNodeType()==Node.ELEMENT_NODE){processNode((Element)obj,vsm,vs,ctx,setAFont);}
1031 }
1032 }
1033 else if (tagName.equals(_title)){
1034 //do nothing for now - we might want to send it back for some unknown reason
1035 //but title elements are not supposed to be part of the representation
1036 }
1037 else System.err.println("SVGReader: unsupported element: "+tagName);
1038 }
1039
1040
1041 private static boolean specialFont(Font cFont,Font mFont){//context font, main font
1042 if (!cFont.getFamily().equals(mFont.getFamily()) || cFont.getSize()!=mFont.getSize() || cFont.getStyle()!=mFont.getStyle()){return true;}
1043 else {return false;}
1044 }
1045
1046}