source: josm/trunk/src/com/kitfox/svg/Text.java@ 5693

Last change on this file since 5693 was 4256, checked in by bastiK, 13 years ago

see #6560 - basic svg support, includes kitfox svgsalamander, r 98, patched

File size: 16.4 KB
Line 
1/*
2 * Stop.java
3 *
4 *
5 * The Salamander Project - 2D and 3D graphics libraries in Java
6 * Copyright (C) 2004 Mark McKay
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
23 * projects can be found at http://www.kitfox.com
24 *
25 * Created on January 26, 2004, 1:56 AM
26 */
27
28package com.kitfox.svg;
29
30import com.kitfox.svg.xml.StyleAttribute;
31import java.awt.*;
32import java.awt.font.*;
33import java.awt.geom.*;
34import java.util.*;
35import java.util.regex.*;
36
37//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
38
39/**
40 * @author Mark McKay
41 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
42 */
43public class Text extends ShapeElement
44{
45
46 float x = 0;
47 float y = 0;
48 AffineTransform transform = null;
49
50 String fontFamily;
51 float fontSize;
52
53 //List of strings and tspans containing the content of this node
54 LinkedList content = new LinkedList();
55
56 Shape textShape;
57
58 public static final int TXAN_START = 0;
59 public static final int TXAN_MIDDLE = 1;
60 public static final int TXAN_END = 2;
61 int textAnchor = TXAN_START;
62
63 public static final int TXST_NORMAL = 0;
64 public static final int TXST_ITALIC = 1;
65 public static final int TXST_OBLIQUE = 2;
66 int fontStyle;
67
68 public static final int TXWE_NORMAL = 0;
69 public static final int TXWE_BOLD = 1;
70 public static final int TXWE_BOLDER = 2;
71 public static final int TXWE_LIGHTER = 3;
72 public static final int TXWE_100 = 4;
73 public static final int TXWE_200 = 5;
74 public static final int TXWE_300 = 6;
75 public static final int TXWE_400 = 7;
76 public static final int TXWE_500 = 8;
77 public static final int TXWE_600 = 9;
78 public static final int TXWE_700 = 10;
79 public static final int TXWE_800 = 11;
80 public static final int TXWE_900 = 12;
81 int fontWeight;
82
83 /** Creates a new instance of Stop */
84 public Text()
85 {
86 }
87
88 public void appendText(String text)
89 {
90 content.addLast(text);
91 }
92
93 public void appendTspan(Tspan tspan) throws SVGElementException
94 {
95 super.loaderAddChild(null, tspan);
96 content.addLast(tspan);
97// tspan.setParent(this);
98 }
99
100 /**
101 * Discard cached information
102 */
103 public void rebuild() throws SVGException
104 {
105 build();
106 }
107
108 public java.util.List getContent()
109 {
110 return content;
111 }
112/*
113 public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent)
114 {
115 //Load style string
116 super.loaderStartElement(helper, attrs, parent);
117
118 String x = attrs.getValue("x");
119 String y = attrs.getValue("y");
120 //String transform = attrs.getValue("transform");
121
122 this.x = XMLParseUtil.parseFloat(x);
123 this.y = XMLParseUtil.parseFloat(y);
124 }
125 */
126 /**
127 * Called after the start element but before the end element to indicate
128 * each child tag that has been processed
129 */
130 public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException
131 {
132 super.loaderAddChild(helper, child);
133
134 content.addLast(child);
135 }
136
137 /**
138 * Called during load process to add text scanned within a tag
139 */
140 public void loaderAddText(SVGLoaderHelper helper, String text)
141 {
142 Matcher matchWs = Pattern.compile("\\s*").matcher(text);
143 if (!matchWs.matches()) content.addLast(text);
144 }
145
146 public void build() throws SVGException
147 {
148 super.build();
149
150 StyleAttribute sty = new StyleAttribute();
151
152 if (getPres(sty.setName("x"))) x = sty.getFloatValueWithUnits();
153
154 if (getPres(sty.setName("y"))) y = sty.getFloatValueWithUnits();
155
156 if (getStyle(sty.setName("font-family"))) fontFamily = sty.getStringValue();
157 else fontFamily = "Sans Serif";
158
159 if (getStyle(sty.setName("font-size"))) fontSize = sty.getFloatValueWithUnits();
160 else fontSize = 12f;
161
162 if (getStyle(sty.setName("font-style")))
163 {
164 String s = sty.getStringValue();
165 if ("normal".equals(s))
166 {
167 fontStyle = TXST_NORMAL;
168 }
169 else if ("italic".equals(s))
170 {
171 fontStyle = TXST_ITALIC;
172 }
173 else if ("oblique".equals(s))
174 {
175 fontStyle = TXST_OBLIQUE;
176 }
177 }
178 else fontStyle = TXST_NORMAL;
179
180 if (getStyle(sty.setName("font-weight")))
181 {
182 String s = sty.getStringValue();
183 if ("normal".equals(s))
184 {
185 fontWeight = TXWE_NORMAL;
186 }
187 else if ("bold".equals(s))
188 {
189 fontWeight = TXWE_BOLD;
190 }
191 }
192 else fontWeight = TXWE_BOLD;
193
194 if (getStyle(sty.setName("text-anchor")))
195 {
196 String s = sty.getStringValue();
197 if (s.equals("middle")) textAnchor = TXAN_MIDDLE;
198 else if (s.equals("end")) textAnchor = TXAN_END;
199 else textAnchor = TXAN_START;
200 }
201 else textAnchor = TXAN_START;
202
203 //text anchor
204 //text-decoration
205 //text-rendering
206
207 buildFont();
208 }
209
210 protected void buildFont() throws SVGException
211 {
212 int style;
213 switch (fontStyle)
214 {
215 case TXST_ITALIC:
216 style = java.awt.Font.ITALIC;
217 break;
218 default:
219 style = java.awt.Font.PLAIN;
220 break;
221 }
222
223 int weight;
224 switch (fontWeight)
225 {
226 case TXWE_BOLD:
227 case TXWE_BOLDER:
228 weight = java.awt.Font.BOLD;
229 break;
230 default:
231 weight = java.awt.Font.PLAIN;
232 break;
233 }
234
235 //Get font
236 Font font = diagram.getUniverse().getFont(fontFamily);
237 if (font == null)
238 {
239// System.err.println("Could not load font");
240
241 java.awt.Font sysFont = new java.awt.Font(fontFamily, style | weight, (int)fontSize);
242 buildSysFont(sysFont);
243 return;
244 }
245
246// font = new java.awt.Font(font.getFamily(), style | weight, font.getSize());
247
248// Area textArea = new Area();
249 GeneralPath textPath = new GeneralPath();
250 textShape = textPath;
251
252 float cursorX = x, cursorY = y;
253
254 FontFace fontFace = font.getFontFace();
255 //int unitsPerEm = fontFace.getUnitsPerEm();
256 int ascent = fontFace.getAscent();
257 float fontScale = fontSize / (float)ascent;
258
259// AffineTransform oldXform = g.getTransform();
260 AffineTransform xform = new AffineTransform();
261
262 for (Iterator it = content.iterator(); it.hasNext();)
263 {
264 Object obj = it.next();
265
266 if (obj instanceof String)
267 {
268 String text = (String)obj;
269
270 strokeWidthScalar = 1f / fontScale;
271
272 for (int i = 0; i < text.length(); i++)
273 {
274 xform.setToIdentity();
275 xform.setToTranslation(cursorX, cursorY);
276 xform.scale(fontScale, fontScale);
277// g.transform(xform);
278
279 String unicode = text.substring(i, i + 1);
280 MissingGlyph glyph = font.getGlyph(unicode);
281
282 Shape path = glyph.getPath();
283 if (path != null)
284 {
285 path = xform.createTransformedShape(path);
286 textPath.append(path, false);
287 }
288// else glyph.render(g);
289
290 cursorX += fontScale * glyph.getHorizAdvX();
291
292// g.setTransform(oldXform);
293 }
294
295 strokeWidthScalar = 1f;
296 }
297 else if (obj instanceof Tspan)
298 {
299 Tspan tspan = (Tspan)obj;
300
301 xform.setToIdentity();
302 xform.setToTranslation(cursorX, cursorY);
303 xform.scale(fontScale, fontScale);
304// tspan.setCursorX(cursorX);
305// tspan.setCursorY(cursorY);
306
307 Shape tspanShape = tspan.getShape();
308 tspanShape = xform.createTransformedShape(tspanShape);
309 textPath.append(tspanShape, false);
310// tspan.render(g);
311// cursorX = tspan.getCursorX();
312// cursorY = tspan.getCursorY();
313 }
314
315 }
316
317 switch (textAnchor)
318 {
319 case TXAN_MIDDLE:
320 {
321 AffineTransform at = new AffineTransform();
322 at.translate(-textPath.getBounds2D().getWidth() / 2, 0);
323 textPath.transform(at);
324 break;
325 }
326 case TXAN_END:
327 {
328 AffineTransform at = new AffineTransform();
329 at.translate(-textPath.getBounds2D().getWidth(), 0);
330 textPath.transform(at);
331 break;
332 }
333 }
334 }
335
336 private void buildSysFont(java.awt.Font font) throws SVGException
337 {
338 GeneralPath textPath = new GeneralPath();
339 textShape = textPath;
340
341 float cursorX = x, cursorY = y;
342
343// FontMetrics fm = g.getFontMetrics(font);
344 FontRenderContext frc = new FontRenderContext(null, true, true);
345
346// FontFace fontFace = font.getFontFace();
347 //int unitsPerEm = fontFace.getUnitsPerEm();
348// int ascent = fm.getAscent();
349// float fontScale = fontSize / (float)ascent;
350
351// AffineTransform oldXform = g.getTransform();
352 AffineTransform xform = new AffineTransform();
353
354 for (Iterator it = content.iterator(); it.hasNext();)
355 {
356 Object obj = it.next();
357
358 if (obj instanceof String)
359 {
360 String text = (String)obj;
361
362 Shape textShape = font.createGlyphVector(frc, text).getOutline(cursorX, cursorY);
363 textPath.append(textShape, false);
364// renderShape(g, textShape);
365// g.drawString(text, cursorX, cursorY);
366
367 Rectangle2D rect = font.getStringBounds(text, frc);
368 cursorX += (float)rect.getWidth();
369 }
370 else if (obj instanceof Tspan)
371 {
372 /*
373 Tspan tspan = (Tspan)obj;
374
375 xform.setToIdentity();
376 xform.setToTranslation(cursorX, cursorY);
377
378 Shape tspanShape = tspan.getShape();
379 tspanShape = xform.createTransformedShape(tspanShape);
380 textArea.add(new Area(tspanShape));
381
382 cursorX += tspanShape.getBounds2D().getWidth();
383 */
384
385
386 Tspan tspan = (Tspan)obj;
387 tspan.setCursorX(cursorX);
388 tspan.setCursorY(cursorY);
389 tspan.addShape(textPath);
390 cursorX = tspan.getCursorX();
391 cursorY = tspan.getCursorY();
392
393 }
394 }
395
396 switch (textAnchor)
397 {
398 case TXAN_MIDDLE:
399 {
400 AffineTransform at = new AffineTransform();
401 at.translate(-textPath.getBounds2D().getWidth() / 2, 0);
402 textPath.transform(at);
403 break;
404 }
405 case TXAN_END:
406 {
407 AffineTransform at = new AffineTransform();
408 at.translate(-textPath.getBounds2D().getWidth(), 0);
409 textPath.transform(at);
410 break;
411 }
412 }
413 }
414
415
416 public void render(Graphics2D g) throws SVGException
417 {
418 beginLayer(g);
419 renderShape(g, textShape);
420 finishLayer(g);
421 }
422
423 public Shape getShape()
424 {
425 return shapeToParent(textShape);
426 }
427
428 public Rectangle2D getBoundingBox() throws SVGException
429 {
430 return boundsToParent(includeStrokeInBounds(textShape.getBounds2D()));
431 }
432
433 /**
434 * Updates all attributes in this diagram associated with a time event.
435 * Ie, all attributes with track information.
436 * @return - true if this node has changed state as a result of the time
437 * update
438 */
439 public boolean updateTime(double curTime) throws SVGException
440 {
441// if (trackManager.getNumTracks() == 0) return false;
442 boolean changeState = super.updateTime(curTime);
443
444 //Get current values for parameters
445 StyleAttribute sty = new StyleAttribute();
446 boolean shapeChange = false;
447
448 if (getPres(sty.setName("x")))
449 {
450 float newVal = sty.getFloatValueWithUnits();
451 if (newVal != x)
452 {
453 x = newVal;
454 shapeChange = true;
455 }
456 }
457
458 if (getPres(sty.setName("y")))
459 {
460 float newVal = sty.getFloatValueWithUnits();
461 if (newVal != y)
462 {
463 y = newVal;
464 shapeChange = true;
465 }
466 }
467
468 if (getPres(sty.setName("font-family")))
469 {
470 String newVal = sty.getStringValue();
471 if (!newVal.equals(fontFamily))
472 {
473 fontFamily = newVal;
474 shapeChange = true;
475 }
476 }
477
478 if (getPres(sty.setName("font-size")))
479 {
480 float newVal = sty.getFloatValueWithUnits();
481 if (newVal != fontSize)
482 {
483 fontSize = newVal;
484 shapeChange = true;
485 }
486 }
487
488
489 if (getStyle(sty.setName("font-style")))
490 {
491 String s = sty.getStringValue();
492 int newVal = fontStyle;
493 if ("normal".equals(s))
494 {
495 newVal = TXST_NORMAL;
496 }
497 else if ("italic".equals(s))
498 {
499 newVal = TXST_ITALIC;
500 }
501 else if ("oblique".equals(s))
502 {
503 newVal = TXST_OBLIQUE;
504 }
505 if (newVal != fontStyle)
506 {
507 fontStyle = newVal;
508 shapeChange = true;
509 }
510 }
511
512 if (getStyle(sty.setName("font-weight")))
513 {
514 String s = sty.getStringValue();
515 int newVal = fontWeight;
516 if ("normal".equals(s))
517 {
518 newVal = TXWE_NORMAL;
519 }
520 else if ("bold".equals(s))
521 {
522 newVal = TXWE_BOLD;
523 }
524 if (newVal != fontWeight)
525 {
526 fontWeight = newVal;
527 shapeChange = true;
528 }
529 }
530
531 if (shapeChange)
532 {
533 build();
534// buildFont();
535// return true;
536 }
537
538 return changeState || shapeChange;
539 }
540}
Note: See TracBrowser for help on using the repository browser.