All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hfg.svg.SVG Maven / Gradle / Ivy

There is a newer version: 20240423
Show newest version
package com.hfg.svg;


import java.awt.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.StringReader;
import java.io.Writer;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.hfg.css.CSS;
import com.hfg.css.CSSDeclaration;
import com.hfg.css.CSSRule;
import com.hfg.graphics.units.GfxSize;
import com.hfg.graphics.units.GfxUnits;
import com.hfg.html.StyleTag;
import com.hfg.svg.filtereffect.*;
import com.hfg.util.collection.CollectionUtil;
import com.hfg.util.mime.MimeType;
import com.hfg.xml.XMLAttribute;
import com.hfg.xml.XMLComment;
import com.hfg.xml.XMLNamespace;
import com.hfg.util.StringUtil;
import com.hfg.xml.XMLNamespaceSet;
import com.hfg.xml.XMLNode;
import com.hfg.xml.XMLTag;
import com.hfg.xml.XMLizable;

//------------------------------------------------------------------------------
/**
 * Object representation of an SVG (Scalable Vector Graphics) tag.
 *
 * @author J. Alex Taylor, hairyfatguy.com
 */
//------------------------------------------------------------------------------
// com.hfg XML/HTML Coding Library
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
// [email protected]
//------------------------------------------------------------------------------

public class SVG extends AbstractSvgNode
{
   private SvgMetadata mMetadata;
   private StyleTag mStyleTag;

   private static final Map sTagToClassMap = new HashMap<>(50);

   private static final Pattern sMeasurementPattern = Pattern.compile("(\\-?[\\d\\.]+)(\\w+)?");
   private static final Pattern sTrailingZeroPattern = Pattern.compile("(\\.?0+)$");

   private static int sNumSigFigsForCoordinates = 7;

   // SVG tag name constants
   public static final String anchor   = "a";
   public static final String circle   = "circle";
   public static final String defs     = "defs";
   public static final String desc     = "desc";
   public static final String ellipse  = "ellipse";
   public static final String feBlend  = "feBlend";
   public static final String feColorMatrix  = "feColorMatrix";
   public static final String feComponentTransfer = "feComponentTransfer";
   public static final String feComposite = "feComposite";
   public static final String feConvolveMatrix = "feConvolveMatrix";
   public static final String feDiffuseLighting = "feDiffuseLighting";
   public static final String feDisplacementMap = "feDisplacementMap";
   public static final String feDistantLight = "feDistantLight";
   public static final String feFlood = "feFlood";
   public static final String feFuncA = "feFuncA";
   public static final String feFuncB = "feFuncB";
   public static final String feFuncG = "feFuncG";
   public static final String feFuncR = "feFuncR";
   public static final String feGaussianBlur = "feGaussianBlur";
   public static final String feImage = "feImage";
   public static final String feMerge = "feMerge";
   public static final String feMergeNode = "feMergeNode";
   public static final String feMorphology = "feMorphology";
   public static final String feOffset = "feOffset";
   public static final String fePointLight = "fePointLight";
   public static final String feSpecularLighting = "feSpecularLighting";
   public static final String feSpotLight = "feSpotLight";
   public static final String feTile = "feTile";
   public static final String feTurbulence = "feTurbulence";
   public static final String foreignObject = "foreignObject";
   public static final String filter   = "filter";
   public static final String group    = "g";
   public static final String line     = "line";
   public static final String marker   = "marker";
   public static final String metadata = "metadata";
   public static final String path     = "path";
   public static final String polygon  = "polygon";
   public static final String polyline = "polyline";
   public static final String rect     = "rect";
   public static final String script   = "script";
   public static final String style    = "style";
   public static final String svg      = "svg";
   public static final String switch_  = "switch";
   public static final String symbol   = "symbol";
   public static final String text     = "text";
   public static final String textPath = "textPath";
   public static final String title    = "title";
   public static final String tspan    = "tspan";
   public static final String use      = "use";

   static
   {
      sTagToClassMap.put(anchor, SvgLink.class);
      sTagToClassMap.put(circle, SvgCircle.class);
      sTagToClassMap.put(defs, SvgDefs.class);
      sTagToClassMap.put(desc, SvgDesc.class);
      sTagToClassMap.put(ellipse, SvgEllipse.class);
      sTagToClassMap.put(feBlend, SvgFeBlend.class);
      sTagToClassMap.put(feColorMatrix, SvgFeColorMatrix.class);
      sTagToClassMap.put(feComponentTransfer, SvgFeComponentTransfer.class);
      sTagToClassMap.put(feComposite, SvgFeComposite.class);
      sTagToClassMap.put(feConvolveMatrix, SvgFeConvolveMatrix.class);
      sTagToClassMap.put(feDiffuseLighting, SvgFeDiffuseLighting.class);
      sTagToClassMap.put(feDisplacementMap, SvgFeDisplacementMap.class);
      sTagToClassMap.put(feDistantLight, SvgFeDistantLight.class);
      sTagToClassMap.put(feFlood, SvgFeFlood.class);
      sTagToClassMap.put(feFuncA, SvgFeFuncA.class);
      sTagToClassMap.put(feFuncB, SvgFeFuncB.class);
      sTagToClassMap.put(feFuncG, SvgFeFuncG.class);
      sTagToClassMap.put(feFuncR, SvgFeFuncR.class);
      sTagToClassMap.put(feGaussianBlur, SvgFeGaussianBlur.class);
      sTagToClassMap.put(feImage, SvgFeImage.class);
      sTagToClassMap.put(feMerge, SvgFeMerge.class);
      sTagToClassMap.put(feMergeNode, SvgFeMergeNode.class);
      sTagToClassMap.put(feMorphology, SvgFeMorphology.class);
      sTagToClassMap.put(feOffset, SvgFeOffset.class);
      sTagToClassMap.put(fePointLight, SvgFePointLight.class);
      sTagToClassMap.put(feSpecularLighting, SvgFeSpecularLighting.class);
      sTagToClassMap.put(feSpotLight, SvgFeSpotLight.class);
      sTagToClassMap.put(feTile, SvgFeTile.class);
      sTagToClassMap.put(feTurbulence, SvgFeTurbulence.class);
      sTagToClassMap.put(filter, SvgFilter.class);
      sTagToClassMap.put(group, SvgGroup.class);
      sTagToClassMap.put(line, SvgLine.class);
      sTagToClassMap.put(marker, SvgMarker.class);
      sTagToClassMap.put(metadata, SvgMetadata.class);
      sTagToClassMap.put(path, SvgPath.class);
      sTagToClassMap.put(polygon, SvgPolygon.class);
      sTagToClassMap.put(polyline, SvgPolyline.class);
      sTagToClassMap.put(rect, SvgRect.class);
      sTagToClassMap.put(script, SvgScript.class);
      sTagToClassMap.put(symbol, SvgSymbol.class);
      sTagToClassMap.put(text, SvgText.class);
      sTagToClassMap.put(textPath, SvgTextPath.class);
      sTagToClassMap.put(title, SvgTitle.class);
      sTagToClassMap.put(tspan, SvgTSpan.class);
      sTagToClassMap.put(use, SvgUse.class);
   }

   //##########################################################################
   // CONSTRUCTORS
   //##########################################################################

   //---------------------------------------------------------------------------
   public SVG()
   {
      super(svg);
      setDefaultXMLNamespaceDeclaration(XMLNamespace.SVG);
//      setAttribute("xmlns:" + XMLNamespace.SVG.getPrefix(),   XMLNamespace.SVG.getURI());
      setAttribute("xmlns:" + XMLNamespace.XLINK.getPrefix(), XMLNamespace.XLINK.getURI());
   }


   //---------------------------------------------------------------------------
   public SVG(XMLNode inXMLTag)
   {
      this();

      inXMLTag.verifyTagName(getTagName());

      if (CollectionUtil.hasValues(inXMLTag.getAttributes()))
      {
         for (XMLAttribute attr : inXMLTag.getAttributes())
         {
            setAttribute(attr.clone());
         }
      }

      List subtags = inXMLTag.getSubtags();
      if (CollectionUtil.hasValues(subtags))
      {
         for (XMLizable subtag : subtags)
         {
            if (subtag instanceof XMLComment
                || ((XMLTag) subtag).getTagName().equalsIgnoreCase("style")
                || ((XMLTag) subtag).getTagName().equalsIgnoreCase("use"))
            {
               addSubtag(subtag);
            }
            else
            {
               addSubtag(SVG.constructFromXMLTag((XMLTag) subtag));
            }
         }
      }
   }

   //##########################################################################
   // PUBLIC METHODS
   //##########################################################################

   //---------------------------------------------------------------------------
   public static int getNumSigFigsForCoordinates()
   {
      return sNumSigFigsForCoordinates;
   }

   //---------------------------------------------------------------------------
   public static void setNumSigFigsForCoordinates(int inValue)
   {
      sNumSigFigsForCoordinates = inValue;
   }

   //---------------------------------------------------------------------------
   /**
    * Method used by SVG classes to impose the globally specified number of
    * significant figures on coordinate values.
    * @param inValue the number to be sig. fig. adjusted
    * @return the sig. fig. adjusted number
    */
   public static String formatCoordinate(float inValue)
   {
      return (0 == inValue ? "0" : trimTrailingZeros(String.format("%." + sNumSigFigsForCoordinates + "G", inValue)));
   }

   //---------------------------------------------------------------------------
   /**
    * Method used by SVG classes to impose the globally specified number of
    * significant figures on coordinate values.
    * @param inValue the number to be sig. fig. adjusted
    * @return the sig. fig. adjusted number
    */
   public static String formatCoordinate(double inValue)
   {
      return (0 == inValue ? "0" : trimTrailingZeros(String.format("%." + sNumSigFigsForCoordinates + "G", inValue)));
   }

   //---------------------------------------------------------------------------
   public static SvgNode constructFromXMLTag(XMLTag inXMLTag)
   {
      Class clazz = sTagToClassMap.get(inXMLTag.getTagName());
      if (null == clazz)
      {
         throw new SvgException("The tag " + StringUtil.singleQuote(inXMLTag.getTagName()) + " could not be mapped to an SVG class!");
      }

      SvgNode svgNode;
      try
      {
         Constructor constructor = clazz.getConstructor(XMLTag.class);

         svgNode = (SvgNode) constructor.newInstance(inXMLTag);
      }
      catch (NoSuchMethodException e)
      {
         throw new SvgException("The class " + StringUtil.singleQuote(clazz) + " needs a constructor that takes an XMLTag.", e);
      }
      catch (Exception e)
      {
         throw new SvgException("Problem during invocation of class " + StringUtil.singleQuote(clazz) + "'s constructor!", e);
      }

      return svgNode;
   }

   //---------------------------------------------------------------------------
   public SVG setFont(Font inFont)
   {
      addStyle("font-family: " + inFont.getName());
      addStyle("font-size:" + inFont.getSize() + "pt;");
      return this;
   }

   //---------------------------------------------------------------------------
   public SVG setOpacity(Float inValue)
   {
      if (inValue != null)
      {
         setAttribute(SvgAttr.opacity, String.format("%.2f", inValue));
      }
      else
      {
         removeAttribute(SvgAttr.opacity);
      }

      return this;
   }

   //--------------------------------------------------------------------------
   /**
    Adds the specified text to a 'style' block.
    @param inStyle CSS style text for inclusion via a 'style' tag
    */
   public SVG addStyleTag(CSS inStyle)
   {
      return addStyleTag(inStyle.toString());
   }

   //--------------------------------------------------------------------------
   /**
    Adds the specified text to a 'style' block.
    @param inStyle CSS style text for inclusion via a 'style' tag
    */
   public SVG addStyleTag(CharSequence inStyle)
   {
      if (null == mStyleTag)
      {
         mStyleTag = new StyleTag();
         mStyleTag.setAttribute(SvgAttr.type, MimeType.TEXT_CSS);
         addSubtag(mStyleTag);
      }

      mStyleTag.addContentWithoutEscaping(inStyle);

      return this;
   }

   //--------------------------------------------------------------------------
   public StyleTag getStyleTag()
   {
      return mStyleTag;
   }

   //--------------------------------------------------------------------------
   @Override
   public SVG addStyle(CharSequence inValue)
   {
      return (SVG) super.addStyle(inValue);
   }

   //---------------------------------------------------------------------------
   @Override
   public SVG setStyle(CharSequence inValue)
   {
      return (SVG) super.setStyle(inValue);
   }

   //---------------------------------------------------------------------------
   public SVG setWidth(int inValue)
   {
      setAttribute(SvgAttr.width, inValue);
      return this;
   }

   //---------------------------------------------------------------------------
   public int getWidth()
   {
      String widthString = getAttributeValue(SvgAttr.width);
      if (! StringUtil.isSet(widthString))
      {
         // Is a viewBox specified?
         Rectangle viewBox = getViewBox();
         if (viewBox != null)
         {
            widthString = viewBox.getWidth() + "px";
         }
         else
         {
            setDimensionsToContentBoundsBox();
            widthString = getAttributeValue(SvgAttr.width);
         }
      }

      Matcher m = sMeasurementPattern.matcher(widthString);
      if (m.matches())
      {
         widthString = m.group(1);
      }

      return (int) Float.parseFloat(widthString);
   }

   //---------------------------------------------------------------------------
   public SVG setHeight(int inValue)
   {
      setAttribute(SvgAttr.height, inValue);
      return this;
   }

   //---------------------------------------------------------------------------
   public int getHeight()
   {
      String heightString = getAttributeValue(SvgAttr.height);
      if (! StringUtil.isSet(heightString))
      {
         // Is a viewBox specified?
         Rectangle viewBox = getViewBox();
         if (viewBox != null)
         {
            heightString = viewBox.getHeight() + "px";
         }
         else
         {
            setDimensionsToContentBoundsBox();
            heightString = getAttributeValue(SvgAttr.height);
         }
      }

      Matcher m = sMeasurementPattern.matcher(heightString);
      if (m.matches())
      {
         heightString = m.group(1);
      }

      return (int) Float.parseFloat(heightString);
   }

   //---------------------------------------------------------------------------
   public SVG setViewBox(Rectangle inValue)
   {
      setAttribute(SvgAttr.viewBox,
                   String.format("%d %d %d %d", (int)inValue.getMinX(), (int)inValue.getMinY(),
                                 (int)inValue.getMaxX(), (int)inValue.getMaxY()));
      return this;
   }

   //---------------------------------------------------------------------------
   public SVG setViewBox(Rectangle2D inValue)
   {
      return setViewBox(inValue.getBounds());
   }

   //---------------------------------------------------------------------------
   public Rectangle getViewBox()
   {
      Rectangle rect = null;
      String stringValue = getAttributeValue(SvgAttr.viewBox);
      if (StringUtil.isSet(stringValue))
      {
         String[] pieces = stringValue.split("\\s+");
         int x = Integer.parseInt(pieces[0]);
         int y = Integer.parseInt(pieces[1]);
         rect = new Rectangle(x, y, (int) Float.parseFloat(pieces[2]) - x, (int) Float.parseFloat(pieces[3]) - y);
      }

      return rect;
   }

   //---------------------------------------------------------------------------
   @Override
   public synchronized void toXML(Writer inWriter)
   {
      if (! hasAttribute(SvgAttr.height)) setDimensionsToContentBoundsBox();
      super.toXML(inWriter);
   }

   //---------------------------------------------------------------------------
   @Override
   protected void toXML(Writer inWriter, XMLNamespaceSet inDeclaredNamespaces)
   {
      if (! hasAttribute(SvgAttr.height)) setDimensionsToContentBoundsBox();
      super.toXML(inWriter, inDeclaredNamespaces);
   }

   //---------------------------------------------------------------------------
   @Override
   public synchronized void toIndentedXML(Writer inWriter, int inInitialIndentLevel, int inIndentSize,
                                          XMLNamespaceSet inDeclaredNamespaces)
   {
      if (! hasAttribute(SvgAttr.height)) setDimensionsToContentBoundsBox();
      super.toIndentedXML(inWriter, inInitialIndentLevel, inIndentSize, inDeclaredNamespaces);
   }

   //---------------------------------------------------------------------------
   public SVG setDimensionsToContentBoundsBox()
   {
      Rectangle2D contentRect = getContentBoundsBox();

      setWidth((int) contentRect.getWidth());
      setHeight((int) contentRect.getHeight());

      // Don't think we need to do this be default
//      setViewBox(contentRect);

      return this;
   }

   //---------------------------------------------------------------------------
   @Override
   public Rectangle2D getBoundsBox()
   {
      double minX = 0;
      double minY = 0;
      double maxX = minX + (getAttributeValue(SvgAttr.width) != null ? GfxSize.allocate(getAttributeValue(SvgAttr.width), GfxUnits.pixels).to(GfxUnits.pixels) : 0);
      double maxY = minY + (getAttributeValue(SvgAttr.height) != null ? GfxSize.allocate(getAttributeValue(SvgAttr.height), GfxUnits.pixels).to(GfxUnits.pixels) : 0);

      Rectangle2D rect = getContentBoundsBox();
      if (rect != null)
      {
         if (rect.getX() < minX) minX = rect.getX();
         if (rect.getY() < minY) minY = rect.getY();
         if (rect.getMaxX() > maxX) maxX = rect.getMaxX();
         if (rect.getMaxY() > maxY) maxY = rect.getMaxY();
      }

      Rectangle2D boundsBox = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
      adjustBoundsForTransform(boundsBox);

      return boundsBox;
   }

   //---------------------------------------------------------------------------
   public Rectangle2D getContentBoundsBox()
   {
      double minX = 0;
      double minY = 0;
      double maxX = 0;
      double maxY = 0;

      for (XMLizable node : getSubtags())
      {
         if (node instanceof SvgNode)
         {
            Rectangle2D rect = ((SvgNode)node).getBoundsBox();
            if (rect != null)
            {
               if (rect.getX() < minX) minX = rect.getX();
               if (rect.getY() < minY) minY = rect.getY();
               if (rect.getMaxX() > maxX) maxX = rect.getMaxX();
               if (rect.getMaxY() > maxY) maxY = rect.getMaxY();
            }
         }
      }

      Rectangle2D boundsBox = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
      adjustBoundsForTransform(boundsBox);

      return boundsBox;
   }

   //---------------------------------------------------------------------------
   public SvgGroup addGroup()
   {
      SvgGroup group = new SvgGroup();
      addSubtag(group);
      return group;
   }

   //---------------------------------------------------------------------------
   public SvgMetadata getMetadata()
   {
      if (null == mMetadata)
      {
         // Check if it has been added via addSubtag()...
         mMetadata = getOptionalSubtagByName(SVG.metadata);
         if (null == mMetadata)
         {
            mMetadata = new SvgMetadata();
            addSubtag(0, mMetadata);
         }
      }

      return mMetadata;
   }

   //---------------------------------------------------------------------------
   public SvgDefs addDefs()
   {
      SvgDefs defs = new SvgDefs();
      addSubtag(defs);
      return defs;
   }

   //---------------------------------------------------------------------------
   public SvgLine addLine(Point inStart, Point inEnd)
   {
      SvgLine line = new SvgLine(inStart, inEnd);
      addSubtag(line);
      return line;
   }

   //---------------------------------------------------------------------------
   public SvgLine addLine(Point2D inStart, Point2D inEnd)
   {
      SvgLine line = new SvgLine(inStart, inEnd);
      addSubtag(line);
      return line;
   }

   //---------------------------------------------------------------------------
   public SvgPath addPath()
   {
      SvgPath path = new SvgPath();
      addSubtag(path);
      return path;
   }

   //---------------------------------------------------------------------------
   public SvgCircle addCircle()
   {
      SvgCircle circle = new SvgCircle();
      addSubtag(circle);
      return circle;
   }

   //---------------------------------------------------------------------------
   public SvgEllipse addEllipse()
   {
      SvgEllipse ellipse = new SvgEllipse();
      addSubtag(ellipse);
      return ellipse;
   }

   //---------------------------------------------------------------------------
   public SvgPolygon addPolygon()
   {
      SvgPolygon polygon = new SvgPolygon();
      addSubtag(polygon);
      return polygon;
   }

   //---------------------------------------------------------------------------
   public SvgPolyline addPolyline()
   {
      SvgPolyline polyline = new SvgPolyline();
      addSubtag(polyline);
      return polyline;
   }

   //---------------------------------------------------------------------------
   public SvgPath addPath(String inPathData)
   {
      SvgPath path = new SvgPath(inPathData);
      addSubtag(path);
      return path;
   }

   //---------------------------------------------------------------------------
   public SvgRect addRect(Rectangle inRect)
   {
      SvgRect rect = new SvgRect(inRect);
      addSubtag(rect);
      return rect;
   }

   //---------------------------------------------------------------------------
   public SvgText addText()
   {
      SvgText text = new SvgText();
      addSubtag(text);
      return text;
   }

   //---------------------------------------------------------------------------
   public SvgText addText(String inText)
   {
      SvgText text = new SvgText(inText);
      addSubtag(text);
      return text;
   }

   //---------------------------------------------------------------------------
   public SvgText addText(String inText, Point2D inLocation)
   {
      SvgText text = new SvgText(inText, inLocation);
      addSubtag(text);
      return text;
   }

   //---------------------------------------------------------------------------
   public SvgText addText(String inText, Font inFont, Point inLocation)
   {
      SvgText text = new SvgText(inText, inFont, inLocation);
      addSubtag(text);
      return text;
   }

   //---------------------------------------------------------------------------
   @Override
   public void draw(Graphics2D g2)
   {
      // Save settings
      Paint  origPaint      = g2.getPaint();
      Stroke origStroke     = g2.getStroke();
      Color  origBackground = g2.getBackground();

      // Paint the background
      Rectangle canvas = new Rectangle(0, 0, getWidth(), getHeight());
      g2.setPaint(Color.WHITE);
      g2.fill(canvas);

      g2.setPaint(Color.BLACK);

      // Were any CSS styles specified?
      CSS css = null;
      List styleTags  = getSubtagsByName(style);
      if (CollectionUtil.hasValues(styleTags))
      {
         for (XMLTag styleTag : styleTags)
         {
            try
            {
               CSS styleCSS = new CSS(new StringReader(styleTag.getUnescapedContent()));
               if (CollectionUtil.hasValues(styleCSS.getCSSRules()))
               {
                  if (null == css)
                  {
                     css = styleCSS;
                  }
                  else
                  {
                     css.addRules(styleCSS.getCSSRules());
                  }
               }
            }
            catch (Exception e)
            {
               System.err.println("Problem extracting CSS style data!:");
               e.printStackTrace();            }
         }
      }

      drawSubnodes(g2, css);

      // Restore settings
      g2.setPaint(origPaint);
      g2.setStroke(origStroke);
      g2.setBackground(origBackground);
   }

   //---------------------------------------------------------------------------
   public void scale(float inProportionalScalingFactor)
   {
      setHeight((int) (getHeight() * inProportionalScalingFactor));
      setWidth((int) (getWidth() * inProportionalScalingFactor));

      // Wrap all contents in a scaling transform
      SvgGroup scalingGroup = new SvgGroup().setTransform("scale(" + formatCoordinate(inProportionalScalingFactor) + ")");

      scalingGroup.setSubtags(getSubtags());

      List subtags = new ArrayList<>();
      subtags.add(scalingGroup);

      setSubtags(subtags);
   }

   //---------------------------------------------------------------------------
   public void optimizeStyles()
   {
       Map styleMap = new HashMap<>();

       recursivelyReplaceStyles(this, styleMap);

       if (null == mStyleTag)
       {
          mStyleTag = new StyleTag();
          addSubtag(mStyleTag);
       }

       for (CSSRule cssRule : styleMap.values())
       {
          mStyleTag.addRule(cssRule);
       }
   }


   //##########################################################################
   // PRIVATE METHODS
   //##########################################################################


   //---------------------------------------------------------------------------
   private void recursivelyReplaceStyles(XMLTag inRootTag, Map inStyleMap)
   {
      List subtags = inRootTag.getSubtags();
      if (CollectionUtil.hasValues(subtags))
      {
         for (XMLizable subtag : subtags)
         {
            if (subtag instanceof XMLTag)
            {
               XMLTag xmlTag = (XMLTag) subtag;

               recursivelyReplaceStyles(xmlTag, inStyleMap);
            }
         }
      }

      String styleValue = inRootTag.getAttributeValue(SvgAttr.style);
      if (styleValue != null)
      {
         // Existing class?
         CSSRule cssRule = inStyleMap.get(styleValue);
         if (null == cssRule)
         {
            String ruleName = "rule_" + (inStyleMap.size() + 1);
            cssRule = new CSSRule().addSelector("." + ruleName).addDeclarations(CSSDeclaration.parse(styleValue));
            inStyleMap.put(styleValue, cssRule);
         }

         inRootTag.removeAttribute(SvgAttr.style);
         ((SvgNode) inRootTag).addClass(cssRule.getSelectors().get(0).toString().substring(1));
      }
   }

   //---------------------------------------------------------------------------
   private static String trimTrailingZeros(String inValue)
   {
      Matcher m = sTrailingZeroPattern.matcher(inValue);

      return (m.find() ? inValue.substring(0, inValue.length() - m.group(1).length()) : inValue);
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy