Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 &amp; 3 and points 1 &amp; 4 have to be horizontally aligned. Moreother, points 1 &amp; 2 and points 3 &amp; 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}