summaryrefslogblamecommitdiffstats
path: root/src/main/java/com/kitfox/svg/Tspan.java
blob: 52188a5f6fba9e75c7114c68d614b1518773f196 (plain) (tree)
1
2
3
4
5
6
7
8
9
   


                                  
   


                                                                   
   






                                                                   
   














                                                                         


                                        


                                          






                                        
                              
                                  




                                                           

                                        
 
                                                   




                           
                              

                     

                        










                                      
 


















                                            























                                                                                                 








                                                                   


                                               
 
                                                   
 



                                       
 



                                       
 








                                        





                                                         
                                                                    
              
 

          
 
                                                                                        
      














                                 

                                                   
 











                                                     
 




                                                          
 




                                                               
                                                                                          




                                                
                                                     




                                                       



                                              
 


                                                    
          
















                                                    


                                                      

                                
                                         
              
 
                                                           








                                                           
                                                                         

          


                                                                            


                                
                                                                  
                                                                                
      
 
                                                                                                    

                                                                         
                                              


                                                       



                                                      
          
















                                                    
                                   
                                                      



                                                                      
 



                                                                                                     



                                                                       


                                                                           
          

                                              



                                                         


                           



                            
                               





                                                   
 

















                                                               
                                                                                                        






                                                
                                                     




















                                                         


                                 
              




                                                
                                                         














                                                                                       


                           





                                                                                          
                                            














                                                           


                                                                                


















                                                                             
/*
 * SVG Salamander
 * Copyright (c) 2004, Mark McKay
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or 
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 *   - Redistributions of source code must retain the above 
 *     copyright notice, this list of conditions and the following
 *     disclaimer.
 *   - Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials 
 *     provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 * 
 * Mark McKay can be contacted at mark@kitfox.com.  Salamander and other
 * projects can be found at http://www.kitfox.com
 *
 * Created on January 26, 2004, 1:56 AM
 */
package com.kitfox.svg;

import com.kitfox.svg.xml.StyleAttribute;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphMetrics;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

/**
 * @author Mark McKay
 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
 */
public class Tspan extends ShapeElement
{

    public static final String TAG_NAME = "tspan";
    float[] x = null;
    float[] y = null;
    float[] dx = null;
    float[] dy = null;
    float[] rotate = null;
    private String text = "";
//    float cursorX;
//    float cursorY;

//    Shape tspanShape;
    /**
     * Creates a new instance of Stop
     */
    public Tspan()
    {
    }

    public String getTagName()
    {
        return TAG_NAME;
    }

//    public float getCursorX()
//    {
//        return cursorX;
//    }
//
//    public float getCursorY()
//    {
//        return cursorY;
//    }
//
//    public void setCursorX(float cursorX)
//    {
//        this.cursorX = cursorX;
//    }
//
//    public void setCursorY(float cursorY)
//    {
//        this.cursorY = cursorY;
//    }
    /*
     public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent)
     {
     //Load style string
     super.loaderStartElement(helper, attrs, parent);

     String x = attrs.getValue("x");
     String y = attrs.getValue("y");
     String dx = attrs.getValue("dx");
     String dy = attrs.getValue("dy");
     String rotate = attrs.getValue("rotate");

     if (x != null) this.x = XMLParseUtil.parseFloatList(x);
     if (y != null) this.y = XMLParseUtil.parseFloatList(y);
     if (dx != null) this.dx = XMLParseUtil.parseFloatList(dx);
     if (dy != null) this.dy = XMLParseUtil.parseFloatList(dy);
     if (rotate != null)
     {
     this.rotate = XMLParseUtil.parseFloatList(rotate);
     for (int i = 0; i < this.rotate.length; i++)
     this.rotate[i] = (float)Math.toRadians(this.rotate[i]);
     }
     }
     */

    /**
     * Called during load process to add text scanned within a tag
     */
    public void loaderAddText(SVGLoaderHelper helper, String text)
    {
        this.text += text;
    }

    protected void build() throws SVGException
    {
        super.build();

        StyleAttribute sty = new StyleAttribute();

        if (getPres(sty.setName("x")))
        {
            x = sty.getFloatList();
        }

        if (getPres(sty.setName("y")))
        {
            y = sty.getFloatList();
        }

        if (getPres(sty.setName("dx")))
        {
            dx = sty.getFloatList();
        }

        if (getPres(sty.setName("dy")))
        {
            dy = sty.getFloatList();
        }

        if (getPres(sty.setName("rotate")))
        {
            rotate = sty.getFloatList();
            for (int i = 0; i < this.rotate.length; i++)
            {
                rotate[i] = (float) Math.toRadians(this.rotate[i]);
            }

        }
    }

    public void appendToShape(GeneralPath addShape, Point2D cursor) throws SVGException
    {
//        if (x != null)
//        {
//            cursorX = x[0];
//        } else if (dx != null)
//        {
//            cursorX += dx[0];
//        }
//
//        if (y != null)
//        {
//            cursorY = y[0];
//        } else if (dy != null)
//        {
//            cursorY += dy[0];
//        }

        StyleAttribute sty = new StyleAttribute();

        String fontFamily = null;
        if (getStyle(sty.setName("font-family")))
        {
            fontFamily = sty.getStringValue();
        }


        float fontSize = 12f;
        if (getStyle(sty.setName("font-size")))
        {
            fontSize = sty.getFloatValueWithUnits();
        }

        float letterSpacing = 0;
        if (getStyle(sty.setName("letter-spacing")))
        {
            letterSpacing = sty.getFloatValueWithUnits();
        }


        //Get font
        Font font = diagram.getUniverse().getFont(fontFamily);
        if (font == null)
        {
            addShapeSysFont(addShape, font, fontFamily, fontSize, letterSpacing, cursor);
            return;
        }

        FontFace fontFace = font.getFontFace();
        int ascent = fontFace.getAscent();
        float fontScale = fontSize / (float) ascent;

        AffineTransform xform = new AffineTransform();

        strokeWidthScalar = 1f / fontScale;

        float cursorX = (float)cursor.getX();
        float cursorY = (float)cursor.getY();
    
//        int i = 0;

        String drawText = this.text;
        drawText = drawText.trim();
        for (int i = 0; i < drawText.length(); i++)
        {
            if (x != null && i < x.length)
            {
                cursorX = x[i];
            } else if (dx != null && i < dx.length)
            {
                cursorX += dx[i];
            }
            
            if (y != null && i < y.length)
            {
                cursorY = y[i];
            } else if (dy != null && i < dy.length)
            {
                cursorY += dy[i];
            }
  //          i++;
            
            xform.setToIdentity();
            xform.setToTranslation(cursorX, cursorY);
            xform.scale(fontScale, fontScale);
            if (rotate != null)
            {
                xform.rotate(rotate[i]);
            }

            String unicode = drawText.substring(i, i + 1);
            MissingGlyph glyph = font.getGlyph(unicode);

            Shape path = glyph.getPath();
            if (path != null)
            {
                path = xform.createTransformedShape(path);
                addShape.append(path, false);
            }

            cursorX += fontScale * glyph.getHorizAdvX() + letterSpacing;
        }

        //Save final draw point so calling method knows where to begin next
        // text draw
        cursor.setLocation(cursorX, cursorY);
        strokeWidthScalar = 1f;
    }

    private void addShapeSysFont(GeneralPath addShape, Font font,
        String fontFamily, float fontSize, float letterSpacing, Point2D cursor)
    {

        java.awt.Font sysFont = new java.awt.Font(fontFamily, java.awt.Font.PLAIN, (int) fontSize);

        FontRenderContext frc = new FontRenderContext(null, true, true);
        String renderText = this.text.trim();

        AffineTransform xform = new AffineTransform();

        float cursorX = (float)cursor.getX();
        float cursorY = (float)cursor.getY();
//        int i = 0;
        for (int i = 0; i < renderText.length(); i++)
        {
            if (x != null && i < x.length)
            {
                cursorX = x[i];
            } else if (dx != null && i < dx.length)
            {
                cursorX += dx[i];
            }

            if (y != null && i < y.length)
            {
                cursorY = y[i];
            } else if (dy != null && i < dy.length)
            {
                cursorY += dy[i];
            }
//            i++;
            
            xform.setToIdentity();
            xform.setToTranslation(cursorX, cursorY);
            if (rotate != null)
            {
                xform.rotate(rotate[Math.min(i, rotate.length - 1)]);
            }

//            String unicode = renderText.substring(i, i + 1);
            GlyphVector textVector = sysFont.createGlyphVector(frc, renderText.substring(i, i + 1));
            Shape glyphOutline = textVector.getGlyphOutline(0);
            GlyphMetrics glyphMetrics = textVector.getGlyphMetrics(0);

            glyphOutline = xform.createTransformedShape(glyphOutline);
            addShape.append(glyphOutline, false);


//            cursorX += fontScale * glyph.getHorizAdvX() + letterSpacing;
            cursorX += glyphMetrics.getAdvance() + letterSpacing;
        }
        
        cursor.setLocation(cursorX, cursorY);
    }

    public void render(Graphics2D g) throws SVGException
    {
        float cursorX = 0;
        float cursorY = 0;
    
        if (x != null)
        {
            cursorX = x[0];
            cursorY = y[0];
        } else if (dx != null)
        {
            cursorX += dx[0];
            cursorY += dy[0];
        }

        StyleAttribute sty = new StyleAttribute();

        String fontFamily = null;
        if (getPres(sty.setName("font-family")))
        {
            fontFamily = sty.getStringValue();
        }


        float fontSize = 12f;
        if (getPres(sty.setName("font-size")))
        {
            fontSize = sty.getFloatValueWithUnits();
        }

        //Get font
        Font font = diagram.getUniverse().getFont(fontFamily);
        if (font == null)
        {
            System.err.println("Could not load font");
            java.awt.Font sysFont = new java.awt.Font(fontFamily, java.awt.Font.PLAIN, (int) fontSize);
            renderSysFont(g, sysFont);
            return;
        }


        FontFace fontFace = font.getFontFace();
        int ascent = fontFace.getAscent();
        float fontScale = fontSize / (float) ascent;

        AffineTransform oldXform = g.getTransform();
        AffineTransform xform = new AffineTransform();

        strokeWidthScalar = 1f / fontScale;

        int posPtr = 1;

        for (int i = 0; i < text.length(); i++)
        {
            xform.setToTranslation(cursorX, cursorY);
            xform.scale(fontScale, fontScale);
            g.transform(xform);

            String unicode = text.substring(i, i + 1);
            MissingGlyph glyph = font.getGlyph(unicode);

            Shape path = glyph.getPath();
            if (path != null)
            {
                renderShape(g, path);
            } else
            {
                glyph.render(g);
            }

            if (x != null && posPtr < x.length)
            {
                cursorX = x[posPtr];
                cursorY = y[posPtr++];
            } else if (dx != null && posPtr < dx.length)
            {
                cursorX += dx[posPtr];
                cursorY += dy[posPtr++];
            }

            cursorX += fontScale * glyph.getHorizAdvX();

            g.setTransform(oldXform);
        }

        strokeWidthScalar = 1f;
    }

    protected void renderSysFont(Graphics2D g, java.awt.Font font) throws SVGException
    {
        float cursorX = 0;
        float cursorY = 0;
    
        int posPtr = 1;
        FontRenderContext frc = g.getFontRenderContext();

        Shape textShape = font.createGlyphVector(frc, text).getOutline(cursorX, cursorY);
        renderShape(g, textShape);
        Rectangle2D rect = font.getStringBounds(text, frc);
        cursorX += (float) rect.getWidth();
    }

    public Shape getShape()
    {
        return null;
        //return shapeToParent(tspanShape);
    }

    public Rectangle2D getBoundingBox()
    {
        return null;
        //return boundsToParent(tspanShape.getBounds2D());
    }

    /**
     * Updates all attributes in this diagram associated with a time event. Ie,
     * all attributes with track information.
     *
     * @return - true if this node has changed state as a result of the time
     * update
     */
    public boolean updateTime(double curTime) throws SVGException
    {
        //Tspan does not change
        return false;
    }

    public String getText()
    {
        return text;
    }

    public void setText(String text)
    {
        this.text = text;
    }
}