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

Last change on this file since 10741 was 8084, checked in by bastiK, 10 years ago

add svn:eol-style=native for svgsalamander

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