Statistics
| Revision:

root / trunk / extensions / extSymbology / src / org / gvsig / symbology / fmap / symbols / SmartTextSymbol.java @ 39572

History | View | Annotate | Download (16 KB)

1
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2005-8 IVER T.I. and Generalitat Valenciana.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
18
 *
19
 * For more information, contact:
20
 *
21
 *  Generalitat Valenciana
22
 *   Conselleria d'Infraestructures i Transport
23
 *   Av. Blasco Ib??ez, 50
24
 *   46010 VALENCIA
25
 *   SPAIN
26
 *
27
 *      +34 963862235
28
 *   gvsig@gva.es
29
 *      www.gvsig.gva.es
30
 *
31
 *    or
32
 *
33
 *   IVER T.I. S.A
34
 *   Salamanca 50
35
 *   46005 Valencia
36
 *   Spain
37
 *
38
 *   +34 963163400
39
 *   dac@iver.es
40
 */
41

    
42
/* CVS MESSAGES:
43
*
44
* $Id: SmartTextSymbol.java 13953 2007-09-21 12:26:04Z jaume $
45
* $Log$
46
* Revision 1.6  2007-09-21 12:25:32  jaume
47
* cancellation support extended down to the IGeometry and ISymbol level
48
*
49
* Revision 1.5  2007/08/16 06:55:19  jvidal
50
* javadoc updated
51
*
52
* Revision 1.4  2007/08/13 11:36:50  jvidal
53
* javadoc
54
*
55
* Revision 1.3  2007/03/28 16:48:14  jaume
56
* *** empty log message ***
57
*
58
* Revision 1.2  2007/03/21 17:36:22  jaume
59
* *** empty log message ***
60
*
61
* Revision 1.1  2007/03/09 11:20:56  jaume
62
* Advanced symbology (start committing)
63
*
64
* Revision 1.1.2.8  2007/02/21 07:34:09  jaume
65
* labeling starts working
66
*
67
* Revision 1.1.2.7  2007/02/16 10:54:12  jaume
68
* multilayer splitted to multilayerline, multilayermarker,and  multilayerfill
69
*
70
* Revision 1.1.2.6  2007/02/15 16:23:44  jaume
71
* *** empty log message ***
72
*
73
* Revision 1.1.2.5  2007/02/09 07:47:05  jaume
74
* Isymbol moved
75
*
76
* Revision 1.1.2.4  2007/02/08 07:36:38  jaume
77
* *** empty log message ***
78
*
79
* Revision 1.1.2.3  2007/02/06 16:54:36  jaume
80
* *** empty log message ***
81
*
82
* Revision 1.1.2.2  2007/02/06 16:47:35  jaume
83
* first steps, follows a line
84
*
85
* Revision 1.1.2.1  2007/02/02 16:21:24  jaume
86
* start commiting labeling stuff
87
*
88
* Revision 1.1  2007/01/24 17:58:22  jaume
89
* new features and architecture error fixes
90
*
91
*
92
*/
93
package org.gvsig.symbology.fmap.symbols;
94

    
95
import java.awt.BasicStroke;
96
import java.awt.BorderLayout;
97
import java.awt.Color;
98
import java.awt.Dimension;
99
import java.awt.Font;
100
import java.awt.Graphics;
101
import java.awt.Graphics2D;
102
import java.awt.Rectangle;
103
import java.awt.Shape;
104
import java.awt.Stroke;
105
import java.awt.event.ActionEvent;
106
import java.awt.event.ActionListener;
107
import java.awt.font.FontRenderContext;
108
import java.awt.font.GlyphVector;
109
import java.awt.font.LineMetrics;
110
import java.awt.geom.AffineTransform;
111
import java.util.ArrayList;
112

    
113
import javax.print.attribute.PrintRequestAttributeSet;
114
import javax.swing.JCheckBox;
115
import javax.swing.JFrame;
116
import javax.swing.JPanel;
117
import javax.swing.JTextField;
118

    
119
import org.gvsig.gui.beans.swing.GridBagLayoutPanel;
120
import org.gvsig.gui.beans.swing.JComboBoxFonts;
121
import org.gvsig.gui.beans.swing.JIncrementalNumberField;
122
import org.gvsig.symbology.fmap.labeling.placements.PointPlacementConstraints;
123

    
124
import com.iver.cit.gvsig.fmap.ViewPort;
125
import com.iver.cit.gvsig.fmap.core.FPolygon2D;
126
import com.iver.cit.gvsig.fmap.core.FPolyline2D;
127
import com.iver.cit.gvsig.fmap.core.FShape;
128
import com.iver.cit.gvsig.fmap.core.GeneralPathX;
129
import com.iver.cit.gvsig.fmap.core.IGeometry;
130
import com.iver.cit.gvsig.fmap.core.SymbologyFactory;
131
import com.iver.cit.gvsig.fmap.core.TextPath;
132
import com.iver.cit.gvsig.fmap.core.symbols.IFillSymbol;
133
import com.iver.cit.gvsig.fmap.core.symbols.ITextSymbol;
134
import com.iver.cit.gvsig.fmap.core.symbols.SimpleTextSymbol;
135
import com.iver.cit.gvsig.fmap.core.symbols.SymbolDrawingException;
136
import com.iver.cit.gvsig.fmap.core.v02.FConstant;
137
import com.iver.cit.gvsig.fmap.rendering.styling.labeling.IPlacementConstraints;
138
import com.iver.utiles.XMLEntity;
139
import com.iver.utiles.swing.JComboBox;
140
import com.iver.utiles.swing.threads.Cancellable;
141

    
142
/**
143
 * Class used to create symbols composed using a text defined by
144
 * the user.This text can be edited (changing the color, the font of the characters, and
145
 * the rotation of the text)and has the property that can follow a path.If this path
146
 * does not exist, the text is treated as a simpletextsymbol (when is drawn).
147
 * @author   jaume dominguez faus - jaume.dominguez@iver.es
148
 */
149
public class SmartTextSymbol extends SimpleTextSymbol implements ITextSymbol {
150

    
151
        private char[] charText;
152
//        Background: ITextBackground
153
//        Case
154
        private double characterSpacing;
155
        private double characterWidth;
156
//        Direction
157
        private IFillSymbol fillSymbol;
158
        private double flipAngle;
159
//        boolean kerning;
160
        private double leading;
161
//        Position: textPosition
162
        private Color ShadowColor;
163
        private double ShadowXOffset;
164
        private double ShadowYOffset;
165
//        TypeSetting: Boolean
166
        private double wordSpacing;
167
//        ISimpleTextSymbol : ITextSymbol
168
//        BreakCharacter: Long
169
//        Clip: Boolean
170
        private TextPath textPath;
171
        private double xOffset;
172
        private double yOffset;
173
        private double angle;
174
//        Color: IColor
175

    
176
//        HorizontalAlignment:
177
//        esriTextHorizontalAlignment
178
        private boolean rightToLeft;
179
        //        VerticalAlignment
180
        private double maskSize;
181
//        MaskStyle
182
        private  IFillSymbol maskSymbol;
183
        private double margin;
184
        private int alignment;
185
        private boolean kerning = false;
186
        private TextPath tp;
187
        private IPlacementConstraints constraints;
188

    
189
        public SmartTextSymbol(ITextSymbol sym, IPlacementConstraints constraints) {
190

    
191
                if(sym instanceof SimpleTextSymbol){
192
                        SimpleTextSymbol mySym = (SimpleTextSymbol)sym;
193

    
194
                        this.setAutoresizeEnabled(mySym.isAutoresizeEnabled());
195
                        this.setDescription(mySym.getDescription());
196
                        this.setFont(mySym.getFont());
197
                        this.setFontSize(mySym.getFont().getSize());
198
                        this.setIsShapeVisible(mySym.isShapeVisible());
199
                        this.setReferenceSystem(mySym.getReferenceSystem());
200
                        this.setRotation(mySym.getRotation());
201
                        this.setText(mySym.getText());
202
                        this.setTextColor(mySym.getTextColor());
203
                        this.setUnit(mySym.getUnit());
204
                        super.setText(this.getText());
205
                        this.constraints = constraints;
206

    
207
                        setCharacterSpacing(2); //???
208
                        setWordSpacing(TextPath.DEFAULT_WORD_SPACING);
209
                        boolean rtl = false; // right to left text
210
                        if (constraints.isAtTheBeginingOfLine()) {
211
                                if (rtl) {
212
                                        setAlignment(FConstant.SYMBOL_STYLE_TEXTALIGNMENT_RIGHT);
213
                                }
214
                                else {
215
                                        setAlignment(FConstant.SYMBOL_STYLE_TEXTALIGNMENT_LEFT);
216
                                }
217
                        }
218
                        else if (constraints.isAtTheEndOfLine()) {
219
                                if (rtl) {
220
                                        setAlignment(FConstant.SYMBOL_STYLE_TEXTALIGNMENT_LEFT);
221
                                }
222
                                else {
223
                                        setAlignment(FConstant.SYMBOL_STYLE_TEXTALIGNMENT_RIGHT);
224
                                }
225
                        }
226
                        else { //constraints.isInTheMiddleOfLine() or constraints.isAtBestOfLine()
227
                                setAlignment(FConstant.SYMBOL_STYLE_TEXTALIGNMENT_CENTERED);
228
                        }
229
                        setKerning(false);
230
                        setRightToLeft(rtl);
231
                }
232
        }
233
        public SmartTextSymbol() {
234
                PointPlacementConstraints pc = new PointPlacementConstraints();
235
                this.constraints = pc;
236
                
237
        }
238

    
239
        /**
240
         * Draws the text according. If this symbol has the text path set, then
241
         * it is used as the text line, otherwise shp <b>must be an FPoint2D</b>
242
         * indicating the starting point of the text and then the text will
243
         * be rendered from there and following the rotation previously set.
244
         */
245
        @Override
246
        public void draw(Graphics2D g, AffineTransform affineTransform, FShape shp, Cancellable cancel) {
247
                if (!isShapeVisible()) return;
248

    
249
                setMargin(0);
250

    
251

    
252
                tp = new TextPath(g, shp, charText, getFont(),
253
                                (float) characterSpacing, (float) characterWidth, kerning,
254
                                (float) leading, alignment, (float) wordSpacing, (float) margin, rightToLeft);
255
                Font font = getFont();
256
                g.setFont(font);
257
                FontRenderContext frc = g.getFontRenderContext();
258
                LineMetrics lineMetrics = font.getLineMetrics(getText(), frc);
259
                
260
//                GlyphVector glyph =  font.layoutGlyphVector(frc, charText, 0, charText.length, Font.LAYOUT_NO_START_CONTEXT);
261
                double cons = 0;
262

    
263
                /* Repartimos el leading (espacio de separaci?n entre lineas)
264
                 * arriba y abajo para que exista la misma separaci?n entre la
265
                 * caja de la letra y la linea tanto si se dibuja por abajo como
266
                 * si se dibuja por arriba. 
267
                 */
268
                if(this.constraints.isAboveTheLine()) {
269
                        cons = lineMetrics.getDescent()+lineMetrics.getLeading()/2;
270
                }
271
                else if (this.constraints.isBelowTheLine()) {
272
                        cons = -(lineMetrics.getAscent()+lineMetrics.getLeading()/2);
273
                }
274
                /* Dibujamos la letra de tal manera que el centro de la caja de letra
275
                 * coincida con la linea
276
                 */
277
                else if(this.constraints.isOnTheLine()) {
278
//                        cons = lineMetrics.getDescent()+(lineMetrics.getLeading()/2)-(lineMetrics.getHeight()/2);
279
                        cons = lineMetrics.getDescent()+lineMetrics.getLeading()-(lineMetrics.getHeight()/2);
280
                }
281
                
282
                double[] coords = tp.nextPosForGlyph(0);
283
                Stroke haloStroke = new BasicStroke(getHaloWidth(), BasicStroke.CAP_ROUND,
284
                BasicStroke.JOIN_ROUND);
285

    
286
                for (int i = 0; i < tp.getGlyphCount(); i++) {
287
                        coords = tp.nextPosForGlyph(i);
288
                        if (coords[0] == TextPath.NO_POS || coords[1] == TextPath.NO_POS)
289
                                continue;
290

    
291
                        // move the label 'cons" units above/below the line
292
                        double xOffset = cons * Math.sin(coords[2]);
293
                        double yOffset = cons * Math.cos(coords[2]);
294

    
295
                        g.translate(coords[0]+xOffset, coords[1]-yOffset);
296
                        g.rotate(coords[2]);
297

    
298
                        char[] aux = new char[1];
299
                        aux[0] = charText[i];
300
                        if (isDrawWithHalo()) {
301
                                GlyphVector glyph = font.createGlyphVector(frc, aux);
302
                                Shape outlineChar = glyph.getOutline();
303
                                g.setStroke(haloStroke);
304
                                g.setColor(getHaloColor());
305
                                g.draw(outlineChar);
306
                        }
307
                        
308
                        g.setColor(this.getTextColor());
309
                        g.drawString(String.valueOf(charText[i]), 0, 0);
310
                        g.rotate(-coords[2]);
311
                        g.translate(-coords[0]-xOffset, -coords[1]+yOffset);
312
                }
313
        }
314

    
315
        @Override
316
        public void getPixExtentPlus(FShape shp, float[] distances, ViewPort viewPort, int dpi) {
317
                // TODO Implement it
318
                throw new Error("Not yet implemented!");
319

    
320
        }
321

    
322
        @Override
323
        public int getOnePointRgb() {
324
                return getTextColor().getRGB();
325
        }
326

    
327
        @Override
328
        public XMLEntity getXMLEntity() {
329
                XMLEntity xml = new XMLEntity();
330
                xml.putProperty("className", getClassName());
331
                xml.putProperty("desc", getDescription());
332
                xml.putProperty("isShapeVisible", isShapeVisible());
333
                return xml;
334
        }
335

    
336
        @Override
337
        public int getSymbolType() {
338
                return FShape.TEXT;
339
        }
340

    
341
        @Override
342
        public boolean isSuitableFor(IGeometry geom) {
343
                return (geom.getGeometryType()%FShape.Z) == FShape.LINE;
344
        }
345

    
346
        @Override
347
        public void drawInsideRectangle(Graphics2D g, AffineTransform scaleInstance, Rectangle r, PrintRequestAttributeSet properties) throws SymbolDrawingException {
348
                // let's take the bottom segment of the rectangle as the line
349

    
350
                GeneralPathX gpx = new GeneralPathX();
351
                gpx.moveTo(r.getX(), r.getY());
352
                gpx.lineTo(r.getX()+r.getWidth(), r.getY());
353
                if (properties==null)
354
                        draw(g, scaleInstance, new FPolygon2D(gpx), null);
355
                else
356
                        print(g, scaleInstance, new FPolygon2D(gpx), properties);
357

    
358
        }
359

    
360
        @Override
361
        public String getClassName() {
362
                return getClass().getName();
363
        }
364

    
365
        @Override
366
        public void setXMLEntity(XMLEntity xml) {
367
                setFont(new Font("Arial", Font.PLAIN, 18));
368
                setText("this is my TEST text that follows a line");
369
                setDescription(xml.getStringProperty("desc"));
370
                setIsShapeVisible(xml.getBooleanProperty("isShapeVisible"));
371

    
372
        }
373

    
374
        @Override
375
        public void setText(String text) {
376
                this.charText = text.toCharArray();
377
        }
378

    
379
        @Override
380
        public String getText() {
381
                return new String(charText);
382
        }
383

    
384
        public void setCharacterSpacing(double charSpacing) {
385
                this.characterSpacing = charSpacing;
386
        }
387

    
388
        public void setWordSpacing(double wordSpacing) {
389
                this.wordSpacing = wordSpacing;
390
        }
391

    
392
        public void setAlignment(int alignment) {
393
                this.alignment = alignment;
394
        }
395

    
396
        public void setKerning(boolean kerning) {
397
                this.kerning = kerning;
398
        }
399

    
400
        public void setMargin(double margin) {
401
                this.margin = margin;
402
        }
403

    
404
        public void setRightToLeft(boolean rightToLeft) {
405
                this.rightToLeft = rightToLeft;
406
        }
407

    
408
        public static void main(String[] args) {
409
                class Item {
410
                        int value;
411
                        String text;
412
                        public Item(int value, String text) {
413
                                this.value = value;
414
                                this.text = text;
415
                        }
416
                        @Override
417
                        public String toString() {
418
                                return text;
419
                        }
420

    
421
                        public int getValue() {
422
                                return value;
423
                        }
424
                }
425

    
426
                final int initialWidth = 300;
427
                final int initialHeight = 300;
428

    
429
                final JTextField textField = new JTextField("write your text here");
430
                final JIncrementalNumberField textFontSize = new JIncrementalNumberField();
431
                textFontSize.setDouble(15);
432
                final JIncrementalNumberField textCharSpacing = new JIncrementalNumberField();
433
                textCharSpacing.setDouble(1);
434
                final JIncrementalNumberField textWordSpacing = new JIncrementalNumberField();
435
                textCharSpacing.setDouble(10);
436
                final JCheckBox chkKerning = new JCheckBox("Kerning:");
437
                final JCheckBox chkRightToLeft = new JCheckBox("Rigth to left:");
438
                final JComboBoxFonts cmbFonts = new JComboBoxFonts();
439

    
440
                ArrayList<Integer> alignments = new ArrayList<Integer>();
441
                final JComboBox cmbAlign = new JComboBox(new Item[] {
442
                                new Item(FConstant.SYMBOL_STYLE_TEXTALIGNMENT_LEFT, "LEFT"),
443
                                new Item(FConstant.SYMBOL_STYLE_TEXTALIGNMENT_RIGHT, "RIGHT"),
444
                                new Item(FConstant.SYMBOL_STYLE_TEXTALIGNMENT_CENTERED, "CENTER"),
445
                                new Item(FConstant.SYMBOL_STYLE_TEXTALIGNMENT_JUSTIFY, "JUSTIFY"),
446
                });
447

    
448
                final JIncrementalNumberField txtMargin = new JIncrementalNumberField();
449
                txtMargin.setDouble(10);
450

    
451

    
452

    
453
                final JFrame f = new JFrame("test Smart Text Symbol");
454
                JPanel content = new JPanel(new BorderLayout(20,20));
455
                GridBagLayoutPanel controlPanel = new GridBagLayoutPanel();
456
                controlPanel.addComponent("Text: ", textField);
457
                controlPanel.addComponent("Font: ", cmbFonts);
458
                controlPanel.addComponent("Font size: ", textFontSize);
459
                controlPanel.addComponent("Char spacing: ", textCharSpacing);
460
                controlPanel.addComponent("Word spacing: ", textWordSpacing);
461
                controlPanel.addComponent("Alignment: ", cmbAlign);
462
                controlPanel.addComponent("Margin: ", txtMargin);
463
                controlPanel.addComponent(chkKerning);
464
                controlPanel.addComponent(chkRightToLeft);
465

    
466
                content.add(controlPanel, BorderLayout.NORTH);
467

    
468
                final JPanel canvas = new JPanel() {
469
                        private static final long serialVersionUID = 623038680274774722L;
470

    
471
                        @Override
472
                        protected void paintComponent(Graphics g) {
473
                                Graphics2D g2 = (Graphics2D) g;
474
                                double width = getBounds().getWidth();
475
                                double height= getBounds().getHeight();
476
                                GeneralPathX gpx = new GeneralPathX();
477

    
478
                                gpx.moveTo(50, 100);
479
                                gpx.lineTo(100, 50);
480
                                gpx.lineTo(150, 100);
481
                                gpx.lineTo(250, 50);
482

    
483
                                SmartTextSymbol sms = new SmartTextSymbol();
484
                                FPolyline2D theLineShape = new FPolyline2D(gpx);
485
                                SymbologyFactory.createDefaultLineSymbol().draw(g2, null, theLineShape, null);
486
                                sms.setFont(new Font((String) cmbFonts.getSelectedItem(), Font.PLAIN, 10));
487
                                sms.setFontSize(textFontSize.getDouble());
488
                                sms.setText(textField.getText());
489
                                sms.setCharacterSpacing(textCharSpacing.getDouble());
490
                                sms.setWordSpacing(textWordSpacing.getDouble());
491
                                sms.setAlignment(((Item) cmbAlign.getSelectedItem()).getValue());
492
                                sms.setKerning(chkKerning.isSelected());
493
                                sms.setRightToLeft(chkRightToLeft.isSelected());
494
                                sms.setMargin(txtMargin.getDouble());
495
                                sms.draw(g2, null, theLineShape, null);
496
                        }
497
                };
498

    
499
                ActionListener action = new ActionListener() {
500
                        public void actionPerformed(ActionEvent e) {
501
                                f.repaint();
502
                        }
503
                };
504
                textFontSize.addActionListener(action);
505
                textCharSpacing.addActionListener(action);
506
                textWordSpacing.addActionListener(action);
507
                cmbAlign.addActionListener(action);
508
                txtMargin.addActionListener(action);
509
                chkKerning.addActionListener(action);
510
                chkRightToLeft.addActionListener(action);
511
                cmbFonts.addActionListener(action);
512

    
513

    
514
                canvas.setPreferredSize(new Dimension(initialWidth, initialHeight));
515
                canvas.setSize(new Dimension(initialWidth, initialHeight));
516
                content.add(canvas, BorderLayout.CENTER);
517
                f.setContentPane(content);
518
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
519

    
520

    
521
                f.pack();
522
                f.setVisible(true);
523
        }
524

    
525
}