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

Last change on this file since 7676 was 7676, checked in by stoecker, 10 years ago

update SVG code to current SVN (fix line endings), see #10479

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