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

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

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

File size: 13.2 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.font.GlyphMetrics;
43import java.awt.font.GlyphVector;
44import java.awt.geom.AffineTransform;
45import java.awt.geom.GeneralPath;
[7676]46import java.awt.geom.Point2D;
[6002]47import java.awt.geom.Rectangle2D;
[4256]48
49/**
50 * @author Mark McKay
51 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
52 */
[6002]53public class Tspan extends ShapeElement
54{
[4256]55
[6002]56 public static final String TAG_NAME = "tspan";
[4256]57 float[] x = null;
58 float[] y = null;
59 float[] dx = null;
60 float[] dy = null;
61 float[] rotate = null;
62 private String text = "";
[7676]63// float cursorX;
64// float cursorY;
[4256]65
66// Shape tspanShape;
[6002]67 /**
68 * Creates a new instance of Stop
69 */
70 public Tspan()
71 {
72 }
[4256]73
[6002]74 public String getTagName()
75 {
76 return TAG_NAME;
[4256]77 }
78
[7676]79// public float getCursorX()
80// {
81// return cursorX;
82// }
83//
84// public float getCursorY()
85// {
86// return cursorY;
87// }
88//
89// public void setCursorX(float cursorX)
90// {
91// this.cursorX = cursorX;
92// }
93//
94// public void setCursorY(float cursorY)
95// {
96// this.cursorY = cursorY;
97// }
[6002]98 /*
99 public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent)
100 {
101 //Load style string
102 super.loaderStartElement(helper, attrs, parent);
[4256]103
[6002]104 String x = attrs.getValue("x");
105 String y = attrs.getValue("y");
106 String dx = attrs.getValue("dx");
107 String dy = attrs.getValue("dy");
108 String rotate = attrs.getValue("rotate");
[4256]109
[6002]110 if (x != null) this.x = XMLParseUtil.parseFloatList(x);
111 if (y != null) this.y = XMLParseUtil.parseFloatList(y);
112 if (dx != null) this.dx = XMLParseUtil.parseFloatList(dx);
113 if (dy != null) this.dy = XMLParseUtil.parseFloatList(dy);
114 if (rotate != null)
115 {
116 this.rotate = XMLParseUtil.parseFloatList(rotate);
117 for (int i = 0; i < this.rotate.length; i++)
118 this.rotate[i] = (float)Math.toRadians(this.rotate[i]);
119 }
120 }
121 */
[4256]122
123 /**
124 * Called during load process to add text scanned within a tag
125 */
126 public void loaderAddText(SVGLoaderHelper helper, String text)
127 {
128 this.text += text;
129 }
130
131 protected void build() throws SVGException
132 {
133 super.build();
[6002]134
[4256]135 StyleAttribute sty = new StyleAttribute();
136
[6002]137 if (getPres(sty.setName("x")))
138 {
139 x = sty.getFloatList();
140 }
[4256]141
[6002]142 if (getPres(sty.setName("y")))
143 {
144 y = sty.getFloatList();
145 }
[4256]146
[6002]147 if (getPres(sty.setName("dx")))
148 {
149 dx = sty.getFloatList();
150 }
[4256]151
[6002]152 if (getPres(sty.setName("dy")))
153 {
154 dy = sty.getFloatList();
155 }
156
[4256]157 if (getPres(sty.setName("rotate")))
158 {
159 rotate = sty.getFloatList();
160 for (int i = 0; i < this.rotate.length; i++)
161 {
[6002]162 rotate[i] = (float) Math.toRadians(this.rotate[i]);
[4256]163 }
[6002]164
[4256]165 }
166 }
[6002]167
[7676]168 public void appendToShape(GeneralPath addShape, Point2D cursor) throws SVGException
[4256]169 {
[7676]170// if (x != null)
171// {
172// cursorX = x[0];
173// } else if (dx != null)
174// {
175// cursorX += dx[0];
176// }
177//
178// if (y != null)
179// {
180// cursorY = y[0];
181// } else if (dy != null)
182// {
183// cursorY += dy[0];
184// }
[6002]185
[4256]186 StyleAttribute sty = new StyleAttribute();
[6002]187
[4256]188 String fontFamily = null;
189 if (getStyle(sty.setName("font-family")))
190 {
191 fontFamily = sty.getStringValue();
192 }
193
194
195 float fontSize = 12f;
196 if (getStyle(sty.setName("font-size")))
197 {
198 fontSize = sty.getFloatValueWithUnits();
199 }
200
[6002]201 float letterSpacing = 0;
202 if (getStyle(sty.setName("letter-spacing")))
203 {
204 letterSpacing = sty.getFloatValueWithUnits();
205 }
206
207
[4256]208 //Get font
209 Font font = diagram.getUniverse().getFont(fontFamily);
210 if (font == null)
211 {
[7676]212 addShapeSysFont(addShape, font, fontFamily, fontSize, letterSpacing, cursor);
[4256]213 return;
214 }
215
216 FontFace fontFace = font.getFontFace();
217 int ascent = fontFace.getAscent();
[6002]218 float fontScale = fontSize / (float) ascent;
[4256]219
220 AffineTransform xform = new AffineTransform();
221
222 strokeWidthScalar = 1f / fontScale;
223
[7676]224 float cursorX = (float)cursor.getX();
225 float cursorY = (float)cursor.getY();
226
227// int i = 0;
[4256]228
[7676]229 String drawText = this.text;
230 drawText = drawText.trim();
231 for (int i = 0; i < drawText.length(); i++)
[4256]232 {
[7676]233 if (x != null && i < x.length)
234 {
235 cursorX = x[i];
236 } else if (dx != null && i < dx.length)
237 {
238 cursorX += dx[i];
239 }
240
241 if (y != null && i < y.length)
242 {
243 cursorY = y[i];
244 } else if (dy != null && i < dy.length)
245 {
246 cursorY += dy[i];
247 }
248 // i++;
249
[4256]250 xform.setToIdentity();
251 xform.setToTranslation(cursorX, cursorY);
252 xform.scale(fontScale, fontScale);
[6002]253 if (rotate != null)
254 {
[7676]255 xform.rotate(rotate[i]);
[6002]256 }
[4256]257
[7676]258 String unicode = drawText.substring(i, i + 1);
[4256]259 MissingGlyph glyph = font.getGlyph(unicode);
260
261 Shape path = glyph.getPath();
262 if (path != null)
263 {
264 path = xform.createTransformedShape(path);
265 addShape.append(path, false);
266 }
267
[6002]268 cursorX += fontScale * glyph.getHorizAdvX() + letterSpacing;
[4256]269 }
270
[7676]271 //Save final draw point so calling method knows where to begin next
272 // text draw
273 cursor.setLocation(cursorX, cursorY);
[4256]274 strokeWidthScalar = 1f;
275 }
276
[6002]277 private void addShapeSysFont(GeneralPath addShape, Font font,
[7676]278 String fontFamily, float fontSize, float letterSpacing, Point2D cursor)
[4256]279 {
[7676]280
[6002]281 java.awt.Font sysFont = new java.awt.Font(fontFamily, java.awt.Font.PLAIN, (int) fontSize);
[4256]282
283 FontRenderContext frc = new FontRenderContext(null, true, true);
[7676]284 String renderText = this.text.trim();
[4256]285
286 AffineTransform xform = new AffineTransform();
287
[7676]288 float cursorX = (float)cursor.getX();
289 float cursorY = (float)cursor.getY();
290// int i = 0;
291 for (int i = 0; i < renderText.length(); i++)
[4256]292 {
[7676]293 if (x != null && i < x.length)
294 {
295 cursorX = x[i];
296 } else if (dx != null && i < dx.length)
297 {
298 cursorX += dx[i];
299 }
300
301 if (y != null && i < y.length)
302 {
303 cursorY = y[i];
304 } else if (dy != null && i < dy.length)
305 {
306 cursorY += dy[i];
307 }
308// i++;
309
[4256]310 xform.setToIdentity();
[7676]311 xform.setToTranslation(cursorX, cursorY);
[6002]312 if (rotate != null)
313 {
314 xform.rotate(rotate[Math.min(i, rotate.length - 1)]);
315 }
[4256]316
[7676]317// String unicode = renderText.substring(i, i + 1);
318 GlyphVector textVector = sysFont.createGlyphVector(frc, renderText.substring(i, i + 1));
319 Shape glyphOutline = textVector.getGlyphOutline(0);
320 GlyphMetrics glyphMetrics = textVector.getGlyphMetrics(0);
[4256]321
322 glyphOutline = xform.createTransformedShape(glyphOutline);
323 addShape.append(glyphOutline, false);
324
[7676]325
326// cursorX += fontScale * glyph.getHorizAdvX() + letterSpacing;
327 cursorX += glyphMetrics.getAdvance() + letterSpacing;
[4256]328 }
[7676]329
330 cursor.setLocation(cursorX, cursorY);
[4256]331 }
332
333 public void render(Graphics2D g) throws SVGException
334 {
[7676]335 float cursorX = 0;
336 float cursorY = 0;
337
[4256]338 if (x != null)
339 {
340 cursorX = x[0];
341 cursorY = y[0];
[6002]342 } else if (dx != null)
[4256]343 {
344 cursorX += dx[0];
345 cursorY += dy[0];
346 }
347
348 StyleAttribute sty = new StyleAttribute();
[6002]349
[4256]350 String fontFamily = null;
351 if (getPres(sty.setName("font-family")))
352 {
353 fontFamily = sty.getStringValue();
354 }
355
356
357 float fontSize = 12f;
358 if (getPres(sty.setName("font-size")))
359 {
360 fontSize = sty.getFloatValueWithUnits();
361 }
362
363 //Get font
364 Font font = diagram.getUniverse().getFont(fontFamily);
365 if (font == null)
366 {
367 System.err.println("Could not load font");
[6002]368 java.awt.Font sysFont = new java.awt.Font(fontFamily, java.awt.Font.PLAIN, (int) fontSize);
[4256]369 renderSysFont(g, sysFont);
370 return;
371 }
372
373
374 FontFace fontFace = font.getFontFace();
375 int ascent = fontFace.getAscent();
[6002]376 float fontScale = fontSize / (float) ascent;
[4256]377
378 AffineTransform oldXform = g.getTransform();
379 AffineTransform xform = new AffineTransform();
380
381 strokeWidthScalar = 1f / fontScale;
382
383 int posPtr = 1;
384
385 for (int i = 0; i < text.length(); i++)
386 {
387 xform.setToTranslation(cursorX, cursorY);
388 xform.scale(fontScale, fontScale);
389 g.transform(xform);
390
391 String unicode = text.substring(i, i + 1);
392 MissingGlyph glyph = font.getGlyph(unicode);
393
394 Shape path = glyph.getPath();
395 if (path != null)
396 {
397 renderShape(g, path);
[6002]398 } else
399 {
400 glyph.render(g);
[4256]401 }
402
403 if (x != null && posPtr < x.length)
404 {
405 cursorX = x[posPtr];
406 cursorY = y[posPtr++];
[6002]407 } else if (dx != null && posPtr < dx.length)
[4256]408 {
409 cursorX += dx[posPtr];
410 cursorY += dy[posPtr++];
411 }
412
413 cursorX += fontScale * glyph.getHorizAdvX();
414
415 g.setTransform(oldXform);
416 }
417
418 strokeWidthScalar = 1f;
419 }
420
421 protected void renderSysFont(Graphics2D g, java.awt.Font font) throws SVGException
422 {
[7676]423 float cursorX = 0;
424 float cursorY = 0;
425
[4256]426 int posPtr = 1;
427 FontRenderContext frc = g.getFontRenderContext();
428
429 Shape textShape = font.createGlyphVector(frc, text).getOutline(cursorX, cursorY);
430 renderShape(g, textShape);
431 Rectangle2D rect = font.getStringBounds(text, frc);
[6002]432 cursorX += (float) rect.getWidth();
[4256]433 }
434
435 public Shape getShape()
436 {
437 return null;
438 //return shapeToParent(tspanShape);
439 }
440
441 public Rectangle2D getBoundingBox()
442 {
443 return null;
444 //return boundsToParent(tspanShape.getBounds2D());
445 }
446
447 /**
[6002]448 * Updates all attributes in this diagram associated with a time event. Ie,
449 * all attributes with track information.
450 *
[4256]451 * @return - true if this node has changed state as a result of the time
452 * update
453 */
454 public boolean updateTime(double curTime) throws SVGException
455 {
456 //Tspan does not change
457 return false;
458 }
459
460 public String getText()
461 {
462 return text;
463 }
464
465 public void setText(String text)
466 {
467 this.text = text;
468 }
469}
Note: See TracBrowser for help on using the repository browser.