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

Last change on this file since 7497 was 6002, checked in by Don-vip, 11 years ago

fix #8742 - update svgsalamander to release 0.1.18+patch (fix bug SVGSALAMANDER-26) -> allow to open more SVG files

File size: 16.6 KB
Line 
1/*
2 * SVG Salamander
3 * Copyright (c) 2004, Mark McKay
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
9 *
10 * - Redistributions of source code must retain the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials
16 * provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
32 * projects can be found at http://www.kitfox.com
33 *
34 * Created on January 26, 2004, 1:56 AM
35 */
36package com.kitfox.svg;
37
38import com.kitfox.svg.xml.StyleAttribute;
39import java.awt.Graphics2D;
40import java.awt.Shape;
41import java.awt.font.FontRenderContext;
42import java.awt.geom.AffineTransform;
43import java.awt.geom.GeneralPath;
44import java.awt.geom.Rectangle2D;
45import java.util.Iterator;
46import java.util.LinkedList;
47import java.util.regex.Matcher;
48import java.util.regex.Pattern;
49
50//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
51/**
52 * @author Mark McKay
53 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
54 */
55public class Text extends ShapeElement
56{
57 public static final String TAG_NAME = "text";
58
59 float x = 0;
60 float y = 0;
61 AffineTransform transform = null;
62 String fontFamily;
63 float fontSize;
64 //List of strings and tspans containing the content of this node
65 LinkedList content = new LinkedList();
66 Shape textShape;
67 public static final int TXAN_START = 0;
68 public static final int TXAN_MIDDLE = 1;
69 public static final int TXAN_END = 2;
70 int textAnchor = TXAN_START;
71 public static final int TXST_NORMAL = 0;
72 public static final int TXST_ITALIC = 1;
73 public static final int TXST_OBLIQUE = 2;
74 int fontStyle;
75 public static final int TXWE_NORMAL = 0;
76 public static final int TXWE_BOLD = 1;
77 public static final int TXWE_BOLDER = 2;
78 public static final int TXWE_LIGHTER = 3;
79 public static final int TXWE_100 = 4;
80 public static final int TXWE_200 = 5;
81 public static final int TXWE_300 = 6;
82 public static final int TXWE_400 = 7;
83 public static final int TXWE_500 = 8;
84 public static final int TXWE_600 = 9;
85 public static final int TXWE_700 = 10;
86 public static final int TXWE_800 = 11;
87 public static final int TXWE_900 = 12;
88 int fontWeight;
89
90 /**
91 * Creates a new instance of Stop
92 */
93 public Text()
94 {
95 }
96
97 public String getTagName()
98 {
99 return TAG_NAME;
100 }
101
102 public void appendText(String text)
103 {
104 content.addLast(text);
105 }
106
107 public void appendTspan(Tspan tspan) throws SVGElementException
108 {
109 super.loaderAddChild(null, tspan);
110 content.addLast(tspan);
111 }
112
113 /**
114 * Discard cached information
115 */
116 public void rebuild() throws SVGException
117 {
118 build();
119 }
120
121 public java.util.List getContent()
122 {
123 return content;
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())
144 {
145 content.addLast(text);
146 }
147 }
148
149 public void build() throws SVGException
150 {
151 super.build();
152
153 StyleAttribute sty = new StyleAttribute();
154
155 if (getPres(sty.setName("x")))
156 {
157 x = sty.getFloatValueWithUnits();
158 }
159
160 if (getPres(sty.setName("y")))
161 {
162 y = sty.getFloatValueWithUnits();
163 }
164
165 if (getStyle(sty.setName("font-family")))
166 {
167 fontFamily = sty.getStringValue();
168 } else
169 {
170 fontFamily = "Sans Serif";
171 }
172
173 if (getStyle(sty.setName("font-size")))
174 {
175 fontSize = sty.getFloatValueWithUnits();
176 } else
177 {
178 fontSize = 12f;
179 }
180
181 if (getStyle(sty.setName("font-style")))
182 {
183 String s = sty.getStringValue();
184 if ("normal".equals(s))
185 {
186 fontStyle = TXST_NORMAL;
187 } else if ("italic".equals(s))
188 {
189 fontStyle = TXST_ITALIC;
190 } else if ("oblique".equals(s))
191 {
192 fontStyle = TXST_OBLIQUE;
193 }
194 } else
195 {
196 fontStyle = TXST_NORMAL;
197 }
198
199 if (getStyle(sty.setName("font-weight")))
200 {
201 String s = sty.getStringValue();
202 if ("normal".equals(s))
203 {
204 fontWeight = TXWE_NORMAL;
205 } else if ("bold".equals(s))
206 {
207 fontWeight = TXWE_BOLD;
208 }
209 } else
210 {
211 fontWeight = TXWE_BOLD;
212 }
213
214 if (getStyle(sty.setName("text-anchor")))
215 {
216 String s = sty.getStringValue();
217 if (s.equals("middle"))
218 {
219 textAnchor = TXAN_MIDDLE;
220 } else if (s.equals("end"))
221 {
222 textAnchor = TXAN_END;
223 } else
224 {
225 textAnchor = TXAN_START;
226 }
227 } else
228 {
229 textAnchor = TXAN_START;
230 }
231
232 //text anchor
233 //text-decoration
234 //text-rendering
235
236 buildFont();
237 }
238
239 protected void buildFont() throws SVGException
240 {
241 int style;
242 switch (fontStyle)
243 {
244 case TXST_ITALIC:
245 style = java.awt.Font.ITALIC;
246 break;
247 default:
248 style = java.awt.Font.PLAIN;
249 break;
250 }
251
252 int weight;
253 switch (fontWeight)
254 {
255 case TXWE_BOLD:
256 case TXWE_BOLDER:
257 weight = java.awt.Font.BOLD;
258 break;
259 default:
260 weight = java.awt.Font.PLAIN;
261 break;
262 }
263
264 //Get font
265 Font font = diagram.getUniverse().getFont(fontFamily);
266 if (font == null)
267 {
268// System.err.println("Could not load font");
269
270 java.awt.Font sysFont = new java.awt.Font(fontFamily, style | weight, (int) fontSize);
271 buildSysFont(sysFont);
272 return;
273 }
274
275// font = new java.awt.Font(font.getFamily(), style | weight, font.getSize());
276
277// Area textArea = new Area();
278 GeneralPath textPath = new GeneralPath();
279 textShape = textPath;
280
281 float cursorX = x, cursorY = y;
282
283 FontFace fontFace = font.getFontFace();
284 //int unitsPerEm = fontFace.getUnitsPerEm();
285 int ascent = fontFace.getAscent();
286 float fontScale = fontSize / (float) ascent;
287
288// AffineTransform oldXform = g.getTransform();
289 AffineTransform xform = new AffineTransform();
290
291 for (Iterator it = content.iterator(); it.hasNext();)
292 {
293 Object obj = it.next();
294
295 if (obj instanceof String)
296 {
297 String text = (String) obj;
298 if (text != null)
299 {
300 text = text.trim();
301 }
302
303 strokeWidthScalar = 1f / fontScale;
304
305 for (int i = 0; i < text.length(); i++)
306 {
307 xform.setToIdentity();
308 xform.setToTranslation(cursorX, cursorY);
309 xform.scale(fontScale, fontScale);
310// g.transform(xform);
311
312 String unicode = text.substring(i, i + 1);
313 MissingGlyph glyph = font.getGlyph(unicode);
314
315 Shape path = glyph.getPath();
316 if (path != null)
317 {
318 path = xform.createTransformedShape(path);
319 textPath.append(path, false);
320 }
321// else glyph.render(g);
322
323 cursorX += fontScale * glyph.getHorizAdvX();
324
325// g.setTransform(oldXform);
326 }
327
328 strokeWidthScalar = 1f;
329 } else if (obj instanceof Tspan)
330 {
331 Tspan tspan = (Tspan) obj;
332
333 xform.setToIdentity();
334 xform.setToTranslation(cursorX, cursorY);
335 xform.scale(fontScale, fontScale);
336// tspan.setCursorX(cursorX);
337// tspan.setCursorY(cursorY);
338
339 Shape tspanShape = tspan.getShape();
340 tspanShape = xform.createTransformedShape(tspanShape);
341 textPath.append(tspanShape, false);
342// tspan.render(g);
343// cursorX = tspan.getCursorX();
344// cursorY = tspan.getCursorY();
345 }
346
347 }
348
349 switch (textAnchor)
350 {
351 case TXAN_MIDDLE:
352 {
353 AffineTransform at = new AffineTransform();
354 at.translate(-textPath.getBounds2D().getWidth() / 2, 0);
355 textPath.transform(at);
356 break;
357 }
358 case TXAN_END:
359 {
360 AffineTransform at = new AffineTransform();
361 at.translate(-textPath.getBounds2D().getWidth(), 0);
362 textPath.transform(at);
363 break;
364 }
365 }
366 }
367
368 private void buildSysFont(java.awt.Font font) throws SVGException
369 {
370 GeneralPath textPath = new GeneralPath();
371 textShape = textPath;
372
373 float cursorX = x, cursorY = y;
374
375// FontMetrics fm = g.getFontMetrics(font);
376 FontRenderContext frc = new FontRenderContext(null, true, true);
377
378// FontFace fontFace = font.getFontFace();
379 //int unitsPerEm = fontFace.getUnitsPerEm();
380// int ascent = fm.getAscent();
381// float fontScale = fontSize / (float)ascent;
382
383// AffineTransform oldXform = g.getTransform();
384 AffineTransform xform = new AffineTransform();
385
386 for (Iterator it = content.iterator(); it.hasNext();)
387 {
388 Object obj = it.next();
389
390 if (obj instanceof String)
391 {
392 String text = (String) obj;
393
394 Shape textShape = font.createGlyphVector(frc, text).getOutline(cursorX, cursorY);
395 textPath.append(textShape, false);
396// renderShape(g, textShape);
397// g.drawString(text, cursorX, cursorY);
398
399 Rectangle2D rect = font.getStringBounds(text, frc);
400 cursorX += (float) rect.getWidth();
401 } else if (obj instanceof Tspan)
402 {
403 /*
404 Tspan tspan = (Tspan)obj;
405
406 xform.setToIdentity();
407 xform.setToTranslation(cursorX, cursorY);
408
409 Shape tspanShape = tspan.getShape();
410 tspanShape = xform.createTransformedShape(tspanShape);
411 textArea.add(new Area(tspanShape));
412
413 cursorX += tspanShape.getBounds2D().getWidth();
414 */
415
416
417 Tspan tspan = (Tspan) obj;
418 tspan.setCursorX(cursorX);
419 tspan.setCursorY(cursorY);
420 tspan.addShape(textPath);
421 cursorX = tspan.getCursorX();
422 cursorY = tspan.getCursorY();
423
424 }
425 }
426
427 switch (textAnchor)
428 {
429 case TXAN_MIDDLE:
430 {
431 AffineTransform at = new AffineTransform();
432 at.translate(-textPath.getBounds2D().getWidth() / 2, 0);
433 textPath.transform(at);
434 break;
435 }
436 case TXAN_END:
437 {
438 AffineTransform at = new AffineTransform();
439 at.translate(-textPath.getBounds2D().getWidth(), 0);
440 textPath.transform(at);
441 break;
442 }
443 }
444 }
445
446 public void render(Graphics2D g) throws SVGException
447 {
448 beginLayer(g);
449 renderShape(g, textShape);
450 finishLayer(g);
451 }
452
453 public Shape getShape()
454 {
455 return shapeToParent(textShape);
456 }
457
458 public Rectangle2D getBoundingBox() throws SVGException
459 {
460 return boundsToParent(includeStrokeInBounds(textShape.getBounds2D()));
461 }
462
463 /**
464 * Updates all attributes in this diagram associated with a time event. Ie,
465 * all attributes with track information.
466 *
467 * @return - true if this node has changed state as a result of the time
468 * update
469 */
470 public boolean updateTime(double curTime) throws SVGException
471 {
472// if (trackManager.getNumTracks() == 0) return false;
473 boolean changeState = super.updateTime(curTime);
474
475 //Get current values for parameters
476 StyleAttribute sty = new StyleAttribute();
477 boolean shapeChange = false;
478
479 if (getPres(sty.setName("x")))
480 {
481 float newVal = sty.getFloatValueWithUnits();
482 if (newVal != x)
483 {
484 x = newVal;
485 shapeChange = true;
486 }
487 }
488
489 if (getPres(sty.setName("y")))
490 {
491 float newVal = sty.getFloatValueWithUnits();
492 if (newVal != y)
493 {
494 y = newVal;
495 shapeChange = true;
496 }
497 }
498
499 if (getPres(sty.setName("font-family")))
500 {
501 String newVal = sty.getStringValue();
502 if (!newVal.equals(fontFamily))
503 {
504 fontFamily = newVal;
505 shapeChange = true;
506 }
507 }
508
509 if (getPres(sty.setName("font-size")))
510 {
511 float newVal = sty.getFloatValueWithUnits();
512 if (newVal != fontSize)
513 {
514 fontSize = newVal;
515 shapeChange = true;
516 }
517 }
518
519
520 if (getStyle(sty.setName("font-style")))
521 {
522 String s = sty.getStringValue();
523 int newVal = fontStyle;
524 if ("normal".equals(s))
525 {
526 newVal = TXST_NORMAL;
527 } else if ("italic".equals(s))
528 {
529 newVal = TXST_ITALIC;
530 } else if ("oblique".equals(s))
531 {
532 newVal = TXST_OBLIQUE;
533 }
534 if (newVal != fontStyle)
535 {
536 fontStyle = newVal;
537 shapeChange = true;
538 }
539 }
540
541 if (getStyle(sty.setName("font-weight")))
542 {
543 String s = sty.getStringValue();
544 int newVal = fontWeight;
545 if ("normal".equals(s))
546 {
547 newVal = TXWE_NORMAL;
548 } else if ("bold".equals(s))
549 {
550 newVal = TXWE_BOLD;
551 }
552 if (newVal != fontWeight)
553 {
554 fontWeight = newVal;
555 shapeChange = true;
556 }
557 }
558
559 if (shapeChange)
560 {
561 build();
562// buildFont();
563// return true;
564 }
565
566 return changeState || shapeChange;
567 }
568}
Note: See TracBrowser for help on using the repository browser.