Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.symbology / org.gvsig.symbology.lib / org.gvsig.symbology.lib.impl / src / main / java / org / gvsig / symbology / fmap / mapcontext / rendering / symbol / text / impl / SimpleTextSymbol.java @ 40679

History | View | Annotate | Download (15.6 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.symbology.fmap.mapcontext.rendering.symbol.text.impl;
25

    
26
import java.awt.BasicStroke;
27
import java.awt.Color;
28
import java.awt.Font;
29
import java.awt.FontMetrics;
30
import java.awt.Graphics2D;
31
import java.awt.Rectangle;
32
import java.awt.RenderingHints;
33
import java.awt.Shape;
34
import java.awt.font.FontRenderContext;
35
import java.awt.font.GlyphVector;
36
import java.awt.geom.AffineTransform;
37
import java.awt.geom.Rectangle2D;
38

    
39
import org.slf4j.Logger;
40
import org.slf4j.LoggerFactory;
41

    
42
import org.gvsig.compat.print.PrintAttributes;
43
import org.gvsig.fmap.dal.feature.Feature;
44
import org.gvsig.fmap.geom.Geometry;
45
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
46
import org.gvsig.fmap.geom.GeometryLocator;
47
import org.gvsig.fmap.geom.GeometryManager;
48
import org.gvsig.fmap.geom.exception.CreateGeometryException;
49
import org.gvsig.fmap.geom.primitive.GeneralPathX;
50
import org.gvsig.fmap.geom.primitive.Point;
51
import org.gvsig.fmap.mapcontext.MapContextLocator;
52
import org.gvsig.fmap.mapcontext.ViewPort;
53
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
54
import org.gvsig.fmap.mapcontext.rendering.symbols.ITextSymbol;
55
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolDrawingException;
56
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolManager;
57
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolPreferences;
58
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.impl.AbstractSymbol;
59
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.impl.CartographicSupportToolkit;
60
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.text.ISimpleTextSymbol;
61
import org.gvsig.tools.ToolsLocator;
62
import org.gvsig.tools.dynobject.DynStruct;
63
import org.gvsig.tools.persistence.PersistenceManager;
64
import org.gvsig.tools.persistence.PersistentState;
65
import org.gvsig.tools.persistence.exception.PersistenceException;
66
import org.gvsig.tools.task.Cancellable;
67
import org.gvsig.tools.util.Callable;
68

    
69
/**
70
 * SimpleTextSymbol is a class used to create symbols composed using a text defined by
71
 * the user.This text can be edited (changing the color, the font of the characters, and
72
 * the rotation of the text).
73
 * @author 2005-2008 jaume dominguez faus - jaume.dominguez@iver.es
74
 * @author 2009-     <a href="cordinyana@gvsig.org">C?sar Ordi?ana</a> - gvSIG team
75
 */
76
public class SimpleTextSymbol extends AbstractSymbol implements ITextSymbol, ISimpleTextSymbol {
77
        final static private Logger LOG = LoggerFactory.getLogger(SimpleTextSymbol.class);
78

    
79
        public static final String SIMPLE_TEXT_SYMBOL_PERSISTENCE_DEFINITION_NAME = "SimpleTextSymbol";
80
        
81
        private static final String FIELD_TEXT = "text";
82
        private static final String FIELD_FONT = "font";
83
        private static final String FIELD_TEXT_COLOR = "textColor";
84
        private static final String FIELD_ROTATION = "rotation";
85
        private static final String FIELD_AUTO_RESIZE = "autoResize";
86
        
87
        private static final String FIELD_HAS_HALO = "hasHalo";
88
        private static final String FIELD_HALO_COLOR = "haloColor";
89
        private static final String FIELD_HALO_WIDTH = "haloWidth";
90

    
91
        private static final GeometryManager geomManager = GeometryLocator.getGeometryManager();
92
        private String text = "";
93
        private Font font;
94
        
95
        private Color haloColor = Color.WHITE;
96
        private float haloWidth = 3;
97
        private boolean drawWithHalo = false;
98
        private BasicStroke haloStroke = new BasicStroke(
99
                        3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
100

    
101
        private Color textColor;
102
        private double rotation;
103
        private FontRenderContext frc = new FontRenderContext(
104
                        new AffineTransform(), false, true);
105
        private boolean autoresize;
106

    
107
        public SimpleTextSymbol() {
108
                super();
109
                SymbolPreferences preferences =
110
                                MapContextLocator.getSymbolManager().getSymbolPreferences();
111
                font = preferences.getDefaultSymbolFont();
112
                textColor = preferences.getDefaultSymbolColor();
113
        }
114

    
115
        public void draw(Graphics2D g, AffineTransform affineTransform,
116
                        Geometry geom, Feature feature, Cancellable cancel) {
117
                if (!isShapeVisible()) {
118
                        return;
119
                }
120
                
121
                Point point = (Point)geom;
122
            point.transform(affineTransform);
123
          
124
                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
125
                g.translate(point.getX(), point.getY());
126

    
127
                g.rotate(rotation);
128
                
129
                if (isDrawWithHalo()) {
130
                        char[] charText = new char[text.length()];
131
                        getText().getChars(0, charText.length, charText, 0);
132
                        GlyphVector glyph = font.layoutGlyphVector(frc, charText, 0, charText.length, Font.LAYOUT_NO_LIMIT_CONTEXT);
133
                        g.setColor(getHaloColor());
134
                        g.setStroke(haloStroke);
135
                        g.draw(glyph.getOutline());
136
                }
137
                // Rectangle2D bounds = getBounds();
138
//                 getBounds devuelve el bounds de un texto dibujado de manera que
139
                // la linea base de la primera letra est? en el punto (0,0).
140
                // Por eso, si queremos alinear el texto de manera que la parte superior
141
                // izquierda de la primera letra est? en (0,0) debemos moverlo seg?n
142
                // el valor de la ordenada, en lugar de seg?n su altura.
143
                g.setColor(textColor);
144
                g.setFont(font);
145

    
146
                g.drawString(getText(), 0, 0);//(int) bounds.getHeight());
147
                g.rotate(-rotation);
148
                g.translate(-point.getX(), -point.getY());
149
        }
150

    
151
        public void drawInsideRectangle(Graphics2D g,
152
                        AffineTransform scaleInstance, Rectangle r, PrintAttributes properties) throws SymbolDrawingException {
153
                int s = getFont().getSize();
154
                if (autoresize) {
155
                        if (s==0) {
156
                                s =1;
157
                                setFontSize(s);
158
                        }
159
                        g.setFont(getFont());
160
                    FontMetrics fm = g.getFontMetrics();
161
                    Rectangle2D rect = fm.getStringBounds(text, g);
162
                    double width = rect.getWidth();
163
                    double height = rect.getHeight();
164
                    double rWidth = r.getWidth();
165
                    double rHeight = r.getHeight();
166
                    double ratioText = width/height;
167
                    double ratioRect = rWidth/rHeight;
168

    
169
                    if (ratioText>ratioRect) {
170
                            s = (int) (s*(rWidth/width));
171
                    } else {
172
                            s = (int) (s*(rHeight/height));
173
                    }
174
//                    r.setLocation(r.x, (int) Math.round(r.getY()+r.getHeight()));
175
                }
176
                setFontSize(s);
177
//                g.setColor(Color.red);
178
//                g.draw(r);
179
                try {
180
                        if (properties==null)
181
                                draw(g, null, geomManager.createPoint(r.getX(), r.getY(),
182
                                                SUBTYPES.GEOM2D), null, null);
183
                        else
184
                                print(g, new AffineTransform(), geomManager.createPoint(r.getX(), r.getY(), SUBTYPES.GEOM2D), properties);
185
                } catch (CreateGeometryException e) {
186
                        LOG.error("Error creatint a point", e);
187
                        e.printStackTrace();
188
                }
189

    
190

    
191
        }
192

    
193
        public int getOnePointRgb() {
194
                return textColor.getRGB();
195
        }
196

    
197
        public void getPixExtentPlus(org.gvsig.fmap.geom.Geometry geom, float[] distances,
198
                        ViewPort viewPort, int dpi) {
199
                throw new Error("Not yet implemented!");
200

    
201
        }
202

    
203
        public ISymbol getSymbolForSelection() {
204
                return this; // a text is not selectable
205
        }
206

    
207
        public int getSymbolType() {
208
                //This method returned the deleted type Geometry.TYPES.TEXT. I suppose that now
209
            //it has to retrun geometry
210
            return Geometry.TYPES.GEOMETRY;
211
        }
212

    
213
        public boolean isSuitableFor(Geometry geom) {
214
                return true;
215
        }
216

    
217
        public String getClassName() {
218
                return getClass().getName();
219
        }
220

    
221
        public void print(Graphics2D g, AffineTransform at, org.gvsig.fmap.geom.Geometry geom, PrintAttributes properties){
222
                float originalSize = getFont().getSize2D();
223
                float size=originalSize;
224
                // scale it to size
225
                int pq = properties.getPrintQuality();
226
                if (pq == PrintAttributes.PRINT_QUALITY_NORMAL){
227
                        size *= (double) 300/72;
228
                }else if (pq == PrintAttributes.PRINT_QUALITY_HIGH){
229
                        size *= (double) 600/72;
230
                }else if (pq == PrintAttributes.PRINT_QUALITY_DRAFT){
231
                        // d *= 72/72; // (which is the same than doing nothing)
232
                }
233
                setFont(getFont().deriveFont(size));
234
                draw(g,at,geom, null, null);
235
                setFont(getFont().deriveFont(originalSize));
236
        }
237

    
238
        public String getText() {
239
                return text;
240
        }
241

    
242
        public Font getFont() {
243
                return font;
244
        }
245

    
246
        public Color getTextColor() {
247
                return textColor;
248
        }
249

    
250
        public void setText(String text) {
251
                this.text = text;
252
        }
253

    
254
        public void setFont(Font font) {
255
                if (font == null) {
256
                        LOG.warn("font <-- null");
257

    
258
                        return;
259
                }
260
                this.font = font;
261
        }
262

    
263
        public void setTextColor(Color color) {
264
                this.textColor = color;
265
        }
266
        
267
        public Color getColor() {
268
                return getTextColor();
269
        }
270
        
271
        public void setColor(Color color) {
272
                setTextColor(color);
273
        }
274

    
275
        public void setFontSize(double size) {
276
                Font newFont = new Font(font.getName(), font.getStyle(), (int) size);
277
//                if (newFont == null) {
278
//                        logger.warn(
279
//                                        "Font(" + font.getName() + ", "
280
//                                        + font.getStyle() + ", " + (int) size + " --> null");
281
//                } else {
282
                        this.font = newFont;
283
//                }
284
        }
285

    
286
        public double getRotation() {
287
                return rotation;
288
        }
289

    
290
        /**
291
         * Defines the angle of rotation for the text that composes the symbol
292
         *
293
         * @param rotation
294
         */
295
        public void setRotation(double rotation) {
296
                this.rotation = rotation;
297
        }
298

    
299
        /**
300
         * Returns an Geometry which represents a rectangle containing the text in
301
         * <b>screen</b> units.
302
         */
303
        public Geometry getTextWrappingShape(org.gvsig.fmap.geom.primitive.Point p) {
304
                Font font = getFont();
305
                GlyphVector gv = font.createGlyphVector(frc, text);
306

    
307
                Shape shape = gv.getOutline((float) p.getX(), (float) p.getY());
308
                Geometry myFShape;
309
                try {
310
                        myFShape = geomManager.createSurface(new GeneralPathX(shape
311
                                        .getBounds2D().getPathIterator(null)), SUBTYPES.GEOM2D);
312
                        myFShape.transform(AffineTransform.getTranslateInstance(p.getX(), p.getY()));
313

    
314
                        if (rotation != 0) {
315
                                myFShape.transform(AffineTransform.getRotateInstance(rotation));
316
                        }
317
                        return myFShape;
318
                } catch (CreateGeometryException e) {
319
                        LOG.error("Error creating a surface", e);
320
                        e.printStackTrace();
321
                }
322
                return null;
323
        }
324

    
325
        public Rectangle getBounds() {
326
//                FontMetrics fm = g.getFontMetrics();
327
//                Rectangle2D rect = fm.getStringBounds("graphics", g);
328

    
329
                Rectangle bounds = null;
330
                try {
331
                        bounds = getTextWrappingShape(geomManager.createPoint(0,0, SUBTYPES.GEOM2D)).getShape().getBounds();
332
                } catch (CreateGeometryException e) {
333
                        LOG.error("Error creating a point", e);
334
                }
335
                return bounds;
336
        }
337

    
338
        public void setCartographicSize(double cartographicSize, Geometry geom) {
339
                setFontSize(cartographicSize);
340
        }
341

    
342
        public double toCartographicSize(ViewPort viewPort, double dpi, Geometry geom) {
343
                double oldSize = getFont().getSize();
344
                setCartographicSize(getCartographicSize(
345
                                                                viewPort,
346
                                                                dpi,
347
                                                                geom),
348
                                                        geom);
349
                return oldSize;
350
        }
351

    
352
        public double getCartographicSize(ViewPort viewPort, double dpi, Geometry geom) {
353
                return CartographicSupportToolkit.
354
                                        getCartographicLength(this,
355
                                                                                  getFont().getSize(),
356
                                                                                  viewPort,
357
                                                                                  dpi);
358
        }
359
        public boolean isAutoresizeEnabled() {
360
                return autoresize;
361
        }
362

    
363
        public void setAutoresizeEnabled(boolean autoresizeFlag) {
364
                this.autoresize = autoresizeFlag;
365
        }
366

    
367
        
368
        public Object clone() throws CloneNotSupportedException {
369
                SimpleTextSymbol copy = (SimpleTextSymbol) super.clone();
370

    
371
                return copy;
372
        }
373

    
374
        public void loadFromState(PersistentState state)
375
        
376
                        throws PersistenceException {
377
                // Set parent fill symbol properties
378
                super.loadFromState(state);
379

    
380
                // Set own properties
381
                setAutoresizeEnabled(state.getBoolean(FIELD_AUTO_RESIZE));
382
                setFont((Font) state.get(FIELD_FONT));
383
                setRotation(state.getDouble(FIELD_ROTATION));
384
                setText(state.getString(FIELD_TEXT));
385
                setTextColor((Color) state.get(FIELD_TEXT_COLOR));
386
                // halo
387
                this.setDrawWithHalo(state.getBoolean(FIELD_HAS_HALO));
388
                this.setHaloColor((Color) state.get(FIELD_HALO_COLOR));
389
                this.setHaloWidth(state.getFloat(FIELD_HALO_WIDTH));
390
        }
391

    
392
        public void saveToState(PersistentState state) throws PersistenceException {
393
                // Save parent fill symbol properties
394
                super.saveToState(state);
395

    
396
                // Save own properties
397
                state.set(FIELD_AUTO_RESIZE, isAutoresizeEnabled());
398
                state.set(FIELD_FONT, getFont());
399
                state.set(FIELD_ROTATION, getRotation());
400
                state.set(FIELD_TEXT, getText());
401
                state.set(FIELD_TEXT_COLOR, getTextColor());
402
                // halo
403
                state.set(FIELD_HAS_HALO, this.isDrawWithHalo());
404
                state.set(FIELD_HALO_COLOR, this.getHaloColor());
405
                state.set(FIELD_HALO_WIDTH, this.getHaloWidth());
406
        }
407

    
408
        public static class RegisterPersistence implements Callable {
409

    
410
                public Object call() throws Exception {
411
                        PersistenceManager manager = ToolsLocator.getPersistenceManager();
412
                        if( manager.getDefinition(SIMPLE_TEXT_SYMBOL_PERSISTENCE_DEFINITION_NAME)==null ) {
413
                                DynStruct definition = manager.addDefinition(
414
                                                SimpleTextSymbol.class,
415
                                                SIMPLE_TEXT_SYMBOL_PERSISTENCE_DEFINITION_NAME,
416
                                                SIMPLE_TEXT_SYMBOL_PERSISTENCE_DEFINITION_NAME+" Persistence definition",
417
                                                null, 
418
                                                null
419
                                );
420
                                // Extend the Symbol base definition
421
                                definition.extend(manager.getDefinition(SYMBOL_PERSISTENCE_DEFINITION_NAME));
422

    
423
                                // Auto resize?
424
                                definition.addDynFieldBoolean(FIELD_AUTO_RESIZE)
425
                                        .setMandatory(true);
426
                                
427
                                // Font
428
                                definition.addDynFieldObject(FIELD_FONT)
429
                                        .setClassOfValue(Font.class)
430
                                        .setMandatory(true);
431
                                
432
                                // Rotation
433
                                definition.addDynFieldDouble(FIELD_ROTATION)
434
                                        .setMandatory(true);
435
                                
436
                                // Text
437
                                definition.addDynFieldString(FIELD_TEXT);
438
                                
439
                                // Text color
440
                                definition.addDynFieldObject(FIELD_TEXT_COLOR)
441
                                        .setClassOfValue(Color.class)
442
                                        .setMandatory(true);
443
                                
444
                                // halo
445
                                definition.addDynFieldBoolean(FIELD_HAS_HALO).setMandatory(true);
446
                                definition.addDynFieldObject(FIELD_HALO_COLOR).setClassOfValue(
447
                                                Color.class).setMandatory(true);
448
                                definition.addDynFieldFloat(FIELD_HALO_WIDTH).setMandatory(true);
449
                        }
450
                        return Boolean.TRUE;
451
                }
452
                
453
        }
454

    
455
        public static class RegisterSymbol implements Callable {
456

    
457
                public Object call() throws Exception {
458
                        int[] shapeTypes;
459
                        SymbolManager manager = MapContextLocator.getSymbolManager();
460
                
461
                        //This method registered the deleted type Geometry.TYPES.TEXT. I suppose that now
462
                //it has to register geometry
463
                shapeTypes = new int[] { Geometry.TYPES.GEOMETRY };
464
                manager.registerSymbol(ITextSymbol.SYMBOL_NAME,
465
                    shapeTypes,
466
                    SimpleTextSymbol.class);
467

    
468
                        return Boolean.TRUE;
469
                }
470
                
471
        }
472

    
473
        
474
        public Color getHaloColor() {
475
                return haloColor;
476
        }
477

    
478
        public void setHaloColor(Color co) {
479
                if (co != null) {
480
                        this.haloColor = co;
481
                }
482
        }
483

    
484
        public float getHaloWidth() {
485
                return haloWidth;
486
        }
487

    
488
        public void setHaloWidth(float haloWidth) {
489
                this.haloWidth = haloWidth;
490
                this.haloStroke = new BasicStroke(haloWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
491
        }
492

    
493
        public boolean isDrawWithHalo() {
494
                return drawWithHalo;
495
        }
496

    
497
        public void setDrawWithHalo(boolean drawWithHalo) {
498
                this.drawWithHalo = drawWithHalo;
499
        }
500

    
501
        
502
}