001/*
002 * SVG Salamander
003 * Copyright (c) 2004, Mark McKay
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or 
007 * without modification, are permitted provided that the following
008 * conditions are met:
009 *
010 *   - Redistributions of source code must retain the above 
011 *     copyright notice, this list of conditions and the following
012 *     disclaimer.
013 *   - Redistributions in binary form must reproduce the above
014 *     copyright notice, this list of conditions and the following
015 *     disclaimer in the documentation and/or other materials 
016 *     provided with the distribution.
017 *
018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
019 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
020 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
021 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
022 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
023 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
025 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
026 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
027 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
029 * OF THE POSSIBILITY OF SUCH DAMAGE. 
030 * 
031 * Mark McKay can be contacted at mark@kitfox.com.  Salamander and other
032 * projects can be found at http://www.kitfox.com
033 *
034 * Created on February 20, 2004, 10:00 PM
035 */
036package com.kitfox.svg;
037
038import com.kitfox.svg.pathcmd.BuildHistory;
039import com.kitfox.svg.pathcmd.PathCommand;
040import com.kitfox.svg.xml.StyleAttribute;
041import java.awt.Graphics2D;
042import java.awt.Shape;
043import java.awt.geom.AffineTransform;
044import java.awt.geom.GeneralPath;
045import java.awt.geom.Rectangle2D;
046import java.util.Iterator;
047
048/**
049 * Implements an embedded font.
050 *
051 * SVG specification: http://www.w3.org/TR/SVG/fonts.html
052 *
053 * @author Mark McKay
054 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
055 */
056public class MissingGlyph extends ShapeElement
057{
058    public static final String TAG_NAME = "missingglyph";
059    
060    //We may define a path
061    private Shape path = null;
062    //Alternately, we may have child graphical elements
063    private int horizAdvX = -1;  //Inherits font's value if not set
064    private int vertOriginX = -1;  //Inherits font's value if not set
065    private int vertOriginY = -1;  //Inherits font's value if not set
066    private int vertAdvY = -1;  //Inherits font's value if not set
067
068    /**
069     * Creates a new instance of Font
070     */
071    public MissingGlyph()
072    {
073    }
074
075    public String getTagName()
076    {
077        return TAG_NAME;
078    }
079
080    /**
081     * Called after the start element but before the end element to indicate
082     * each child tag that has been processed
083     */
084    public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException
085    {
086        super.loaderAddChild(helper, child);
087    }
088
089    protected void build() throws SVGException
090    {
091        super.build();
092
093        StyleAttribute sty = new StyleAttribute();
094
095        String commandList = "";
096        if (getPres(sty.setName("d")))
097        {
098            commandList = sty.getStringValue();
099        }
100
101
102        //If glyph path was specified, calculate it
103        if (commandList != null)
104        {
105            String fillRule = getStyle(sty.setName("fill-rule")) ? sty.getStringValue() : "nonzero";
106
107            PathCommand[] commands = parsePathList(commandList);
108
109            GeneralPath buildPath = new GeneralPath(
110                fillRule.equals("evenodd") ? GeneralPath.WIND_EVEN_ODD : GeneralPath.WIND_NON_ZERO,
111                commands.length);
112
113            BuildHistory hist = new BuildHistory();
114
115            for (int i = 0; i < commands.length; i++)
116            {
117                PathCommand cmd = commands[i];
118                cmd.appendPath(buildPath, hist);
119            }
120
121            //Reflect glyph path to put it in user coordinate system
122            AffineTransform at = new AffineTransform();
123            at.scale(1, -1);
124            path = at.createTransformedShape(buildPath);
125        }
126
127
128        //Read glyph spacing info
129        if (getPres(sty.setName("horiz-adv-x")))
130        {
131            horizAdvX = sty.getIntValue();
132        }
133
134        if (getPres(sty.setName("vert-origin-x")))
135        {
136            vertOriginX = sty.getIntValue();
137        }
138
139        if (getPres(sty.setName("vert-origin-y")))
140        {
141            vertOriginY = sty.getIntValue();
142        }
143
144        if (getPres(sty.setName("vert-adv-y")))
145        {
146            vertAdvY = sty.getIntValue();
147        }
148    }
149
150    public Shape getPath()
151    {
152        return path;
153    }
154
155    public void render(Graphics2D g) throws SVGException
156    {
157        //Do not push or pop stack
158
159        if (path != null)
160        {
161            renderShape(g, path);
162        }
163
164        Iterator it = children.iterator();
165        while (it.hasNext())
166        {
167            SVGElement ele = (SVGElement) it.next();
168            if (ele instanceof RenderableElement)
169            {
170                ((RenderableElement) ele).render(g);
171            }
172        }
173
174        //Do not push or pop stack
175    }
176
177    public int getHorizAdvX()
178    {
179        if (horizAdvX == -1)
180        {
181            horizAdvX = ((Font) parent).getHorizAdvX();
182        }
183        return horizAdvX;
184    }
185
186    public int getVertOriginX()
187    {
188        if (vertOriginX == -1)
189        {
190            vertOriginX = getHorizAdvX() / 2;
191        }
192        return vertOriginX;
193    }
194
195    public int getVertOriginY()
196    {
197        if (vertOriginY == -1)
198        {
199            vertOriginY = ((Font) parent).getFontFace().getAscent();
200        }
201        return vertOriginY;
202    }
203
204    public int getVertAdvY()
205    {
206        if (vertAdvY == -1)
207        {
208            vertAdvY = ((Font) parent).getFontFace().getUnitsPerEm();
209        }
210        return vertAdvY;
211
212    }
213
214    public Shape getShape()
215    {
216        if (path != null)
217        {
218            return shapeToParent(path);
219        }
220        return null;
221    }
222
223    public Rectangle2D getBoundingBox() throws SVGException
224    {
225        if (path != null)
226        {
227            return boundsToParent(includeStrokeInBounds(path.getBounds2D()));
228        }
229        return null;
230    }
231
232    /**
233     * Updates all attributes in this diagram associated with a time event. Ie,
234     * all attributes with track information.
235     *
236     * @return - true if this node has changed state as a result of the time
237     * update
238     */
239    public boolean updateTime(double curTime) throws SVGException
240    {
241        //Fonts can't change
242        return false;
243    }
244
245    /**
246     * @param path the path to set
247     */
248    public void setPath(Shape path)
249    {
250        this.path = path;
251    }
252
253    /**
254     * @param horizAdvX the horizAdvX to set
255     */
256    public void setHorizAdvX(int horizAdvX)
257    {
258        this.horizAdvX = horizAdvX;
259    }
260
261    /**
262     * @param vertOriginX the vertOriginX to set
263     */
264    public void setVertOriginX(int vertOriginX)
265    {
266        this.vertOriginX = vertOriginX;
267    }
268
269    /**
270     * @param vertOriginY the vertOriginY to set
271     */
272    public void setVertOriginY(int vertOriginY)
273    {
274        this.vertOriginY = vertOriginY;
275    }
276
277    /**
278     * @param vertAdvY the vertAdvY to set
279     */
280    public void setVertAdvY(int vertAdvY)
281    {
282        this.vertAdvY = vertAdvY;
283    }
284}