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

Last change on this file since 14361 was 14361, checked in by Don-vip, 4 months 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.