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 float horizAdvX = -1;  //Inherits font's value if not set
064    private float vertOriginX = -1;  //Inherits font's value if not set
065    private float vertOriginY = -1;  //Inherits font's value if not set
066    private float 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    @Override
076    public String getTagName()
077    {
078        return TAG_NAME;
079    }
080
081    /**
082     * Called after the start element but before the end element to indicate
083     * each child tag that has been processed
084     */
085    @Override
086    public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException
087    {
088        super.loaderAddChild(helper, child);
089    }
090
091    @Override
092    protected void build() throws SVGException
093    {
094        super.build();
095
096        StyleAttribute sty = new StyleAttribute();
097
098        String commandList = "";
099        if (getPres(sty.setName("d")))
100        {
101            commandList = sty.getStringValue();
102        }
103
104
105        //If glyph path was specified, calculate it
106        if (commandList != null)
107        {
108            String fillRule = getStyle(sty.setName("fill-rule")) ? sty.getStringValue() : "nonzero";
109
110            PathCommand[] commands = parsePathList(commandList);
111
112            GeneralPath buildPath = new GeneralPath(
113                fillRule.equals("evenodd") ? GeneralPath.WIND_EVEN_ODD : GeneralPath.WIND_NON_ZERO,
114                commands.length);
115
116            BuildHistory hist = new BuildHistory();
117
118            for (int i = 0; i < commands.length; i++)
119            {
120                PathCommand cmd = commands[i];
121                cmd.appendPath(buildPath, hist);
122            }
123
124            //Reflect glyph path to put it in user coordinate system
125            AffineTransform at = new AffineTransform();
126            at.scale(1, -1);
127            path = at.createTransformedShape(buildPath);
128        }
129
130
131        //Read glyph spacing info
132        if (getPres(sty.setName("horiz-adv-x")))
133        {
134            horizAdvX = sty.getFloatValue();
135        }
136
137        if (getPres(sty.setName("vert-origin-x")))
138        {
139            vertOriginX = sty.getFloatValue();
140        }
141
142        if (getPres(sty.setName("vert-origin-y")))
143        {
144            vertOriginY = sty.getFloatValue();
145        }
146
147        if (getPres(sty.setName("vert-adv-y")))
148        {
149            vertAdvY = sty.getFloatValue();
150        }
151    }
152
153    public Shape getPath()
154    {
155        return path;
156    }
157
158    @Override
159    public void render(Graphics2D g) throws SVGException
160    {
161        //Do not push or pop stack
162
163        if (path != null)
164        {
165            renderShape(g, path);
166        }
167
168        Iterator<SVGElement> it = children.iterator();
169        while (it.hasNext())
170        {
171            SVGElement ele = it.next();
172            if (ele instanceof RenderableElement)
173            {
174                ((RenderableElement) ele).render(g);
175            }
176        }
177
178        //Do not push or pop stack
179    }
180
181    public float getHorizAdvX()
182    {
183        if (horizAdvX == -1)
184        {
185            horizAdvX = ((Font) parent).getHorizAdvX();
186        }
187        return horizAdvX;
188    }
189
190    public float getVertOriginX()
191    {
192        if (vertOriginX == -1)
193        {
194            vertOriginX = getHorizAdvX() / 2;
195        }
196        return vertOriginX;
197    }
198
199    public float getVertOriginY()
200    {
201        if (vertOriginY == -1)
202        {
203            vertOriginY = ((Font) parent).getFontFace().getAscent();
204        }
205        return vertOriginY;
206    }
207
208    public float getVertAdvY()
209    {
210        if (vertAdvY == -1)
211        {
212            vertAdvY = ((Font) parent).getFontFace().getUnitsPerEm();
213        }
214        return vertAdvY;
215
216    }
217
218    @Override
219    public Shape getShape()
220    {
221        if (path != null)
222        {
223            return shapeToParent(path);
224        }
225        return null;
226    }
227
228    @Override
229    public Rectangle2D getBoundingBox() throws SVGException
230    {
231        if (path != null)
232        {
233            return boundsToParent(includeStrokeInBounds(path.getBounds2D()));
234        }
235        return null;
236    }
237
238    /**
239     * Updates all attributes in this diagram associated with a time event. Ie,
240     * all attributes with track information.
241     *
242     * @return - true if this node has changed state as a result of the time
243     * update
244     */
245    @Override
246    public boolean updateTime(double curTime) throws SVGException
247    {
248        //Fonts can't change
249        return false;
250    }
251
252    /**
253     * @param path the path to set
254     */
255    public void setPath(Shape path)
256    {
257        this.path = path;
258    }
259
260    /**
261     * @param horizAdvX the horizAdvX to set
262     */
263    public void setHorizAdvX(float horizAdvX)
264    {
265        this.horizAdvX = horizAdvX;
266    }
267
268    /**
269     * @param vertOriginX the vertOriginX to set
270     */
271    public void setVertOriginX(float vertOriginX)
272    {
273        this.vertOriginX = vertOriginX;
274    }
275
276    /**
277     * @param vertOriginY the vertOriginY to set
278     */
279    public void setVertOriginY(float vertOriginY)
280    {
281        this.vertOriginY = vertOriginY;
282    }
283
284    /**
285     * @param vertAdvY the vertAdvY to set
286     */
287    public void setVertAdvY(float vertAdvY)
288    {
289        this.vertAdvY = vertAdvY;
290    }
291}