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 @ 40710

History | View | Annotate | Download (15.7 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(
182
                                                r.getX(),
183
                                                // Small adjustment to center the fonts
184
                                                r.getY() + 0.725 * r.getHeight(),
185
                                                SUBTYPES.GEOM2D), null, null);
186
                        else
187
                                print(g, new AffineTransform(), geomManager.createPoint(r.getX(), r.getY(), SUBTYPES.GEOM2D), properties);
188
                } catch (CreateGeometryException e) {
189
                        LOG.error("Error creatint a point", e);
190
                        e.printStackTrace();
191
                }
192

    
193

    
194
        }
195

    
196
        public int getOnePointRgb() {
197
                return textColor.getRGB();
198
        }
199

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

    
204
        }
205

    
206
        public ISymbol getSymbolForSelection() {
207
                return this; // a text is not selectable
208
        }
209

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

    
216
        public boolean isSuitableFor(Geometry geom) {
217
                return true;
218
        }
219

    
220
        public String getClassName() {
221
                return getClass().getName();
222
        }
223

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

    
241
        public String getText() {
242
                return text;
243
        }
244

    
245
        public Font getFont() {
246
                return font;
247
        }
248

    
249
        public Color getTextColor() {
250
                return textColor;
251
        }
252

    
253
        public void setText(String text) {
254
                this.text = text;
255
        }
256

    
257
        public void setFont(Font font) {
258
                if (font == null) {
259
                        LOG.warn("font <-- null");
260

    
261
                        return;
262
                }
263
                this.font = font;
264
        }
265

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

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

    
289
        public double getRotation() {
290
                return rotation;
291
        }
292

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

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

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

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

    
328
        public Rectangle getBounds() {
329
//                FontMetrics fm = g.getFontMetrics();
330
//                Rectangle2D rect = fm.getStringBounds("graphics", g);
331

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

    
341
        public void setCartographicSize(double cartographicSize, Geometry geom) {
342
                setFontSize(cartographicSize);
343
        }
344

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

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

    
366
        public void setAutoresizeEnabled(boolean autoresizeFlag) {
367
                this.autoresize = autoresizeFlag;
368
        }
369

    
370
        
371
        public Object clone() throws CloneNotSupportedException {
372
                SimpleTextSymbol copy = (SimpleTextSymbol) super.clone();
373

    
374
                return copy;
375
        }
376

    
377
        public void loadFromState(PersistentState state)
378
        
379
                        throws PersistenceException {
380
                // Set parent fill symbol properties
381
                super.loadFromState(state);
382

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

    
395
        public void saveToState(PersistentState state) throws PersistenceException {
396
                // Save parent fill symbol properties
397
                super.saveToState(state);
398

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

    
411
        public static class RegisterPersistence implements Callable {
412

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

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

    
458
        public static class RegisterSymbol implements Callable {
459

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

    
471
                        return Boolean.TRUE;
472
                }
473
                
474
        }
475

    
476
        
477
        public Color getHaloColor() {
478
                return haloColor;
479
        }
480

    
481
        public void setHaloColor(Color co) {
482
                if (co != null) {
483
                        this.haloColor = co;
484
                }
485
        }
486

    
487
        public float getHaloWidth() {
488
                return haloWidth;
489
        }
490

    
491
        public void setHaloWidth(float haloWidth) {
492
                this.haloWidth = haloWidth;
493
                this.haloStroke = new BasicStroke(haloWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
494
        }
495

    
496
        public boolean isDrawWithHalo() {
497
                return drawWithHalo;
498
        }
499

    
500
        public void setDrawWithHalo(boolean drawWithHalo) {
501
                this.drawWithHalo = drawWithHalo;
502
        }
503

    
504
        
505
}