diff --git a/library/src/main/java/com/pixplicity/sharp/Sharp.java b/library/src/main/java/com/pixplicity/sharp/Sharp.java index 859b777..08a7658 100644 --- a/library/src/main/java/com/pixplicity/sharp/Sharp.java +++ b/library/src/main/java/com/pixplicity/sharp/Sharp.java @@ -46,6 +46,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextPaint; +import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.ImageView; @@ -63,7 +64,9 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Scanner; import java.util.Stack; import java.util.regex.Matcher; @@ -1110,41 +1113,36 @@ public void inherit(Gradient parent) { } } - private static class StyleSet { - HashMap styleMap = new HashMap<>(); - - private StyleSet(String string) { - String[] styles = string.split(";"); - for (String s : styles) { - String[] style = s.split(":"); - if (style.length == 2) { - styleMap.put(style[0], style[1]); - } - } - } - - public String getStyle(String name) { - return styleMap.get(name); - } - } - private static class Properties { StyleSet mStyles = null; Attributes mAttrs; + private final String elementName; - private Properties(Attributes attrs) { + private Properties(Attributes attrs, String elementName, StyleSet globalStyleSet) { mAttrs = attrs; String styleAttr = getStringAttr("style", attrs); - if (styleAttr != null) { - mStyles = new StyleSet(styleAttr); - } + + mStyles = new StyleSet.Builder() + .setAttributeValue(styleAttr) + .applyStyleSet(globalStyleSet) + .build(); + + this.elementName = elementName; } public String getAttr(String name) { String v = null; if (mStyles != null) { - v = mStyles.getStyle(name); + + String classAttributeValue = getStringAttr("class", mAttrs); + + List elementClassesList = null; + if (!TextUtils.isEmpty(classAttributeValue)) { + elementClassesList = Arrays.asList(TextUtils.split(classAttributeValue, "\\s+")); + } + + v = mStyles.getStyle(name, elementName, elementClassesList); } if (v == null) { v = getStringAttr(name, mAttrs); @@ -1274,6 +1272,9 @@ private void onSvgEnd() { mSharp.onSvgEnd(mCanvas, mBounds); } + private boolean recordStyleElementContent = false; + private StringBuffer styleElementContent = null; + private T onSvgElement(@Nullable String id, @NonNull T element, @Nullable RectF elementBounds, @@ -1612,6 +1613,8 @@ private void doColor(Properties atts, Integer color, boolean fillMode, Paint pai private int hiddenLevel = 0; private boolean boundsMode = false; + private StyleSet globalStyleSet; + private void doLimits(float x, float y) { if (x < mLimits.left) { mLimits.left = x; @@ -1726,6 +1729,9 @@ public void startElement(String namespaceURI, String localName, String qName, At (int) Math.ceil(mBounds.height())); //Log.d(TAG, "canvas size: " + mCanvas.getWidth() + "x" + mCanvas.getHeight()); onSvgStart(); + } else if (localName.equals("style")) { + recordStyleElementContent = true; + styleElementContent = new StringBuffer(); } else if (localName.equals("defs")) { mReadingDefs = true; } else if (localName.equals("linearGradient")) { @@ -1734,7 +1740,7 @@ public void startElement(String namespaceURI, String localName, String qName, At mGradient = doGradient(false, atts); } else if (localName.equals("stop")) { if (mGradient != null) { - Properties props = new Properties(atts); + Properties props = new Properties(atts, localName, globalStyleSet); float offset = props.getFloat("offset", 0); int color = props.getColor("stop-color"); float alpha = props.getFloat("stop-opacity", 1); @@ -1744,7 +1750,7 @@ public void startElement(String namespaceURI, String localName, String qName, At mGradient.mColors.add(color); } } else if (localName.equals("g")) { - Properties props = new Properties(atts); + Properties props = new Properties(atts, localName, globalStyleSet); // Check to see if this is the "bounds" layer if ("bounds".equalsIgnoreCase(id)) { boundsMode = true; @@ -1827,7 +1833,7 @@ public void startElement(String namespaceURI, String localName, String qName, At ry = height / 2; } pushTransform(atts); - Properties props = new Properties(atts); + Properties props = new Properties(atts, localName, globalStyleSet); mRect.set(x, y, x + width, y + height); if (doFill(props, mRect)) { mRect = onSvgElement(id, mRect, mRect, mFillPaint); @@ -1851,7 +1857,7 @@ public void startElement(String namespaceURI, String localName, String qName, At Float x2 = getFloatAttr("x2", atts); Float y1 = getFloatAttr("y1", atts); Float y2 = getFloatAttr("y2", atts); - Properties props = new Properties(atts); + Properties props = new Properties(atts, localName, globalStyleSet); if (doStroke(props, mRect)) { pushTransform(atts); mLine.set(x1, y1, x2, y2); @@ -1877,7 +1883,7 @@ public void startElement(String namespaceURI, String localName, String qName, At } if (centerX != null && centerY != null && radiusX != null && radiusY != null) { pushTransform(atts); - Properties props = new Properties(atts); + Properties props = new Properties(atts, localName, globalStyleSet); mRect.set(centerX - radiusX, centerY - radiusY, centerX + radiusX, centerY + radiusY); if (doFill(props, mRect)) { mRect = onSvgElement(id, mRect, mRect, mFillPaint); @@ -1903,7 +1909,7 @@ public void startElement(String namespaceURI, String localName, String qName, At Path p = new Path(); if (points.size() > 1) { pushTransform(atts); - Properties props = new Properties(atts); + Properties props = new Properties(atts, localName, globalStyleSet); p.moveTo(points.get(0), points.get(1)); for (int i = 2; i < points.size(); i += 2) { float x = points.get(i); @@ -1954,7 +1960,7 @@ public void startElement(String namespaceURI, String localName, String qName, At } Path p = doPath(d); pushTransform(atts); - Properties props = new Properties(atts); + Properties props = new Properties(atts, localName, globalStyleSet); p.computeBounds(mRect, false); if (doFill(props, mRect)) { p = onSvgElement(id, p, mRect, mFillPaint); @@ -1996,6 +2002,10 @@ public void characters(char ch[], int start, int length) { if (!mTextStack.isEmpty()) { mTextStack.peek().setText(ch, start, length); } + + if (recordStyleElementContent) { + styleElementContent.append(ch, start, length); + } } @Override @@ -2011,6 +2021,11 @@ public void endElement(String namespaceURI, String localName, String qName) onSvgEnd(); mPicture.endRecording(); break; + case "style": + recordStyleElementContent = false; + globalStyleSet = new StyleSet.Builder() + .setStyleTagValue(styleElementContent.toString()) + .build(); case "text": case "tspan": if (!mTextStack.isEmpty()) { @@ -2112,7 +2127,7 @@ public SvgText(Attributes atts, SvgText parentText) { y = getFloatAttr("y", atts, parentText != null ? parentText.y : 0f); text = null; - Properties props = new Properties(atts); + Properties props = new Properties(atts, "text", globalStyleSet); if (doFill(props, null)) { fill = new TextPaint(parentText != null && parentText.fill != null ? parentText.fill diff --git a/library/src/main/java/com/pixplicity/sharp/StyleSet.java b/library/src/main/java/com/pixplicity/sharp/StyleSet.java new file mode 100644 index 0000000..493aa78 --- /dev/null +++ b/library/src/main/java/com/pixplicity/sharp/StyleSet.java @@ -0,0 +1,122 @@ +package com.pixplicity.sharp; + +import android.text.TextUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +class StyleSet { + private HashMap attributeStyleMap = new HashMap<>(); + private HashMap> elementStyleMap = new HashMap<>(); + private HashMap> classHashMap = new HashMap<>(); + + private StyleSet extraStyleSet; + + String getStyle(String styleAttributeName, String elementType, List elementClasses) { + + // returns value that is defined in the style attribute of the element + if (attributeStyleMap.containsKey(styleAttributeName)) { + return attributeStyleMap.get(styleAttributeName); + } + + // returns value that is defined in the class of the element + if (elementClasses != null && elementClasses.size() > 0) { + for(String className: elementClasses) { + if (classHashMap != null && classHashMap.containsKey(className) && classHashMap.get(className).containsKey(styleAttributeName)) { + return classHashMap.get(className).get(styleAttributeName); + } + } + } + + if (elementStyleMap != null && elementStyleMap.containsKey(elementType) && elementStyleMap.get(elementType).containsKey(styleAttributeName)) { + return elementStyleMap.get(elementType).get(styleAttributeName); + } + + if (extraStyleSet != null) { + return extraStyleSet.getStyle(styleAttributeName, elementType, elementClasses); + } + + return null; + } + + private StyleSet() { } + + private void addStyleAttributesToMap(String attributeValue, HashMap attributeStyleMap) { + String[] styles = attributeValue.split(";"); + for (String s : styles) { + String[] style = s.split(":"); + if (style.length == 2) { + attributeStyleMap.put(style[0], style[1]); + } + } + } + + private void parseStyleTag(String styleTagValue) { + + if (TextUtils.isEmpty(styleTagValue)) { + return; + } + // Split into separate elements + + Pattern regex = Pattern.compile("([^\\{]+)\\s*\\{\\s*([^\\}]+)\\s*\\}"); + Matcher matcher = regex.matcher(styleTagValue); + + while (matcher.find()) { + if (matcher.groupCount() == 2) { + HashMap attrMap = new HashMap<>(); + addStyleAttributesToMap(matcher.group(2), attrMap); + if (matcher.group(1).startsWith(".")) { + // Add to the map. Remove the dot in the class name + classHashMap.put(TextUtils.substring(matcher.group(1), 1, matcher.group(1).length()), attrMap); + } else {; + elementStyleMap.put(matcher.group(1), attrMap); + } + } + } + } + + private void mergeStyleSet(StyleSet styleSet) { + extraStyleSet = styleSet; + } + + static class Builder { + private String attributeValue; + private String styleTagValue; + private StyleSet mergeStyleSet; + + protected Builder setAttributeValue(String attributeValue) { + this.attributeValue = attributeValue; + return this; + } + + protected Builder setStyleTagValue(String styleTagValue) { + this.styleTagValue = styleTagValue; + return this; + } + + protected Builder applyStyleSet(StyleSet styleSet) { + this.mergeStyleSet = styleSet; + return this; + } + + protected StyleSet build() { + StyleSet styleSet = new StyleSet(); + + if (!TextUtils.isEmpty(attributeValue)) { + styleSet.addStyleAttributesToMap(attributeValue, styleSet.attributeStyleMap); + } + + if (mergeStyleSet != null) { + styleSet.mergeStyleSet(mergeStyleSet); + } + + if (!TextUtils.isEmpty(styleTagValue)) { + styleSet.parseStyleTag(styleTagValue); + } + + return styleSet; + } + } +}