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

Last change on this file since 14361 was 14361, checked in by Don-vip, 5 years ago

see #14319, see #16838 - fix another NPE / regression from svgSalamander 1.1.2 (causing unit test failure in DXF plugin)

see https://github.com/blackears/svgSalamander/pull/34

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