001 /*
002 * Stop.java
003 *
004 *
005 * The Salamander Project - 2D and 3D graphics libraries in Java
006 * Copyright (C) 2004 Mark McKay
007 *
008 * This library is free software; you can redistribute it and/or
009 * modify it under the terms of the GNU Lesser General Public
010 * License as published by the Free Software Foundation; either
011 * version 2.1 of the License, or (at your option) any later version.
012 *
013 * This library is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016 * Lesser General Public License for more details.
017 *
018 * You should have received a copy of the GNU Lesser General Public
019 * License along with this library; if not, write to the Free Software
020 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021 *
022 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
023 * projects can be found at http://www.kitfox.com
024 *
025 * Created on January 26, 2004, 1:56 AM
026 */
027
028 package com.kitfox.svg;
029
030 import com.kitfox.svg.xml.StyleAttribute;
031 import java.awt.*;
032 import java.awt.font.*;
033 import java.awt.geom.*;
034 import java.util.*;
035
036 import com.kitfox.svg.xml.*;
037 import org.xml.sax.*;
038
039 //import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
040
041 /**
042 * @author Mark McKay
043 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
044 */
045 public class Tspan extends ShapeElement {
046
047 float[] x = null;
048 float[] y = null;
049 float[] dx = null;
050 float[] dy = null;
051 float[] rotate = null;
052
053 private String text = "";
054
055 float cursorX;
056 float cursorY;
057
058 // Shape tspanShape;
059
060 /** Creates a new instance of Stop */
061 public Tspan() {
062 }
063
064 public float getCursorX() { return cursorX; }
065 public float getCursorY() { return cursorY; }
066
067 public void setCursorX(float cursorX)
068 {
069 this.cursorX = cursorX;
070 }
071
072 public void setCursorY(float cursorY)
073 {
074 this.cursorY = cursorY;
075 }
076 /*
077 public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent)
078 {
079 //Load style string
080 super.loaderStartElement(helper, attrs, parent);
081
082 String x = attrs.getValue("x");
083 String y = attrs.getValue("y");
084 String dx = attrs.getValue("dx");
085 String dy = attrs.getValue("dy");
086 String rotate = attrs.getValue("rotate");
087
088 if (x != null) this.x = XMLParseUtil.parseFloatList(x);
089 if (y != null) this.y = XMLParseUtil.parseFloatList(y);
090 if (dx != null) this.dx = XMLParseUtil.parseFloatList(dx);
091 if (dy != null) this.dy = XMLParseUtil.parseFloatList(dy);
092 if (rotate != null)
093 {
094 this.rotate = XMLParseUtil.parseFloatList(rotate);
095 for (int i = 0; i < this.rotate.length; i++)
096 this.rotate[i] = (float)Math.toRadians(this.rotate[i]);
097 }
098 }
099 */
100
101 /**
102 * Called during load process to add text scanned within a tag
103 */
104 public void loaderAddText(SVGLoaderHelper helper, String text)
105 {
106 this.text += text;
107 }
108
109
110 protected void build() throws SVGException
111 {
112 super.build();
113
114 StyleAttribute sty = new StyleAttribute();
115
116 if (getPres(sty.setName("x"))) x = sty.getFloatList();
117
118 if (getPres(sty.setName("y"))) y = sty.getFloatList();
119
120 if (getPres(sty.setName("dx"))) dx = sty.getFloatList();
121
122 if (getPres(sty.setName("dy"))) dy = sty.getFloatList();
123
124 if (getPres(sty.setName("rotate")))
125 {
126 rotate = sty.getFloatList();
127 for (int i = 0; i < this.rotate.length; i++)
128 {
129 rotate[i] = (float)Math.toRadians(this.rotate[i]);
130 }
131
132 }
133 }
134
135 public void addShape(GeneralPath addShape) throws SVGException
136 {
137 if (x != null)
138 {
139 cursorX = x[0];
140 cursorY = y[0];
141 }
142 else if (dx != null)
143 {
144 cursorX += dx[0];
145 cursorY += dy[0];
146 }
147
148 StyleAttribute sty = new StyleAttribute();
149
150 String fontFamily = null;
151 if (getStyle(sty.setName("font-family")))
152 {
153 fontFamily = sty.getStringValue();
154 }
155
156
157 float fontSize = 12f;
158 if (getStyle(sty.setName("font-size")))
159 {
160 fontSize = sty.getFloatValueWithUnits();
161 }
162
163 //Get font
164 Font font = diagram.getUniverse().getFont(fontFamily);
165 if (font == null)
166 {
167 addShapeSysFont(addShape, font, fontFamily, fontSize);
168 return;
169 }
170
171 FontFace fontFace = font.getFontFace();
172 int ascent = fontFace.getAscent();
173 float fontScale = fontSize / (float)ascent;
174
175 AffineTransform xform = new AffineTransform();
176
177 strokeWidthScalar = 1f / fontScale;
178
179 int posPtr = 1;
180
181 for (int i = 0; i < text.length(); i++)
182 {
183 xform.setToIdentity();
184 xform.setToTranslation(cursorX, cursorY);
185 xform.scale(fontScale, fontScale);
186 if (rotate != null) xform.rotate(rotate[posPtr]);
187
188 String unicode = text.substring(i, i + 1);
189 MissingGlyph glyph = font.getGlyph(unicode);
190
191 Shape path = glyph.getPath();
192 if (path != null)
193 {
194 path = xform.createTransformedShape(path);
195 addShape.append(path, false);
196 }
197
198 if (x != null && posPtr < x.length)
199 {
200 cursorX = x[posPtr];
201 cursorY = y[posPtr++];
202 }
203 else if (dx != null && posPtr < dx.length)
204 {
205 cursorX += dx[posPtr];
206 cursorY += dy[posPtr++];
207 }
208
209 cursorX += fontScale * glyph.getHorizAdvX();
210 }
211
212 strokeWidthScalar = 1f;
213 }
214
215 private void addShapeSysFont(GeneralPath addShape, Font font, String fontFamily, float fontSize)
216 {
217 java.awt.Font sysFont = new java.awt.Font(fontFamily, java.awt.Font.PLAIN, (int)fontSize);
218
219 FontRenderContext frc = new FontRenderContext(null, true, true);
220 GlyphVector textVector = sysFont.createGlyphVector(frc, text);
221
222 AffineTransform xform = new AffineTransform();
223
224 int posPtr = 1;
225 for (int i = 0; i < text.length(); i++)
226 {
227 xform.setToIdentity();
228 xform.setToTranslation(cursorX, cursorY);
229 if (rotate != null) xform.rotate(rotate[Math.min(i, rotate.length - 1)]);
230
231 String unicode = text.substring(i, i + 1);
232 Shape glyphOutline = textVector.getGlyphOutline(i);
233 GlyphMetrics glyphMetrics = textVector.getGlyphMetrics(i);
234
235 glyphOutline = xform.createTransformedShape(glyphOutline);
236 addShape.append(glyphOutline, false);
237
238 if (x != null && posPtr < x.length)
239 {
240 cursorX = x[posPtr];
241 cursorY = y[posPtr++];
242 }
243 else if (dx != null && posPtr < dx.length)
244 {
245 cursorX += dx[posPtr];
246 cursorY += dy[posPtr++];
247 }
248 }
249 }
250
251 public void render(Graphics2D g) throws SVGException
252 {
253 if (x != null)
254 {
255 cursorX = x[0];
256 cursorY = y[0];
257 }
258 else if (dx != null)
259 {
260 cursorX += dx[0];
261 cursorY += dy[0];
262 }
263
264 StyleAttribute sty = new StyleAttribute();
265
266 String fontFamily = null;
267 if (getPres(sty.setName("font-family")))
268 {
269 fontFamily = sty.getStringValue();
270 }
271
272
273 float fontSize = 12f;
274 if (getPres(sty.setName("font-size")))
275 {
276 fontSize = sty.getFloatValueWithUnits();
277 }
278
279 //Get font
280 Font font = diagram.getUniverse().getFont(fontFamily);
281 if (font == null)
282 {
283 System.err.println("Could not load font");
284 java.awt.Font sysFont = new java.awt.Font(fontFamily, java.awt.Font.PLAIN, (int)fontSize);
285 renderSysFont(g, sysFont);
286 return;
287 }
288
289
290 FontFace fontFace = font.getFontFace();
291 int ascent = fontFace.getAscent();
292 float fontScale = fontSize / (float)ascent;
293
294 AffineTransform oldXform = g.getTransform();
295 AffineTransform xform = new AffineTransform();
296
297 strokeWidthScalar = 1f / fontScale;
298
299 int posPtr = 1;
300
301 for (int i = 0; i < text.length(); i++)
302 {
303 xform.setToTranslation(cursorX, cursorY);
304 xform.scale(fontScale, fontScale);
305 g.transform(xform);
306
307 String unicode = text.substring(i, i + 1);
308 MissingGlyph glyph = font.getGlyph(unicode);
309
310 Shape path = glyph.getPath();
311 if (path != null)
312 {
313 renderShape(g, path);
314 }
315 else glyph.render(g);
316
317 if (x != null && posPtr < x.length)
318 {
319 cursorX = x[posPtr];
320 cursorY = y[posPtr++];
321 }
322 else if (dx != null && posPtr < dx.length)
323 {
324 cursorX += dx[posPtr];
325 cursorY += dy[posPtr++];
326 }
327
328 cursorX += fontScale * glyph.getHorizAdvX();
329
330 g.setTransform(oldXform);
331 }
332
333 strokeWidthScalar = 1f;
334 }
335
336 protected void renderSysFont(Graphics2D g, java.awt.Font font) throws SVGException
337 {
338 int posPtr = 1;
339 FontRenderContext frc = g.getFontRenderContext();
340
341 Shape textShape = font.createGlyphVector(frc, text).getOutline(cursorX, cursorY);
342 renderShape(g, textShape);
343 Rectangle2D rect = font.getStringBounds(text, frc);
344 cursorX += (float)rect.getWidth();
345 }
346
347 public Shape getShape()
348 {
349 return null;
350 //return shapeToParent(tspanShape);
351 }
352
353 public Rectangle2D getBoundingBox()
354 {
355 return null;
356 //return boundsToParent(tspanShape.getBounds2D());
357 }
358
359 /**
360 * Updates all attributes in this diagram associated with a time event.
361 * Ie, all attributes with track information.
362 * @return - true if this node has changed state as a result of the time
363 * update
364 */
365 public boolean updateTime(double curTime) throws SVGException
366 {
367 //Tspan does not change
368 return false;
369 }
370
371 public String getText()
372 {
373 return text;
374 }
375
376 public void setText(String text)
377 {
378 this.text = text;
379 }
380 }