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

Last change on this file since 15912 was 14346, checked in by Don-vip, 6 years ago

fix #16852 - fix default font in svgSalamander

See https://github.com/blackears/svgSalamander/pull/30/commits/f3981f3c68700da647d48b5f2576ee90ad0df9ce

  • Property svn:eol-style set to native
File size: 17.0 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 = "SansSerif";
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(families[i]);
275 if (font != null)
276 {
277 break;
278 }
279 }
280
281 if (font == null)
282 {
283 //Check system fonts
284 font = FontSystem.createFont(fontFamily, fontStyle, fontWeight, (int)fontSize);
285 }
286
287 if (font == null)
288 {
289 font = FontSystem.createFont("Serif", fontStyle, fontWeight, fontStyle);
290 }
291
292 GeneralPath textPath = new GeneralPath();
293 textShape = textPath;
294
295 float cursorX = x, cursorY = y;
296
297 AffineTransform xform = new AffineTransform();
298
299 for (Serializable obj : content) {
300 if (obj instanceof String)
301 {
302 String text = (String) obj;
303 if (text != null)
304 {
305 text = text.trim();
306 }
307
308 for (int i = 0; i < text.length(); i++)
309 {
310 xform.setToIdentity();
311 xform.setToTranslation(cursorX, cursorY);
312
313 String unicode = text.substring(i, i + 1);
314 MissingGlyph glyph = font.getGlyph(unicode);
315
316 Shape path = glyph.getPath();
317 if (path != null)
318 {
319 path = xform.createTransformedShape(path);
320 textPath.append(path, false);
321 }
322
323 cursorX += glyph.getHorizAdvX();
324 }
325
326 strokeWidthScalar = 1f;
327 }
328 else if (obj instanceof Tspan)
329 {
330// Tspan tspan = (Tspan) obj;
331//
332// xform.setToIdentity();
333// xform.setToTranslation(cursorX, cursorY);
334// xform.scale(fontScale, fontScale);
335//// tspan.setCursorX(cursorX);
336//// tspan.setCursorY(cursorY);
337//
338// Shape tspanShape = tspan.getShape();
339// tspanShape = xform.createTransformedShape(tspanShape);
340// textPath.append(tspanShape, false);
341//// tspan.render(g);
342//// cursorX = tspan.getCursorX();
343//// cursorY = tspan.getCursorY();
344
345
346 Tspan tspan = (Tspan)obj;
347 Point2D cursor = new Point2D.Float(cursorX, cursorY);
348// tspan.setCursorX(cursorX);
349// tspan.setCursorY(cursorY);
350 tspan.appendToShape(textPath, cursor);
351// cursorX = tspan.getCursorX();
352// cursorY = tspan.getCursorY();
353 cursorX = (float)cursor.getX();
354 cursorY = (float)cursor.getY();
355
356 }
357
358 }
359
360 switch (textAnchor)
361 {
362 case TXAN_MIDDLE:
363 {
364 AffineTransform at = new AffineTransform();
365 at.translate(-textPath.getBounds().getWidth() / 2, 0);
366 textPath.transform(at);
367 break;
368 }
369 case TXAN_END:
370 {
371 AffineTransform at = new AffineTransform();
372 at.translate(-textPath.getBounds().getWidth(), 0);
373 textPath.transform(at);
374 break;
375 }
376 }
377 }
378
379// private void buildSysFont(java.awt.Font font) throws SVGException
380// {
381// GeneralPath textPath = new GeneralPath();
382// textShape = textPath;
383//
384// float cursorX = x, cursorY = y;
385//
386//// FontMetrics fm = g.getFontMetrics(font);
387// FontRenderContext frc = new FontRenderContext(null, true, true);
388//
389//// FontFace fontFace = font.getFontFace();
390// //int unitsPerEm = fontFace.getUnitsPerEm();
391//// int ascent = fm.getAscent();
392//// float fontScale = fontSize / (float)ascent;
393//
394//// AffineTransform oldXform = g.getTransform();
395// AffineTransform xform = new AffineTransform();
396//
397// for (Iterator it = content.iterator(); it.hasNext();)
398// {
399// Object obj = it.next();
400//
401// if (obj instanceof String)
402// {
403// String text = (String)obj;
404// text = text.trim();
405//
406// Shape textShape = font.createGlyphVector(frc, text).getOutline(cursorX, cursorY);
407// textPath.append(textShape, false);
408//// renderShape(g, textShape);
409//// g.drawString(text, cursorX, cursorY);
410//
411// Rectangle2D rect = font.getStringBounds(text, frc);
412// cursorX += (float) rect.getWidth();
413// } else if (obj instanceof Tspan)
414// {
415// /*
416// Tspan tspan = (Tspan)obj;
417//
418// xform.setToIdentity();
419// xform.setToTranslation(cursorX, cursorY);
420//
421// Shape tspanShape = tspan.getShape();
422// tspanShape = xform.createTransformedShape(tspanShape);
423// textArea.add(new Area(tspanShape));
424//
425// cursorX += tspanShape.getBounds2D().getWidth();
426// */
427//
428//
429// Tspan tspan = (Tspan)obj;
430// Point2D cursor = new Point2D.Float(cursorX, cursorY);
431//// tspan.setCursorX(cursorX);
432//// tspan.setCursorY(cursorY);
433// tspan.appendToShape(textPath, cursor);
434//// cursorX = tspan.getCursorX();
435//// cursorY = tspan.getCursorY();
436// cursorX = (float)cursor.getX();
437// cursorY = (float)cursor.getY();
438//
439// }
440// }
441//
442// switch (textAnchor)
443// {
444// case TXAN_MIDDLE:
445// {
446// AffineTransform at = new AffineTransform();
447// at.translate(-textPath.getBounds().getWidth() / 2, 0);
448// textPath.transform(at);
449// break;
450// }
451// case TXAN_END:
452// {
453// AffineTransform at = new AffineTransform();
454// at.translate(-Math.ceil(textPath.getBounds().getWidth()), 0);
455// textPath.transform(at);
456// break;
457// }
458// }
459// }
460
461 @Override
462 public void render(Graphics2D g) throws SVGException
463 {
464 beginLayer(g);
465 renderShape(g, textShape);
466 finishLayer(g);
467 }
468
469 @Override
470 public Shape getShape()
471 {
472 return shapeToParent(textShape);
473 }
474
475 @Override
476 public Rectangle2D getBoundingBox() throws SVGException
477 {
478 return boundsToParent(includeStrokeInBounds(textShape.getBounds2D()));
479 }
480
481 /**
482 * Updates all attributes in this diagram associated with a time event. Ie,
483 * all attributes with track information.
484 *
485 * @return - true if this node has changed state as a result of the time
486 * update
487 */
488 @Override
489 public boolean updateTime(double curTime) throws SVGException
490 {
491// if (trackManager.getNumTracks() == 0) return false;
492 boolean changeState = super.updateTime(curTime);
493
494 //Get current values for parameters
495 StyleAttribute sty = new StyleAttribute();
496 boolean shapeChange = false;
497
498 if (getPres(sty.setName("x")))
499 {
500 float newVal = sty.getFloatValueWithUnits();
501 if (newVal != x)
502 {
503 x = newVal;
504 shapeChange = true;
505 }
506 }
507
508 if (getPres(sty.setName("y")))
509 {
510 float newVal = sty.getFloatValueWithUnits();
511 if (newVal != y)
512 {
513 y = newVal;
514 shapeChange = true;
515 }
516 }
517
518 if (getStyle(sty.setName("textLength")))
519 {
520 textLength = sty.getFloatValueWithUnits();
521 }
522 else
523 {
524 textLength = -1;
525 }
526
527 if (getStyle(sty.setName("lengthAdjust")))
528 {
529 lengthAdjust = sty.getStringValue();
530 }
531 else
532 {
533 lengthAdjust = "spacing";
534 }
535
536 if (getPres(sty.setName("font-family")))
537 {
538 String newVal = sty.getStringValue();
539 if (!newVal.equals(fontFamily))
540 {
541 fontFamily = newVal;
542 shapeChange = true;
543 }
544 }
545
546 if (getPres(sty.setName("font-size")))
547 {
548 float newVal = sty.getFloatValueWithUnits();
549 if (newVal != fontSize)
550 {
551 fontSize = newVal;
552 shapeChange = true;
553 }
554 }
555
556
557 if (getStyle(sty.setName("font-style")))
558 {
559 String s = sty.getStringValue();
560 int newVal = fontStyle;
561 if ("normal".equals(s))
562 {
563 newVal = TXST_NORMAL;
564 } else if ("italic".equals(s))
565 {
566 newVal = TXST_ITALIC;
567 } else if ("oblique".equals(s))
568 {
569 newVal = TXST_OBLIQUE;
570 }
571 if (newVal != fontStyle)
572 {
573 fontStyle = newVal;
574 shapeChange = true;
575 }
576 }
577
578 if (getStyle(sty.setName("font-weight")))
579 {
580 String s = sty.getStringValue();
581 int newVal = fontWeight;
582 if ("normal".equals(s))
583 {
584 newVal = TXWE_NORMAL;
585 } else if ("bold".equals(s))
586 {
587 newVal = TXWE_BOLD;
588 }
589 if (newVal != fontWeight)
590 {
591 fontWeight = newVal;
592 shapeChange = true;
593 }
594 }
595
596 if (shapeChange)
597 {
598 build();
599// buildFont();
600// return true;
601 }
602
603 return changeState || shapeChange;
604 }
605}
Note: See TracBrowser for help on using the repository browser.