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

Last change on this file since 10787 was 10787, checked in by Don-vip, 8 years ago

fix #13291 - upgrade to svgSalamander v1.1.0 (patched)

now detects two invalid SVG files: presets/sport/volleyball.svg and presets/shop/diy_store.svg

  • Property svn:eol-style set to native
File size: 16.7 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.util.FontSystem;
39import com.kitfox.svg.xml.StyleAttribute;
40import java.awt.Graphics2D;
41import java.awt.Shape;
42import java.awt.geom.AffineTransform;
43import java.awt.geom.GeneralPath;
44import java.awt.geom.Point2D;
45import java.awt.geom.Rectangle2D;
46import java.util.Iterator;
47import java.util.LinkedList;
48import java.util.regex.Matcher;
49import java.util.regex.Pattern;
50
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 float textLength = -1;
91 String lengthAdjust = "spacing";
92
93 /**
94 * Creates a new instance of Stop
95 */
96 public Text()
97 {
98 }
99
100 public String getTagName()
101 {
102 return TAG_NAME;
103 }
104
105 public void appendText(String text)
106 {
107 content.addLast(text);
108 }
109
110 public void appendTspan(Tspan tspan) throws SVGElementException
111 {
112 super.loaderAddChild(null, tspan);
113 content.addLast(tspan);
114 }
115
116 /**
117 * Discard cached information
118 */
119 public void rebuild() throws SVGException
120 {
121 build();
122 }
123
124 public java.util.List getContent()
125 {
126 return content;
127 }
128
129 /**
130 * Called after the start element but before the end element to indicate
131 * each child tag that has been processed
132 */
133 public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException
134 {
135 super.loaderAddChild(helper, child);
136
137 content.addLast(child);
138 }
139
140 /**
141 * Called during load process to add text scanned within a tag
142 */
143 public void loaderAddText(SVGLoaderHelper helper, String text)
144 {
145 Matcher matchWs = Pattern.compile("\\s*").matcher(text);
146 if (!matchWs.matches())
147 {
148 content.addLast(text);
149 }
150 }
151
152 public void build() throws SVGException
153 {
154 super.build();
155
156 StyleAttribute sty = new StyleAttribute();
157
158 if (getPres(sty.setName("x")))
159 {
160 x = sty.getFloatValueWithUnits();
161 }
162
163 if (getPres(sty.setName("y")))
164 {
165 y = sty.getFloatValueWithUnits();
166 }
167
168 if (getStyle(sty.setName("font-family")))
169 {
170 fontFamily = sty.getStringValue();
171 }
172 else
173 {
174 fontFamily = "Sans Serif";
175 }
176
177 if (getStyle(sty.setName("font-size")))
178 {
179 fontSize = sty.getFloatValueWithUnits();
180 }
181 else
182 {
183 fontSize = 12f;
184 }
185
186 if (getStyle(sty.setName("textLength")))
187 {
188 textLength = sty.getFloatValueWithUnits();
189 }
190 else
191 {
192 textLength = -1;
193 }
194
195 if (getStyle(sty.setName("lengthAdjust")))
196 {
197 lengthAdjust = sty.getStringValue();
198 }
199 else
200 {
201 lengthAdjust = "spacing";
202 }
203
204 if (getStyle(sty.setName("font-style")))
205 {
206 String s = sty.getStringValue();
207 if ("normal".equals(s))
208 {
209 fontStyle = TXST_NORMAL;
210 } else if ("italic".equals(s))
211 {
212 fontStyle = TXST_ITALIC;
213 } else if ("oblique".equals(s))
214 {
215 fontStyle = TXST_OBLIQUE;
216 }
217 } else
218 {
219 fontStyle = TXST_NORMAL;
220 }
221
222 if (getStyle(sty.setName("font-weight")))
223 {
224 String s = sty.getStringValue();
225 if ("normal".equals(s))
226 {
227 fontWeight = TXWE_NORMAL;
228 } else if ("bold".equals(s))
229 {
230 fontWeight = TXWE_BOLD;
231 }
232 } else
233 {
234 fontWeight = TXWE_NORMAL;
235 }
236
237 if (getStyle(sty.setName("text-anchor")))
238 {
239 String s = sty.getStringValue();
240 if (s.equals("middle"))
241 {
242 textAnchor = TXAN_MIDDLE;
243 } else if (s.equals("end"))
244 {
245 textAnchor = TXAN_END;
246 } else
247 {
248 textAnchor = TXAN_START;
249 }
250 } else
251 {
252 textAnchor = TXAN_START;
253 }
254
255 //text anchor
256 //text-decoration
257 //text-rendering
258
259 buildText();
260 }
261
262 protected void buildText() throws SVGException
263 {
264 //Get font
265 String[] families = fontFamily.split(",");
266 Font font = null;
267 for (int i = 0; i < families.length; ++i)
268 {
269 font = diagram.getUniverse().getFont(fontFamily);
270 if (font != null)
271 {
272 break;
273 }
274 }
275
276 if (font == null)
277 {
278 font = new FontSystem(fontFamily, fontStyle, fontWeight, (int)fontSize);
279 }
280
281 GeneralPath textPath = new GeneralPath();
282 textShape = textPath;
283
284 float cursorX = x, cursorY = y;
285
286 AffineTransform xform = new AffineTransform();
287
288 for (Iterator it = content.iterator(); it.hasNext();)
289 {
290 Object obj = it.next();
291
292 if (obj instanceof String)
293 {
294 String text = (String) obj;
295 if (text != null)
296 {
297 text = text.trim();
298 }
299
300 for (int i = 0; i < text.length(); i++)
301 {
302 xform.setToIdentity();
303 xform.setToTranslation(cursorX, cursorY);
304
305 String unicode = text.substring(i, i + 1);
306 MissingGlyph glyph = font.getGlyph(unicode);
307
308 Shape path = glyph.getPath();
309 if (path != null)
310 {
311 path = xform.createTransformedShape(path);
312 textPath.append(path, false);
313 }
314
315 cursorX += glyph.getHorizAdvX();
316 }
317
318 strokeWidthScalar = 1f;
319 }
320 else if (obj instanceof Tspan)
321 {
322// Tspan tspan = (Tspan) obj;
323//
324// xform.setToIdentity();
325// xform.setToTranslation(cursorX, cursorY);
326// xform.scale(fontScale, fontScale);
327//// tspan.setCursorX(cursorX);
328//// tspan.setCursorY(cursorY);
329//
330// Shape tspanShape = tspan.getShape();
331// tspanShape = xform.createTransformedShape(tspanShape);
332// textPath.append(tspanShape, false);
333//// tspan.render(g);
334//// cursorX = tspan.getCursorX();
335//// cursorY = tspan.getCursorY();
336
337
338 Tspan tspan = (Tspan)obj;
339 Point2D cursor = new Point2D.Float(cursorX, cursorY);
340// tspan.setCursorX(cursorX);
341// tspan.setCursorY(cursorY);
342 tspan.appendToShape(textPath, cursor);
343// cursorX = tspan.getCursorX();
344// cursorY = tspan.getCursorY();
345 cursorX = (float)cursor.getX();
346 cursorY = (float)cursor.getY();
347
348 }
349
350 }
351
352 switch (textAnchor)
353 {
354 case TXAN_MIDDLE:
355 {
356 AffineTransform at = new AffineTransform();
357 at.translate(-textPath.getBounds().getWidth() / 2, 0);
358 textPath.transform(at);
359 break;
360 }
361 case TXAN_END:
362 {
363 AffineTransform at = new AffineTransform();
364 at.translate(-textPath.getBounds().getWidth(), 0);
365 textPath.transform(at);
366 break;
367 }
368 }
369 }
370
371// private void buildSysFont(java.awt.Font font) throws SVGException
372// {
373// GeneralPath textPath = new GeneralPath();
374// textShape = textPath;
375//
376// float cursorX = x, cursorY = y;
377//
378//// FontMetrics fm = g.getFontMetrics(font);
379// FontRenderContext frc = new FontRenderContext(null, true, true);
380//
381//// FontFace fontFace = font.getFontFace();
382// //int unitsPerEm = fontFace.getUnitsPerEm();
383//// int ascent = fm.getAscent();
384//// float fontScale = fontSize / (float)ascent;
385//
386//// AffineTransform oldXform = g.getTransform();
387// AffineTransform xform = new AffineTransform();
388//
389// for (Iterator it = content.iterator(); it.hasNext();)
390// {
391// Object obj = it.next();
392//
393// if (obj instanceof String)
394// {
395// String text = (String)obj;
396// text = text.trim();
397//
398// Shape textShape = font.createGlyphVector(frc, text).getOutline(cursorX, cursorY);
399// textPath.append(textShape, false);
400//// renderShape(g, textShape);
401//// g.drawString(text, cursorX, cursorY);
402//
403// Rectangle2D rect = font.getStringBounds(text, frc);
404// cursorX += (float) rect.getWidth();
405// } else if (obj instanceof Tspan)
406// {
407// /*
408// Tspan tspan = (Tspan)obj;
409//
410// xform.setToIdentity();
411// xform.setToTranslation(cursorX, cursorY);
412//
413// Shape tspanShape = tspan.getShape();
414// tspanShape = xform.createTransformedShape(tspanShape);
415// textArea.add(new Area(tspanShape));
416//
417// cursorX += tspanShape.getBounds2D().getWidth();
418// */
419//
420//
421// Tspan tspan = (Tspan)obj;
422// Point2D cursor = new Point2D.Float(cursorX, cursorY);
423//// tspan.setCursorX(cursorX);
424//// tspan.setCursorY(cursorY);
425// tspan.appendToShape(textPath, cursor);
426//// cursorX = tspan.getCursorX();
427//// cursorY = tspan.getCursorY();
428// cursorX = (float)cursor.getX();
429// cursorY = (float)cursor.getY();
430//
431// }
432// }
433//
434// switch (textAnchor)
435// {
436// case TXAN_MIDDLE:
437// {
438// AffineTransform at = new AffineTransform();
439// at.translate(-textPath.getBounds().getWidth() / 2, 0);
440// textPath.transform(at);
441// break;
442// }
443// case TXAN_END:
444// {
445// AffineTransform at = new AffineTransform();
446// at.translate(-Math.ceil(textPath.getBounds().getWidth()), 0);
447// textPath.transform(at);
448// break;
449// }
450// }
451// }
452
453 public void render(Graphics2D g) throws SVGException
454 {
455 beginLayer(g);
456 renderShape(g, textShape);
457 finishLayer(g);
458 }
459
460 public Shape getShape()
461 {
462 return shapeToParent(textShape);
463 }
464
465 public Rectangle2D getBoundingBox() throws SVGException
466 {
467 return boundsToParent(includeStrokeInBounds(textShape.getBounds2D()));
468 }
469
470 /**
471 * Updates all attributes in this diagram associated with a time event. Ie,
472 * all attributes with track information.
473 *
474 * @return - true if this node has changed state as a result of the time
475 * update
476 */
477 public boolean updateTime(double curTime) throws SVGException
478 {
479// if (trackManager.getNumTracks() == 0) return false;
480 boolean changeState = super.updateTime(curTime);
481
482 //Get current values for parameters
483 StyleAttribute sty = new StyleAttribute();
484 boolean shapeChange = false;
485
486 if (getPres(sty.setName("x")))
487 {
488 float newVal = sty.getFloatValueWithUnits();
489 if (newVal != x)
490 {
491 x = newVal;
492 shapeChange = true;
493 }
494 }
495
496 if (getPres(sty.setName("y")))
497 {
498 float newVal = sty.getFloatValueWithUnits();
499 if (newVal != y)
500 {
501 y = newVal;
502 shapeChange = true;
503 }
504 }
505
506 if (getStyle(sty.setName("textLength")))
507 {
508 textLength = sty.getFloatValueWithUnits();
509 }
510 else
511 {
512 textLength = -1;
513 }
514
515 if (getStyle(sty.setName("lengthAdjust")))
516 {
517 lengthAdjust = sty.getStringValue();
518 }
519 else
520 {
521 lengthAdjust = "spacing";
522 }
523
524 if (getPres(sty.setName("font-family")))
525 {
526 String newVal = sty.getStringValue();
527 if (!newVal.equals(fontFamily))
528 {
529 fontFamily = newVal;
530 shapeChange = true;
531 }
532 }
533
534 if (getPres(sty.setName("font-size")))
535 {
536 float newVal = sty.getFloatValueWithUnits();
537 if (newVal != fontSize)
538 {
539 fontSize = newVal;
540 shapeChange = true;
541 }
542 }
543
544
545 if (getStyle(sty.setName("font-style")))
546 {
547 String s = sty.getStringValue();
548 int newVal = fontStyle;
549 if ("normal".equals(s))
550 {
551 newVal = TXST_NORMAL;
552 } else if ("italic".equals(s))
553 {
554 newVal = TXST_ITALIC;
555 } else if ("oblique".equals(s))
556 {
557 newVal = TXST_OBLIQUE;
558 }
559 if (newVal != fontStyle)
560 {
561 fontStyle = newVal;
562 shapeChange = true;
563 }
564 }
565
566 if (getStyle(sty.setName("font-weight")))
567 {
568 String s = sty.getStringValue();
569 int newVal = fontWeight;
570 if ("normal".equals(s))
571 {
572 newVal = TXWE_NORMAL;
573 } else if ("bold".equals(s))
574 {
575 newVal = TXWE_BOLD;
576 }
577 if (newVal != fontWeight)
578 {
579 fontWeight = newVal;
580 shapeChange = true;
581 }
582 }
583
584 if (shapeChange)
585 {
586 build();
587// buildFont();
588// return true;
589 }
590
591 return changeState || shapeChange;
592 }
593}
Note: See TracBrowser for help on using the repository browser.