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

Last change on this file since 14082 was 11525, checked in by Don-vip, 7 years ago

see #14319 - update to latest version of svgSalamander (2017-01-07, patched)

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