001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/graphics/sld/Mark.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005       Department of Geography, University of Bonn
006     and
007       lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035    ----------------------------------------------------------------------------*/
036    package org.deegree.graphics.sld;
037    
038    import static org.deegree.framework.xml.XMLTools.escape;
039    
040    import java.awt.BasicStroke;
041    import java.awt.Color;
042    import java.awt.FontMetrics;
043    import java.awt.Graphics2D;
044    import java.awt.Polygon;
045    import java.awt.image.BufferedImage;
046    
047    import org.deegree.framework.util.StringTools;
048    import org.deegree.framework.xml.Marshallable;
049    import org.deegree.model.feature.Feature;
050    import org.deegree.model.filterencoding.FilterEvaluationException;
051    
052    /**
053     * A Mark takes a "shape" and applies coloring to it. The shape can be derived either from a well-known name (such as
054     * "square"), an external URL in various formats (such as, perhaps GIF), or from a glyph of a font. Multiple external
055     * formats may be used with the semantic that they all contain the equivalent shape in different formats. If an image
056     * format is used that has inherent coloring, the coloring is discarded and only the opacity channel (or equivalent) is
057     * used. A Halo, Fill, and/or Stroke is applied as appropriate for the shape's source format.
058     * <p>
059     *
060     * @author <a href="mailto:k.lupp@web.de">Katharina Lupp </a>
061     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
062     * @version $Revision: 18195 $ $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
063     */
064    
065    public class Mark implements Marshallable {
066    
067        private BufferedImage image = null;
068    
069        private Fill fill = null;
070    
071        private String wellKnownName = null;
072    
073        private Stroke stroke = null;
074    
075        /**
076         * Constructor for the default <tt>Mark</tt>.
077         */
078        Mark() {
079            // nothing to do
080        }
081    
082        /**
083         * constructor initializing the class with the <Mark>
084         *
085         * @param wellKnownName
086         * @param stroke
087         * @param fill
088         */
089        Mark( String wellKnownName, Stroke stroke, Fill fill ) {
090            setWellKnownName( wellKnownName );
091            setStroke( stroke );
092            setFill( fill );
093        }
094    
095        /**
096         * Gives the well known name of a Mark's shape. Allowed values include at least "square", "circle", "triangle",
097         * "star", "cross", and "x", though map servers may draw a different symbol instead if they don't have a shape for
098         * all of these. Renderings of these marks may be made solid or hollow depending on Fill and Stroke parameters. The
099         * default value is "square".
100         *
101         * @return the WK-Name of the mark
102         *
103         */
104        public String getWellKnownName() {
105            return wellKnownName;
106        }
107    
108        /**
109         * Sets the well known name of a Mark's shape. Allowed values include at least "square", "circle", "triangle",
110         * "star", "cross", and "x", though map servers may draw a different symbol instead if they don't have a shape for
111         * all of these. Renderings of these marks may be made solid or hollow depending on Fill and Stroke parameters. The
112         * default value is "square".
113         *
114         * @param wellKnownName
115         *            the WK-Name of the mark
116         *
117         */
118        public void setWellKnownName( String wellKnownName ) {
119            this.wellKnownName = wellKnownName;
120        }
121    
122        /**
123         * A Fill allows area geometries to be filled. There are two types of fills: solid-color and repeated GraphicFill.
124         * In general, if a Fill element is omitted in its containing element, no fill will be rendered. The default is a
125         * solid 50%-gray (color "#808080") opaque fill.
126         *
127         * @return the fill of the mark
128         */
129        public Fill getFill() {
130            return fill;
131        }
132    
133        /**
134         * sets the <Fill>
135         *
136         * @param fill
137         *            the fill of the mark
138         */
139        public void setFill( Fill fill ) {
140            this.fill = fill;
141        }
142    
143        /**
144         * A Stroke allows a string of line segments (or any linear geometry) to be rendered. There are three basic types of
145         * strokes: solid Color, GraphicFill (stipple), and repeated GraphicStroke. A repeated graphic is plotted linearly
146         * and has its graphic symbol bended around the curves of the line string. The default is a solid black line (Color
147         * "#000000").
148         *
149         * @return the stroke of the mark
150         */
151        public Stroke getStroke() {
152            return stroke;
153        }
154    
155        /**
156         * sets <Stroke>
157         *
158         * @param stroke
159         *            the stroke of the mark
160         */
161        public void setStroke( Stroke stroke ) {
162            this.stroke = stroke;
163        }
164    
165        /**
166         * Draws the given feature on the buffered image. The drawing values are used from the 'Fill' object (if set). or
167         * white as a default fill color and black as a stroke color. If the stroke is null the BasicStroke values
168         * (CAP_ROUND, JOIN_ROUND) will be used.
169         * <p>
170         * Be careful to set the buffered image first!.
171         *
172         * @param feature
173         *
174         * @param size
175         *            DOCUMENT ME!
176         *
177         * @return The feature as a buffered image with a the 'Fill' values. or white as a default fill color and black as a
178         *         stroke color.
179         * @throws FilterEvaluationException
180         */
181        public BufferedImage getAsImage( Feature feature, int size )
182                                throws FilterEvaluationException {
183            double fillOpacity = 1.0;
184            double strokeOpacity = 1.0;
185            float[] dash = null;
186            float dashOffset = 0;
187            int cap = BasicStroke.CAP_ROUND;
188            int join = BasicStroke.JOIN_ROUND;
189            float width = 1;
190            Color fillColor = new Color( 128, 128, 128 );
191            Color strokeColor = new Color( 0, 0, 0 );
192    
193            if ( fill != null ) {
194                fillOpacity = fill.getOpacity( feature );
195                fillColor = fill.getFill( feature );
196            }
197    
198            if ( stroke != null ) {
199                strokeOpacity = stroke.getOpacity( feature );
200                strokeColor = stroke.getStroke( feature );
201                dash = stroke.getDashArray( feature );
202                cap = stroke.getLineCap( feature );
203                join = stroke.getLineJoin( feature );
204                width = (float) stroke.getWidth( feature );
205                dashOffset = stroke.getDashOffset( feature );
206            }
207    
208            if ( wellKnownName == null ) {
209                wellKnownName = "square";
210            }
211    
212            if ( wellKnownName.equalsIgnoreCase( "circle" ) ) {
213                image = drawCircle( size, fillOpacity, fillColor, strokeOpacity, strokeColor, dash, dashOffset, width, cap,
214                                    join );
215            } else if ( wellKnownName.equalsIgnoreCase( "triangle" ) ) {
216                image = drawTriangle( size, fillOpacity, fillColor, strokeOpacity, strokeColor, dash, dashOffset, width,
217                                      cap, join );
218            } else if ( wellKnownName.equalsIgnoreCase( "cross" ) ) {
219                image = drawCross1( size, strokeOpacity, strokeColor, dash, dashOffset, width, cap, join );
220            } else if ( wellKnownName.equalsIgnoreCase( "x" ) ) {
221                image = drawCross2( size, strokeOpacity, strokeColor, dash, dashOffset, width, cap, join );
222            } else if ( wellKnownName.startsWith( "CHAR" ) ) {
223                image = drawCharacter( size, fillOpacity, fillColor, strokeOpacity, strokeColor, wellKnownName );
224            } else if ( wellKnownName.equalsIgnoreCase( "star" ) ) {
225                image = drawStar( size, fillOpacity, fillColor, strokeOpacity, strokeColor, dash, dashOffset, width, cap,
226                                  join );
227            } else {
228                image = drawSquare( size, fillOpacity, fillColor, strokeOpacity, strokeColor, dash, dashOffset, width, cap,
229                                    join );
230            }
231    
232            return image;
233        }
234    
235        /**
236         * Sets the mark as an image. RThis method is not part of the sld specifications but it is added to speed up
237         * applications.
238         *
239         * @param bufferedImage
240         *            the bufferedImage to be set for the mark
241         */
242        public void setAsImage( BufferedImage bufferedImage ) {
243            this.image = bufferedImage;
244        }
245    
246        /**
247         *
248         * @param dash
249         * @param dashOffset
250         * @param width
251         * @param cap
252         * @param join
253         * @return the basic stroke
254         */
255        private BasicStroke createBasicStroke( float[] dash, float dashOffset, float width, int cap, int join ) {
256            BasicStroke bs2 = null;
257            if ( ( dash == null ) || ( dash.length < 2 ) ) {
258                bs2 = new BasicStroke( width, cap, join );
259            } else {
260                bs2 = new BasicStroke( width, cap, join, 10.0f, dash, dashOffset );
261            }
262            return bs2;
263        }
264    
265        /**
266         * Draws a scaled instance of a triangle mark according to the given parameters.
267         *
268         * @param size
269         *            resulting image's height and width
270         * @param fillOpacity
271         *            opacity value for the filled parts of the image
272         * @param fillColor
273         *            <tt>Color</tt> to be used for the fill
274         * @param strokeOpacity
275         *            opacity value for the stroked parts of the image
276         * @param strokeColor
277         *            <tt>Color</tt> to be used for the strokes
278         * @param dash
279         *            dash array for rendering boundary line
280         * @param width
281         *            of the boundary line
282         * @param cap
283         *            of the boundary line
284         * @param join
285         *            of the boundary line
286         * @param dashOffset
287         *
288         * @return image displaying a triangle
289         */
290        public BufferedImage drawTriangle( int size, double fillOpacity, Color fillColor, double strokeOpacity,
291                                           Color strokeColor, float[] dash, float dashOffset, float width, int cap, int join ) {
292    
293            int offset = (int) ( width * 2 + 1 ) / 2;
294            BufferedImage image = new BufferedImage( size + offset * 2, size + offset * 2, BufferedImage.TYPE_INT_ARGB );
295    
296            int[] x_ = new int[3];
297            int[] y_ = new int[3];
298            x_[0] = offset;
299            y_[0] = offset;
300            x_[1] = size / 2 + offset;
301            y_[1] = size - 1 + offset;
302            x_[2] = size - 1 + offset;
303            y_[2] = offset;
304    
305            Graphics2D g2D = (Graphics2D) image.getGraphics();
306            BasicStroke bs = createBasicStroke( dash, dashOffset, width, cap, join );
307            g2D.setStroke( bs );
308            setColor( g2D, fillColor, fillOpacity );
309            g2D.fillPolygon( x_, y_, 3 );
310            setColor( g2D, strokeColor, strokeOpacity );
311            g2D.drawPolygon( x_, y_, 3 );
312    
313            return image;
314        }
315    
316        /**
317         * Draws a five-pointed star (pentagram) according to the given parameters.
318         *
319         * @param size
320         *            resulting image's height and width
321         * @param fillOpacity
322         *            opacity value for the filled parts of the image
323         * @param fillColor
324         *            <tt>Color</tt> to be used for the fill
325         * @param strokeOpacity
326         *            opacity value for the stroked parts of the image
327         * @param strokeColor
328         *            <tt>Color</tt> to be used for the strokes
329         * @param dash
330         *            dash array for rendering boundary line
331         * @param width
332         *            of the boundary line
333         * @param cap
334         *            of the boundary line
335         * @param join
336         *            of the boundary line
337         * @param dashOffset
338         *
339         * @return an image of a pentagram
340         */
341        public BufferedImage drawStar( int size, double fillOpacity, Color fillColor, double strokeOpacity,
342                                       Color strokeColor, float[] dash, float dashOffset, float width, int cap, int join ) {
343            int offset = (int) ( width * 2 + 1 ) / 2;
344            BufferedImage image = new BufferedImage( size + offset * 2, size + offset * 2, BufferedImage.TYPE_INT_ARGB );
345    
346            Graphics2D g2D = image.createGraphics();
347            BasicStroke bs = createBasicStroke( dash, dashOffset, width, cap, join );
348            g2D.setStroke( bs );
349            int s = size / 2;
350            int x0 = s;
351            int y0 = s;
352            double sin36 = Math.sin( Math.toRadians( 36 ) );
353            double cos36 = Math.cos( Math.toRadians( 36 ) );
354            double sin18 = Math.sin( Math.toRadians( 18 ) );
355            double cos18 = Math.cos( Math.toRadians( 18 ) );
356            int smallRadius = (int) ( s * sin18 / Math.sin( Math.toRadians( 54 ) ) );
357    
358            int p0X = x0;
359            int p0Y = y0 - s;
360            int p1X = x0 + (int) ( smallRadius * sin36 );
361            int p1Y = y0 - (int) ( smallRadius * cos36 );
362            int p2X = x0 + (int) ( s * cos18 );
363            int p2Y = y0 - (int) ( s * sin18 );
364            int p3X = x0 + (int) ( smallRadius * cos18 );
365            int p3Y = y0 + (int) ( smallRadius * sin18 );
366            int p4X = x0 + (int) ( s * sin36 );
367            int p4Y = y0 + (int) ( s * cos36 );
368            int p5Y = y0 + smallRadius;
369            int p6X = x0 - (int) ( s * sin36 );
370            int p7X = x0 - (int) ( smallRadius * cos18 );
371            int p8X = x0 - (int) ( s * cos18 );
372            int p9X = x0 - (int) ( smallRadius * sin36 );
373    
374            int[] x = new int[] { p0X, p1X, p2X, p3X, p4X, p0X, p6X, p7X, p8X, p9X };
375            int[] y = new int[] { p0Y, p1Y, p2Y, p3Y, p4Y, p5Y, p4Y, p3Y, p2Y, p1Y };
376            for ( int i = 0; i < y.length; i++ ) {
377                x[i] = x[i] + offset;
378                y[i] = y[i] + offset;
379            }
380            Polygon shape = new Polygon( x, y, 10 );
381    
382            setColor( g2D, fillColor, fillOpacity );
383            g2D.fill( shape );
384            setColor( g2D, strokeColor, strokeOpacity );
385            g2D.draw( shape );
386    
387            g2D.dispose();
388    
389            return image;
390        }
391    
392        /**
393         * Draws a scaled instance of a circle mark according to the given parameters.
394         *
395         * @param size
396         *            resulting image's height and widthh
397         * @param fillOpacity
398         *            opacity value for the filled parts of the image
399         * @param fillColor
400         *            <tt>Color</tt> to be used for the fill
401         * @param strokeOpacity
402         *            opacity value for the stroked parts of the image
403         * @param strokeColor
404         *            <tt>Color</tt> to be used for the strokes
405         * @param dash
406         *            dash array for rendering boundary line
407         * @param width
408         *            of the boundary line
409         * @param cap
410         *            of the boundary line
411         * @param join
412         *            of the boundary line
413         * @param dashOffset
414         *
415         * @return image displaying a circle
416         */
417        public BufferedImage drawCircle( int size, double fillOpacity, Color fillColor, double strokeOpacity,
418                                         Color strokeColor, float[] dash, float dashOffset, float width, int cap, int join ) {
419            int offset = (int) ( width * 2 + 1 ) / 2;
420            BufferedImage image = new BufferedImage( size + offset * 2, size + offset * 2, BufferedImage.TYPE_INT_ARGB );
421    
422            Graphics2D g2D = (Graphics2D) image.getGraphics();
423            BasicStroke bs = createBasicStroke( dash, dashOffset, width, cap, join );
424            g2D.setStroke( bs );
425            setColor( g2D, fillColor, fillOpacity );
426            g2D.fillOval( offset, offset, size, size );
427    
428            setColor( g2D, strokeColor, strokeOpacity );
429            g2D.drawOval( offset, offset, size, size );
430    
431            return image;
432        }
433    
434        /**
435         * Draws a scaled instance of a square mark according to the given parameters.
436         *
437         * @param size
438         *            resulting image's height and widthh
439         * @param fillOpacity
440         *            opacity value for the filled parts of the image
441         * @param fillColor
442         *            <tt>Color</tt> to be used for the fill
443         * @param strokeOpacity
444         *            opacity value for the stroked parts of the image
445         * @param strokeColor
446         *            <tt>Color</tt> to be used for the strokes
447         * @param dash
448         *            dash array for rendering boundary line
449         * @param width
450         *            of the boundary line
451         * @param cap
452         *            of the boundary line
453         * @param join
454         *            of the boundary line
455         * @param dashOffset
456         *
457         * @return image displaying a square
458         */
459        public BufferedImage drawSquare( int size, double fillOpacity, Color fillColor, double strokeOpacity,
460                                         Color strokeColor, float[] dash, float dashOffset, float width, int cap, int join ) {
461            int offset = (int) ( width * 2 + 1 ) / 2;
462            BufferedImage image = new BufferedImage( size + offset * 2, size + offset * 2, BufferedImage.TYPE_INT_ARGB );
463    
464            Graphics2D g2D = (Graphics2D) image.getGraphics();
465            BasicStroke bs = createBasicStroke( dash, dashOffset, width, cap, join );
466            g2D.setStroke( bs );
467            setColor( g2D, fillColor, fillOpacity );
468            g2D.fillRect( offset, offset, size, size );
469    
470            setColor( g2D, strokeColor, strokeOpacity );
471            g2D.drawRect( offset, offset, size - 1, size - 1 );
472    
473            return image;
474        }
475    
476        /**
477         * Draws a scaled instance of a cross mark (a "+") according to the given parameters.
478         *
479         * @param size
480         *            resulting image's height and widthh
481         * @param strokeOpacity
482         *            opacity value for the stroked parts of the image
483         * @param strokeColor
484         *            <tt>Color</tt> to be used for the strokes
485         * @param dash
486         *            dash array for rendering boundary line
487         * @param width
488         *            of the boundary line
489         * @param cap
490         *            of the boundary line
491         * @param join
492         *            of the boundary line
493         * @param dashOffset
494         *
495         * @return image displaying a cross (a "+")
496         */
497        public BufferedImage drawCross1( int size, double strokeOpacity, Color strokeColor, float[] dash, float dashOffset,
498                                         float width, int cap, int join ) {
499    
500            int offset = (int) ( width * 2 + 1 ) / 2;
501            BufferedImage image = new BufferedImage( size + offset * 2, size + offset * 2, BufferedImage.TYPE_INT_ARGB );
502    
503            Graphics2D g2D = (Graphics2D) image.getGraphics();
504    
505            BasicStroke bs = createBasicStroke( dash, dashOffset, width, cap, join );
506            g2D.setStroke( bs );
507    
508            setColor( g2D, strokeColor, strokeOpacity );
509            g2D.drawLine( offset, size / 2 + offset, size - 1 + offset, size / 2 + offset );
510            g2D.drawLine( size / 2 + offset, offset, size / 2 + offset, size - 1 + offset );
511    
512            return image;
513        }
514    
515        /**
516         * Draws a scaled instance of a cross mark (an "X") according to the given parameters.
517         *
518         * @param size
519         *            resulting image's height and widthh
520         * @param strokeOpacity
521         *            opacity value for the stroked parts of the image
522         * @param strokeColor
523         *            <tt>Color</tt> to be used for the strokes
524         * @param dash
525         *            dash array for rendering boundary line
526         * @param width
527         *            of the boundary line
528         * @param cap
529         *            of the boundary line
530         * @param join
531         *            of the boundary line
532         * @param dashOffset
533         *
534         * @return image displaying a cross (a "X")
535         */
536        public BufferedImage drawCross2( int size, double strokeOpacity, Color strokeColor, float[] dash, float dashOffset,
537                                         float width, int cap, int join ) {
538    
539            int offset = (int) ( width * 2 + 1 ) / 2;
540            BufferedImage image = new BufferedImage( size + offset * 2, size + offset * 2, BufferedImage.TYPE_INT_ARGB );
541    
542            Graphics2D g2D = (Graphics2D) image.getGraphics();
543    
544            BasicStroke bs = createBasicStroke( dash, dashOffset, width, cap, join );
545            g2D.setStroke( bs );
546    
547            setColor( g2D, strokeColor, strokeOpacity );
548            g2D.drawLine( offset, offset, size - 1 + offset, size - 1 + offset );
549            g2D.drawLine( offset, size - 1 + offset, size - 1 + offset, offset );
550    
551            return image;
552        }
553    
554        /**
555         *
556         * @param size
557         * @param fillOpacity
558         * @param fillColor
559         * @param strokeOpacity
560         * @param strokeColor
561         * @param charDesc
562         *            e.g. CHAR:Times New Roman:45
563         */
564        private BufferedImage drawCharacter( int size, double fillOpacity, Color fillColor, double strokeOpacity,
565                                             Color strokeColor, String charDesc ) {
566    
567            String[] tmp = StringTools.toArray( charDesc, ":", false );
568    
569            BufferedImage image = new BufferedImage( size, size, BufferedImage.TYPE_INT_ARGB );
570    
571            Graphics2D g2 = (Graphics2D) image.getGraphics();
572            setColor( g2, fillColor, fillOpacity );
573            g2.fillRect( 0, 0, size, size );
574    
575            java.awt.Font font = new java.awt.Font( tmp[1], java.awt.Font.PLAIN, size );
576            g2.setFont( font );
577            FontMetrics fm = g2.getFontMetrics();
578    
579            char c = (char) Integer.parseInt( tmp[2] );
580            int w = fm.charWidth( c );
581            int h = fm.getHeight();
582    
583            String s = "" + c;
584            setColor( g2, strokeColor, strokeOpacity );
585            g2.drawString( s, size / 2 - w / 2, size / 2 + h / 2 - fm.getDescent() );
586            g2.dispose();
587            return image;
588        }
589    
590        /**
591         * @param g2D
592         * @param color
593         * @param opacity
594         */
595        private void setColor( Graphics2D g2D, Color color, double opacity ) {
596            if ( opacity < 0.999 ) {
597                final int alpha = (int) Math.round( opacity * 255 );
598                final int red = color.getRed();
599                final int green = color.getGreen();
600                final int blue = color.getBlue();
601                color = new Color( red, green, blue, alpha );
602            }
603    
604            g2D.setColor( color );
605        }
606    
607        /**
608         * exports the content of the Mark as XML formated String
609         *
610         * @return xml representation of the Mark
611         */
612        public String exportAsXML() {
613    
614            StringBuffer sb = new StringBuffer( 1000 );
615            sb.append( "<Mark>" );
616            if ( wellKnownName != null && !wellKnownName.equals( "" ) ) {
617                sb.append( "<WellKnownName>" ).append( escape( wellKnownName ) );
618                sb.append( "</WellKnownName>" );
619            }
620            if ( fill != null ) {
621                sb.append( ( (Marshallable) fill ).exportAsXML() );
622            }
623            if ( stroke != null ) {
624                sb.append( ( (Marshallable) stroke ).exportAsXML() );
625            }
626    
627            sb.append( "</Mark>" );
628    
629            return sb.toString();
630        }
631    
632        // private void drawUnicode(Graphics2D g2, int x, int y, double rotation,
633        // double size, String m, Mark mark) {
634        // int sz = (int)size;
635        // double fo = mark.getFill().getOpacity();
636        // double so = mark.getStroke().getOpacity();
637        //
638        // java.awt.Font font = new java.awt.Font("sans serif", java.awt.Font.PLAIN,
639        // sz);
640        // g2.setFont( font );
641        // FontMetrics fm = g2.getFontMetrics();
642        //
643        // char c = (char)m.charAt(0);
644        // int w = fm.charWidth(c);
645        // int h = fm.getHeight();
646        //
647        // g2 = setColor( g2, mark.getFill().getFill(), fo );
648        // g2.fillRect( x-w/2, y-h/2, w, h);
649        // g2 = setColor( g2, mark.getStroke().getStroke(), so );
650        //
651        // String s = "" + c;
652        // g2.drawString( s, x-w/2, y+h/2-fm.getDescent());
653        // }
654        // else {
655        //
656        // Mark[] marks = sym.getGraphic().getMarks();
657        // double rotation = sym.getGraphic().getRotation();
658        // double size = sym.getGraphic().getSize();
659        // if (marks != null) {
660        //
661        // for (int k = 0; k > marks.length; k++) {
662        //
663        // float w = (float)marks[k].getStroke().getWidth();
664        // g2.setStroke( new BasicStroke( w ) );
665        //
666        // if (marks[k].getWellKnownName().equalsIgnoreCase("triangle") ) {
667        // drawTriangle( g2, x, y, rotation, size, marks[k] );
668        // }
669        // else
670        // if (marks[k].getWellKnownName().equalsIgnoreCase("circle") ) {
671        // drawCircle( g2, x, y, rotation, size, marks[k] );
672        // }
673        // else
674        // if (marks[k].getWellKnownName().equalsIgnoreCase("square") ) {
675        // drawSquare( g2, x, y, rotation, size, marks[k] );
676        // }
677        // else
678        // if (marks[k].getWellKnownName().equalsIgnoreCase("cross") ) {
679        // drawCross1( g2, x, y, rotation, size, marks[k] );
680        // }
681        // else
682        // if (marks[k].getWellKnownName().equalsIgnoreCase("x") ) {
683        // drawCross2( g2, x, y, rotation, size, marks[k] );
684        // }
685        // else
686        // if (marks[k].getWellKnownName().length() == 0 ) {
687        // drawSquare( g2, x, y, rotation, size, marks[k] );
688        // }
689        // else {
690        // drawUnicode( g2, x, y, rotation, size,
691        // marks[k].getWellKnownName(), marks[k] );
692        // }
693        // }
694        // }
695        // }
696    }