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

Last change on this file since 6168 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
RevLine 
[4256]1/*
[6002]2 * SVG Salamander
3 * Copyright (c) 2004, Mark McKay
4 * All rights reserved.
[4256]5 *
[6002]6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
[4256]9 *
[6002]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.
[4256]17 *
[6002]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
[4256]33 *
34 * Created on January 26, 2004, 1:56 AM
35 */
36package com.kitfox.svg;
37
38import com.kitfox.svg.xml.StyleAttribute;
[6002]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;
[4256]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{
[6002]57 public static final String TAG_NAME = "text";
[4256]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;
[6002]89
90 /**
91 * Creates a new instance of Stop
92 */
[4256]93 public Text()
94 {
95 }
[6002]96
97 public String getTagName()
98 {
99 return TAG_NAME;
100 }
101
[4256]102 public void appendText(String text)
103 {
104 content.addLast(text);
105 }
[6002]106
[4256]107 public void appendTspan(Tspan tspan) throws SVGElementException
108 {
109 super.loaderAddChild(null, tspan);
110 content.addLast(tspan);
111 }
[6002]112
[4256]113 /**
114 * Discard cached information
115 */
116 public void rebuild() throws SVGException
117 {
118 build();
119 }
[6002]120
[4256]121 public java.util.List getContent()
122 {
123 return content;
124 }
[6002]125
[4256]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);
[6002]133
[4256]134 content.addLast(child);
135 }
[6002]136
[4256]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);
[6002]143 if (!matchWs.matches())
144 {
145 content.addLast(text);
146 }
[4256]147 }
[6002]148
[4256]149 public void build() throws SVGException
150 {
151 super.build();
[6002]152
[4256]153 StyleAttribute sty = new StyleAttribute();
[6002]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
[4256]181 if (getStyle(sty.setName("font-style")))
182 {
183 String s = sty.getStringValue();
184 if ("normal".equals(s))
185 {
186 fontStyle = TXST_NORMAL;
[6002]187 } else if ("italic".equals(s))
[4256]188 {
189 fontStyle = TXST_ITALIC;
[6002]190 } else if ("oblique".equals(s))
[4256]191 {
192 fontStyle = TXST_OBLIQUE;
193 }
[6002]194 } else
195 {
196 fontStyle = TXST_NORMAL;
[4256]197 }
[6002]198
[4256]199 if (getStyle(sty.setName("font-weight")))
200 {
201 String s = sty.getStringValue();
202 if ("normal".equals(s))
203 {
204 fontWeight = TXWE_NORMAL;
[6002]205 } else if ("bold".equals(s))
[4256]206 {
207 fontWeight = TXWE_BOLD;
208 }
[6002]209 } else
210 {
211 fontWeight = TXWE_BOLD;
[4256]212 }
[6002]213
[4256]214 if (getStyle(sty.setName("text-anchor")))
215 {
216 String s = sty.getStringValue();
[6002]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;
[4256]230 }
[6002]231
[4256]232 //text anchor
233 //text-decoration
234 //text-rendering
[6002]235
[4256]236 buildFont();
237 }
[6002]238
[4256]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 }
[6002]263
[4256]264 //Get font
265 Font font = diagram.getUniverse().getFont(fontFamily);
266 if (font == null)
267 {
268// System.err.println("Could not load font");
[6002]269
270 java.awt.Font sysFont = new java.awt.Font(fontFamily, style | weight, (int) fontSize);
[4256]271 buildSysFont(sysFont);
272 return;
273 }
[6002]274
[4256]275// font = new java.awt.Font(font.getFamily(), style | weight, font.getSize());
[6002]276
[4256]277// Area textArea = new Area();
278 GeneralPath textPath = new GeneralPath();
279 textShape = textPath;
[6002]280
[4256]281 float cursorX = x, cursorY = y;
[6002]282
[4256]283 FontFace fontFace = font.getFontFace();
284 //int unitsPerEm = fontFace.getUnitsPerEm();
285 int ascent = fontFace.getAscent();
[6002]286 float fontScale = fontSize / (float) ascent;
287
[4256]288// AffineTransform oldXform = g.getTransform();
289 AffineTransform xform = new AffineTransform();
[6002]290
[4256]291 for (Iterator it = content.iterator(); it.hasNext();)
292 {
293 Object obj = it.next();
[6002]294
[4256]295 if (obj instanceof String)
296 {
[6002]297 String text = (String) obj;
298 if (text != null)
299 {
300 text = text.trim();
301 }
302
[4256]303 strokeWidthScalar = 1f / fontScale;
[6002]304
[4256]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);
[6002]311
[4256]312 String unicode = text.substring(i, i + 1);
313 MissingGlyph glyph = font.getGlyph(unicode);
[6002]314
[4256]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);
[6002]322
[4256]323 cursorX += fontScale * glyph.getHorizAdvX();
[6002]324
[4256]325// g.setTransform(oldXform);
326 }
[6002]327
[4256]328 strokeWidthScalar = 1f;
[6002]329 } else if (obj instanceof Tspan)
[4256]330 {
[6002]331 Tspan tspan = (Tspan) obj;
332
[4256]333 xform.setToIdentity();
334 xform.setToTranslation(cursorX, cursorY);
335 xform.scale(fontScale, fontScale);
336// tspan.setCursorX(cursorX);
337// tspan.setCursorY(cursorY);
[6002]338
[4256]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 }
[6002]346
[4256]347 }
[6002]348
[4256]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 }
[6002]367
[4256]368 private void buildSysFont(java.awt.Font font) throws SVGException
369 {
370 GeneralPath textPath = new GeneralPath();
371 textShape = textPath;
[6002]372
[4256]373 float cursorX = x, cursorY = y;
[6002]374
[4256]375// FontMetrics fm = g.getFontMetrics(font);
376 FontRenderContext frc = new FontRenderContext(null, true, true);
[6002]377
[4256]378// FontFace fontFace = font.getFontFace();
379 //int unitsPerEm = fontFace.getUnitsPerEm();
380// int ascent = fm.getAscent();
381// float fontScale = fontSize / (float)ascent;
[6002]382
[4256]383// AffineTransform oldXform = g.getTransform();
384 AffineTransform xform = new AffineTransform();
[6002]385
[4256]386 for (Iterator it = content.iterator(); it.hasNext();)
387 {
388 Object obj = it.next();
[6002]389
[4256]390 if (obj instanceof String)
391 {
[6002]392 String text = (String) obj;
393
[4256]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);
[6002]398
[4256]399 Rectangle2D rect = font.getStringBounds(text, frc);
[6002]400 cursorX += (float) rect.getWidth();
401 } else if (obj instanceof Tspan)
[4256]402 {
403 /*
[6002]404 Tspan tspan = (Tspan)obj;
[4256]405
[6002]406 xform.setToIdentity();
407 xform.setToTranslation(cursorX, cursorY);
[4256]408
[6002]409 Shape tspanShape = tspan.getShape();
410 tspanShape = xform.createTransformedShape(tspanShape);
411 textArea.add(new Area(tspanShape));
[4256]412
[6002]413 cursorX += tspanShape.getBounds2D().getWidth();
[4256]414 */
[6002]415
416
417 Tspan tspan = (Tspan) obj;
[4256]418 tspan.setCursorX(cursorX);
419 tspan.setCursorY(cursorY);
420 tspan.addShape(textPath);
421 cursorX = tspan.getCursorX();
422 cursorY = tspan.getCursorY();
[6002]423
[4256]424 }
425 }
[6002]426
[4256]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 }
[6002]445
[4256]446 public void render(Graphics2D g) throws SVGException
447 {
448 beginLayer(g);
449 renderShape(g, textShape);
450 finishLayer(g);
451 }
[6002]452
[4256]453 public Shape getShape()
454 {
455 return shapeToParent(textShape);
456 }
[6002]457
[4256]458 public Rectangle2D getBoundingBox() throws SVGException
459 {
460 return boundsToParent(includeStrokeInBounds(textShape.getBounds2D()));
461 }
[6002]462
[4256]463 /**
[6002]464 * Updates all attributes in this diagram associated with a time event. Ie,
465 * all attributes with track information.
466 *
[4256]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);
[6002]474
[4256]475 //Get current values for parameters
476 StyleAttribute sty = new StyleAttribute();
477 boolean shapeChange = false;
[6002]478
[4256]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 }
[6002]488
[4256]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 }
[6002]498
[4256]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 }
[6002]508
[4256]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 }
[6002]518
519
[4256]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;
[6002]527 } else if ("italic".equals(s))
[4256]528 {
529 newVal = TXST_ITALIC;
[6002]530 } else if ("oblique".equals(s))
[4256]531 {
532 newVal = TXST_OBLIQUE;
533 }
534 if (newVal != fontStyle)
535 {
536 fontStyle = newVal;
537 shapeChange = true;
538 }
539 }
[6002]540
[4256]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;
[6002]548 } else if ("bold".equals(s))
[4256]549 {
550 newVal = TXWE_BOLD;
551 }
552 if (newVal != fontWeight)
553 {
554 fontWeight = newVal;
555 shapeChange = true;
556 }
557 }
[6002]558
[4256]559 if (shapeChange)
560 {
561 build();
562// buildFont();
563// return true;
564 }
[6002]565
[4256]566 return changeState || shapeChange;
567 }
568}
Note: See TracBrowser for help on using the repository browser.