diff --git a/lib/annotations-1.0.0.jar b/lib/annotations-1.0.0.jar deleted file mode 100644 index a2267d6..0000000 Binary files a/lib/annotations-1.0.0.jar and /dev/null differ diff --git a/lib/cssparser-0.9.21.jar b/lib/cssparser-0.9.21.jar new file mode 100644 index 0000000..2625697 Binary files /dev/null and b/lib/cssparser-0.9.21.jar differ diff --git a/readme.md b/readme.md index beade04..1574557 100644 --- a/readme.md +++ b/readme.md @@ -36,7 +36,7 @@ Intellij Platform插件,通过其可以完成从svg文件到Android VectorDraw #### 安装 本插件支持Intellij IDEA和Android Studio,需要JDK版本1.6+ ##### 通过本地jar文件安装 -[从此处](https://github.com/misakuo/svgtoandroid/blob/master/svg2android.jar) 下载`svg2android.jar` 文件,在IDE中打开 Preferences -> Plugins -> Install plugin from disk... 选择 SVG2VectorDrawable.jar ,添加后重启IDE +[从此处](https://github.com/misakuo/svgtoandroid/blob/master/svg2android.zip) 下载`svg2android.zip` 文件,在IDE中打开 Preferences -> Plugins -> Install plugin from disk... 选择 svg2android.zip ,添加后重启IDE ##### 通过插件仓库在线安装 在IDE中打开Preferences -> Plugins -> Browse Repositories,搜索SVG2VectorDrawable,安装插件并重启IDE #### 界面 diff --git a/readme_en.md b/readme_en.md index 22bf4d0..cc6d06d 100644 --- a/readme_en.md +++ b/readme_en.md @@ -34,7 +34,7 @@ SVG2VectorDrawable, an plugin to Intellij platform, provides an automatic tool t This plugin is supporting to Intellij IDEA and Android Studio, need JDK1.6 and higher. ##### Installing from jar file -[Click to download](https://github.com/misakuo/svgtoandroid/raw/master/SVG2VectorDrawable.jar) file `SVG2VectorDrawable.jar`, open `Preferences -> Plugins -> Install plugin from disk...` in IDE, choosing `SVG2VectorDrawable.jar`, you can find plugin's icon in toolbar after restart IDE. +[Click to download](https://github.com/misakuo/svgtoandroid/raw/master/svg2android.zip) file `svg2android.zip`, open `Preferences -> Plugins -> Install plugin from disk...` in IDE, choosing `svg2android.zip`, you can find plugin's icon in toolbar after restart IDE. ##### Installing from plugin repo In IDE,open `Preferences -> Plugins -> Browse Repositories`, search `SVG2VectorDrawable`, install and restart IDE. diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index 61b130a..c2effdc 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -1,8 +1,8 @@ com.moxun.plugin.s2v SVG2VectorDrawable - 1.5.2 - 12 + 1.6 + 13 moxun 1.6
+ Supports SVG with CSS styles; Improve conversion accuracy. 1.5.2
Fix the NumberFormatException.
1.5.1
diff --git a/src/com/moxun/s2v/GenerateAction.java b/src/com/moxun/s2v/GenerateAction.java index 60dfc28..608c651 100644 --- a/src/com/moxun/s2v/GenerateAction.java +++ b/src/com/moxun/s2v/GenerateAction.java @@ -3,33 +3,18 @@ import com.intellij.notification.NotificationType; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.DataKeys; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.MessageType; -import com.intellij.openapi.ui.popup.Balloon; -import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.wm.StatusBar; -import com.intellij.openapi.wm.WindowManager; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.file.PsiDirectoryFactory; import com.intellij.psi.xml.XmlFile; -import com.intellij.ui.awt.RelativePoint; -import com.moxun.s2v.message.InfoMessage; import com.moxun.s2v.utils.CommonUtil; import com.moxun.s2v.utils.Logger; - import com.moxun.s2v.utils.ModulesUtil; import org.apache.http.util.TextUtils; -import org.jetbrains.annotations.NotNull; -import java.io.File; -import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; diff --git a/src/com/moxun/s2v/SVGParser.java b/src/com/moxun/s2v/SVGParser.java index 3751353..ada1da1 100644 --- a/src/com/moxun/s2v/SVGParser.java +++ b/src/com/moxun/s2v/SVGParser.java @@ -130,6 +130,20 @@ public List getSVGChildes() { return childes; } + public XmlTag getStyles() { + List childes = getSVGChildes(); + for (XmlTag tag : childes) { + if ("defs".equals(tag.getName()) && tag.getSubTags() != null) { + for (XmlTag subTag : tag.getSubTags()) { + if ("style".equals(subTag.getName())) { + return subTag; + } + } + } + } + return null; + } + public List getShapeTags(XmlTag parentTag) { List tags = new ArrayList(); XmlTag[] subTags = parentTag.getSubTags(); diff --git a/src/com/moxun/s2v/Transformer.java b/src/com/moxun/s2v/Transformer.java index fc05e34..6b90ee0 100644 --- a/src/com/moxun/s2v/Transformer.java +++ b/src/com/moxun/s2v/Transformer.java @@ -15,18 +15,14 @@ import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.search.FilenameIndex; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlDocument; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; -import com.moxun.s2v.message.InfoMessage; import com.moxun.s2v.utils.*; import java.util.*; -import java.util.Collection; -import java.util.List; -import java.util.Map; - /** * Created by moxun on 15/12/14. */ @@ -39,6 +35,7 @@ public class Transformer { private String xmlName; private PsiDirectory distDir; private SVGParser svgParser; + private StyleParser styleParser; private Transformer() { @@ -46,6 +43,8 @@ private Transformer() { public void transforming(CallBack callBack) { svgParser = new SVGParser(svg, dpi); + styleParser = new StyleParser(svgParser.getStyles()); + Logger.debug(svgParser.toString()); XmlFile dist = getDistXml(); @@ -151,14 +150,14 @@ private void parseShapeNode(XmlTag srcTag, XmlTag distTag, Map e } } - if (element.getAttribute("android:fillColor") == null) { - if (srcTag.getAttribute("fill") != null) { - element.setAttribute("android:fillColor", StdColorUtil.formatColor(srcTag.getAttribute("fill").getValue())); - } else { - element.setAttribute("android:fillColor", Configuration.getDefaultTint()); - } + XmlAttribute id = child.getAttribute("class"); + String idValue = id == null ? null : id.getValue(); + if (idValue != null) { + element.setAttribute("android:name", idValue); } + element.setAttribute("android:fillColor", decideFillColor(srcTag, child)); + distTag.addSubTag(element, false); } } @@ -257,9 +256,32 @@ public Transformer create() { return transformer; } } - - public interface CallBack{ + + public interface CallBack { void onComplete(XmlFile dist); } + + //Priority: style > self > parent + private String decideFillColor(XmlTag group, XmlTag self) { + String result = Configuration.getDefaultTint(); + + XmlAttribute groupFill; + if ((groupFill = group.getAttribute("fill")) != null) { + result = StdColorUtil.formatColor(groupFill.getValue()); + } + + XmlAttribute selfFill; + if ((selfFill = self.getAttribute("fill")) != null) { + result = StdColorUtil.formatColor(selfFill.getValue()); + } + + XmlAttribute id = self.getAttribute("class"); + String colorFromStyle; + if (id != null && id.getValue() != null && (colorFromStyle = styleParser.getFillColor(id.getValue())) != null) { + result = colorFromStyle; + } + + return result; + } } diff --git a/src/com/moxun/s2v/utils/AttrMapper.java b/src/com/moxun/s2v/utils/AttrMapper.java index ba7a8b7..27c5ac8 100644 --- a/src/com/moxun/s2v/utils/AttrMapper.java +++ b/src/com/moxun/s2v/utils/AttrMapper.java @@ -2,9 +2,7 @@ import org.apache.commons.lang.StringUtils; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; +import java.util.*; /** * http://developer.android.com/reference/android/graphics/drawable/VectorDrawable.html @@ -12,6 +10,7 @@ */ public class AttrMapper { private static Map mapper = new HashMap(); + private static List ignoreAttrs = new ArrayList(); static { mapper.clear(); @@ -25,6 +24,11 @@ public class AttrMapper { mapper.put("stroke-linejoin", "android:strokeLineJoin"); mapper.put("stroke-miterlimit", "android:strokeMiterLimit"); mapper.put("stroke-linecap", "android:strokeLineCap"); + + ignoreAttrs.clear(); + ignoreAttrs.add("transform"); + ignoreAttrs.add("d"); + ignoreAttrs.add("class"); } public static boolean isShapeName(String name) { @@ -43,7 +47,7 @@ public static boolean isShapeName(String name) { } public static String getAttrName(String svgAttrName) { - if (!mapper.containsKey(svgAttrName) && !svgAttrName.equals("transform")) { + if (!mapper.containsKey(svgAttrName) && !ignoreAttrs.contains(svgAttrName)) { Logger.warn("Skipping attr [" + svgAttrName + "], because it not supported by Android."); } return mapper.get(svgAttrName); diff --git a/src/com/moxun/s2v/utils/SVGAttrParser.java b/src/com/moxun/s2v/utils/SVGAttrParser.java index d3d131a..9a5e3fb 100644 --- a/src/com/moxun/s2v/utils/SVGAttrParser.java +++ b/src/com/moxun/s2v/utils/SVGAttrParser.java @@ -8,33 +8,33 @@ * Created by moxun on 15/12/16. */ public class SVGAttrParser { - public static String rectToPath(int x, int y, int width, int height, double rx, double ry) { + public static String rectToPath(double x, double y, double width, double height, double rx, double ry) { StringBuilder sb = new StringBuilder(); if (rx == 0 && ry == 0) { - sb.append("M").append(x).append(",").append(y).append("L").append(x + width) - .append(",").append(y).append("L").append(x + width).append(",") - .append(y + height).append("L").append(x).append(",").append(y + height) + sb.append("M").append(x).append(",").append(y).append("L").append(toFixed(x + width)) + .append(",").append(toFixed(y)).append("L").append(toFixed(x + width)).append(",") + .append(toFixed(y + height)).append("L").append(toFixed(x)).append(",").append(toFixed(y + height)) .append("z"); } else { - sb.append("M").append(x + rx).append(",").append(y).append(",") - .append("L").append(x + width - rx).append(",").append(y).append(",") - .append("Q").append(x + width).append(",").append(y).append(",").append(x + width).append(",").append(y + ry).append(",") - .append("L").append(x + width).append(",").append(y + height - ry).append(",") - .append("Q").append(x + width).append(",").append(y + height).append(",").append(x + width - rx).append(",").append(y + height).append(",") - .append("L").append(x + rx).append(",").append(y + height).append(",") - .append("Q").append(x).append(",").append(y + height).append(",").append(x).append(",").append(y + height - ry).append(",") - .append("L").append(x).append(",").append(y + ry).append(",") - .append("Q").append(x).append(",").append(y).append(",").append(x + rx).append(",").append(y).append("z"); + sb.append("M").append(toFixed(x + rx)).append(",").append(toFixed(y)).append(",") + .append("L").append(toFixed(x + width - rx)).append(",").append(toFixed(y)).append(",") + .append("Q").append(toFixed(x + width)).append(",").append(toFixed(y)).append(",").append(toFixed(x + width)).append(",").append(toFixed(y + ry)).append(",") + .append("L").append(toFixed(x + width)).append(",").append(toFixed(y + height - ry)).append(",") + .append("Q").append(toFixed(x + width)).append(",").append(toFixed(y + height)).append(",").append(toFixed(x + width - rx)).append(",").append(toFixed(y + height)).append(",") + .append("L").append(toFixed(x + rx)).append(",").append(toFixed(y + height)).append(",") + .append("Q").append(toFixed(x)).append(",").append(toFixed(y + height)).append(",").append(toFixed(x)).append(",").append(toFixed(y + height - ry)).append(",") + .append("L").append(toFixed(x)).append(",").append(toFixed(y + ry)).append(",") + .append("Q").append(toFixed(x)).append(",").append(toFixed(y)).append(",").append(toFixed(x + rx)).append(",").append(toFixed(y)).append("z"); } - return sb.toString().replaceAll("\\.0,",","); + return sb.toString().replaceAll("\\.0,", ","); } public static String circleToPath(double cx, double cy, double r) { StringBuilder sb = new StringBuilder(); - sb.append("M").append(cx - r).append(",").append(cy).append("a").append(r).append(",").append(r) - .append(" 0 0,1 ").append(r * 2).append(",0").append("a").append(r).append(",").append(r) - .append(" 0 0,1 -").append(r * 2).append(",0z"); - return sb.toString().replaceAll("\\.0,",","); + sb.append("M").append(toFixed(cx - r)).append(",").append(toFixed(cy)).append("a").append(toFixed(r)).append(",").append(toFixed(r)) + .append(" 0 0,1 ").append(toFixed(r * 2)).append(",0").append("a").append(toFixed(r)).append(",").append(toFixed(r)) + .append(" 0 0,1 -").append(toFixed(r * 2)).append(",0z"); + return sb.toString().replaceAll("\\.0,", ","); } public static String polygonToPath(String points) { @@ -51,16 +51,20 @@ public static String ellipseToPath(double cx, double cy, double rx, double ry) { double ctlY = ry * 0.5522847498307935; StringBuilder sb = new StringBuilder(); - sb.append("M").append(cx).append(",").append(cy - ry).append(",") - .append("C").append(toFixed(cx + ctlX)).append(",").append(cy - ry).append(",").append(cx+rx).append(",").append(toFixed(cy - ctlY)).append(",").append(cx + rx).append(",").append(cy).append(",") - .append("C").append(cx+rx).append(",").append(toFixed(cy + ctlY)).append(",").append(toFixed(cx + ctlX)).append(",").append(cy+ry).append(",").append(cx).append(",").append(cy + ry).append(",") - .append("C").append(toFixed(cx - ctlX)).append(",").append(cy+ry).append(",").append(cx - rx).append(",").append(toFixed(cy + ctlY)).append(",").append(cx - rx).append(",").append(cy).append(",") - .append("C").append(cx - rx).append(",").append(toFixed(cy - ctlY)).append(",").append(toFixed(cx - ctlX)).append(",").append(cy - ry).append(",").append(cx).append(",").append(cy - ry).append("z"); - return sb.toString().replaceAll("\\.0,",","); + sb.append("M").append(toFixed(cx)).append(",").append(toFixed(cy - ry)).append(",") + .append("C").append(toFixed(cx + ctlX)).append(",").append(toFixed(cy - ry)).append(",").append(toFixed(cx + rx)).append(",").append(toFixed(cy - ctlY)).append(",").append(toFixed(cx + rx)).append(",").append(toFixed(cy)).append(",") + .append("C").append(toFixed(cx + rx)).append(",").append(toFixed(cy + ctlY)).append(",").append(toFixed(cx + ctlX)).append(",").append(toFixed(cy + ry)).append(",").append(toFixed(cx)).append(",").append(toFixed(cy + ry)).append(",") + .append("C").append(toFixed(cx - ctlX)).append(",").append(toFixed(cy + ry)).append(",").append(toFixed(cx - rx)).append(",").append(toFixed(cy + ctlY)).append(",").append(toFixed(cx - rx)).append(",").append(toFixed(cy)).append(",") + .append("C").append(toFixed(cx - rx)).append(",").append(toFixed(cy - ctlY)).append(",").append(toFixed(cx - ctlX)).append(",").append(toFixed(cy - ry)).append(",").append(toFixed(cx)).append(",").append(toFixed(cy - ry)).append("z"); + return sb.toString().replaceAll("\\.0,", ","); } private static String toFixed(double d) { - return String.format("%.2f", d); + String formatted = String.format("%.2f", d); + if (formatted.endsWith(".00")) { + formatted = formatted.replace(".00",""); + } + return formatted; } public static String getPathData(XmlTag tag) { @@ -68,29 +72,29 @@ public static String getPathData(XmlTag tag) { if (type.equals("path")) { return tag.getAttributeValue("d"); } else if (type.equals("rect")) { - int x = getValue(tag,"x"); - int y = getValue(tag,"y"); - int width = getValue(tag,"width"); - int height = getValue(tag,"height"); - double rx = getValueF(tag,"rx"); - double ry = getValueF(tag,"ry"); + double x = getValueF(tag, "x"); + double y = getValueF(tag, "y"); + double width = getValue(tag, "width"); + double height = getValue(tag, "height"); + double rx = getValueF(tag, "rx"); + double ry = getValueF(tag, "ry"); if (ry == 0) { ry = rx; - } else if (rx == 0){ + } else if (rx == 0) { rx = ry; } - return rectToPath(x,y,width,height,rx,ry); + return rectToPath(x, y, width, height, rx, ry); } else if (type.equals("circle")) { - double cx = getValueF(tag,"cx"); - double cy = getValueF(tag,"cy"); - double r = getValueF(tag,"r"); - return circleToPath(cx,cy,r); + double cx = getValueF(tag, "cx"); + double cy = getValueF(tag, "cy"); + double r = getValueF(tag, "r"); + return circleToPath(cx, cy, r); } else if (type.equals("ellipse")) { - double cx = getValueF(tag,"cx"); - double cy = getValueF(tag,"cy"); - double rx = getValueF(tag,"rx"); - double ry = getValueF(tag,"ry"); - return ellipseToPath(cx,cy,rx,ry); + double cx = getValueF(tag, "cx"); + double cy = getValueF(tag, "cy"); + double rx = getValueF(tag, "rx"); + double ry = getValueF(tag, "ry"); + return ellipseToPath(cx, cy, rx, ry); } else { String points = tag.getAttributeValue("points"); return polygonToPath(points); @@ -121,7 +125,7 @@ public static void main(String[] args) { System.out.println(polygonToPath(" 60,20 100,40 100,80 60,100 20,80 20,40 ")); System.out.println(ellipseToPath(20, 16, 20, 16)); System.out.println(circleToPath(16.852, 7.376, 5)); - System.out.println(rectToPath(10,10,100,100,15,15)); + System.out.println(rectToPath(10, 10, 100, 100, 15, 15)); System.out.println(safeGetValue(null)); System.out.println(safeGetValue("")); diff --git a/src/com/moxun/s2v/utils/StyleParser.java b/src/com/moxun/s2v/utils/StyleParser.java new file mode 100644 index 0000000..0c02b31 --- /dev/null +++ b/src/com/moxun/s2v/utils/StyleParser.java @@ -0,0 +1,90 @@ +package com.moxun.s2v.utils; + +import com.intellij.psi.xml.XmlTag; +import com.steadystate.css.dom.CSSStyleRuleImpl; +import com.steadystate.css.dom.CSSValueImpl; +import com.steadystate.css.format.CSSFormat; +import com.steadystate.css.parser.CSSOMParser; +import com.steadystate.css.parser.SACParserCSS3; +import org.w3c.css.sac.CSSException; +import org.w3c.css.sac.CSSParseException; +import org.w3c.css.sac.ErrorHandler; +import org.w3c.css.sac.InputSource; +import org.w3c.dom.css.CSSRule; +import org.w3c.dom.css.CSSRuleList; +import org.w3c.dom.css.CSSStyleSheet; + +import java.io.IOException; +import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; + +/** + * Created by moxun on 17/1/19. + */ +public class StyleParser { + private XmlTag style; + private CSSStyleSheet styleSheet; + private CSSFormat cssFormat; + private Map styleRuleMap = new HashMap(); + + public StyleParser(XmlTag style) { + this.style = style; + init(); + } + + private void init() { + if (style != null) { + String styleContent = style.getValue().getText(); + if (styleContent != null && !styleContent.isEmpty()) { + InputSource source = new InputSource(new StringReader(styleContent)); + CSSOMParser parser = new CSSOMParser(new SACParserCSS3()); + parser.setErrorHandler(new ParserErrorHandler()); + try { + styleSheet = parser.parseStyleSheet(source, null, null); + cssFormat = new CSSFormat().setRgbAsHex(true); + + CSSRuleList rules = styleSheet.getCssRules(); + for (int i = 0; i < rules.getLength(); i++) { + final CSSRule rule = rules.item(i); + if (rule instanceof CSSStyleRuleImpl) { + styleRuleMap.put(((CSSStyleRuleImpl) rule).getSelectorText(), (CSSStyleRuleImpl) rule); + } + } + + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public String getFillColor(String cssClass) { + //class selector + String id = "." + cssClass; + CSSStyleRuleImpl rule = styleRuleMap.get(id); + if (rule != null) { + CSSValueImpl cssValue = (CSSValueImpl) rule.getStyle().getPropertyCSSValue("fill"); + return cssValue.getCssText(cssFormat).toUpperCase(); + } + return null; + } + + private class ParserErrorHandler implements ErrorHandler { + + @Override + public void warning(CSSParseException e) throws CSSException { + Logger.warn(e.toString()); + } + + @Override + public void error(CSSParseException e) throws CSSException { + Logger.error(e.toString()); + } + + @Override + public void fatalError(CSSParseException e) throws CSSException { + Logger.error("Fatal Error: " + e.toString()); + } + } +} diff --git a/svg2android.zip b/svg2android.zip new file mode 100644 index 0000000..54e6b98 Binary files /dev/null and b/svg2android.zip differ diff --git a/svg2android.jar b/svg2android_1.5.2.jar similarity index 100% rename from svg2android.jar rename to svg2android_1.5.2.jar diff --git a/version.json b/version.json index 0c2fda4..2517188 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "1.5.2", - "versionCode": 12, - "desc": "Fix the 'NumberFormatException.'" + "version": "1.6", + "versionCode": 13, + "desc": "Supports SVG with CSS styles;\n Improve conversion accuracy." }