/**************************************************************************** * Copyright (c) 2008, 2009 Jeremy Dowdall * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Jeremy Dowdall <jeremyd@aspencloud.com> - initial API and implementation *****************************************************************************/ package org.eclipse.nebula.cwt.svg; import static java.lang.Math.PI; import static java.lang.Math.abs; import static java.lang.Math.acos; import static java.lang.Math.cos; import static java.lang.Math.hypot; import static java.lang.Math.round; import static java.lang.Math.sin; import static java.lang.Math.sqrt; import static java.lang.Math.toRadians; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; 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 org.eclipse.nebula.cwt.svg.SvgPaint.PaintType; import org.eclipse.nebula.cwt.svg.SvgTransform.Type; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.PathData; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Display; class SvgLoader { private static final char[] ATTR_CLASS = new char[] { 'c', 'l', 'a', 's', 's' }; private static final char[] ATTR_CX = new char[] { 'c', 'x' }; private static final char[] ATTR_CY = new char[] { 'c', 'y' }; private static final char[] ATTR_D = new char[] { 'd' }; private static final char[] ATTR_FILL = new char[] { 'f', 'i', 'l', 'l' }; private static final char[] ATTR_FILL_OPACITY = new char[] { 'f', 'i', 'l', 'l', '-', 'o', 'p', 'a', 'c', 'i', 't', 'y' }; private static final char[] ATTR_FILL_RULE = new char[] { 'f', 'i', 'l', 'l', '-', 'r', 'u', 'l', 'e' }; private static final char[] ATTR_FX = new char[] { 'f', 'x' }; private static final char[] ATTR_FY = new char[] { 'f', 'y' }; private static final char[] ATTR_GRADIENT_TRANSFORM = new char[] { 'g', 'r', 'a', 'd', 'i', 'e', 'n', 't', 'T', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm' }; private static final char[] ATTR_GRADIENT_UNITS = new char[] { 'g', 'r', 'a', 'd', 'i', 'e', 'n', 't', 'U', 'n', 'i', 't', 's' }; private static final char[] ATTR_HEIGHT = new char[] { 'h', 'e', 'i', 'g', 'h', 't' }; private static final char[] ATTR_ID = new char[] { 'i', 'd' }; private static final char[] ATTR_OFFSET = new char[] { 'o', 'f', 'f', 's', 'e', 't' }; private static final char[] ATTR_POINTS = new char[] { 'p', 'o', 'i', 'n', 't', 's' }; private static final char[] ATTR_R = new char[] { 'r' }; private static final char[] ATTR_RX = new char[] { 'r', 'x' }; private static final char[] ATTR_RY = new char[] { 'r', 'y' }; private static final char[] ATTR_SPREAD_METHOD = new char[] { 's', 'p', 'r', 'e', 'a', 'd', 'M', 'e', 't', 'h', 'o', 'd' }; private static final char[] ATTR_STOP = new char[] { 's', 't', 'o', 'p' }; private static final char[] ATTR_STOP_COLOR = new char[] { 's', 't', 'o', 'p', '-', 'c', 'o', 'l', 'o', 'r' }; private static final char[] ATTR_STOP_OPACITY = new char[] { 's', 't', 'o', 'p', '-', 'o', 'p', 'a', 'c', 'i', 't', 'y' }; private static final char[] ATTR_STROKE = new char[] { 's', 't', 'r', 'o', 'k', 'e' }; private static final char[] ATTR_STROKE_OPACITY = new char[] { 's', 't', 'r', 'o', 'k', 'e', '-', 'o', 'p', 'a', 'c', 'i', 't', 'y' }; private static final char[] ATTR_STROKE_WIDTH = new char[] { 's', 't', 'r', 'o', 'k', 'e', '-', 'w', 'i', 'd', 't', 'h' }; private static final char[] ATTR_STROKE_CAP = new char[] { 's', 't', 'r', 'o', 'k', 'e', '-', 'l', 'i', 'n', 'e', 'c', 'a', 'p' }; private static final char[] ATTR_STROKE_JOIN = new char[] { 's', 't', 'r', 'o', 'k', 'e', '-', 'l', 'i', 'n', 'e', 'j', 'o', 'i', 'n' }; private static final char[] ATTR_STYLE = new char[] { 's', 't', 'y', 'l', 'e' }; private static final char[] ATTR_TRANSFORM = new char[] { 't', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm' }; private static final char[] ATTR_VIEWBOX = new char[] { 'v', 'i', 'e', 'w', 'B', 'o', 'x' }; private static final char[] ATTR_WIDTH = new char[] { 'w', 'i', 'd', 't', 'h' }; private static final char[] ATTR_X = new char[] { 'x' }; private static final char[] ATTR_X1 = new char[] { 'x', '1' }; private static final char[] ATTR_X2 = new char[] { 'x', '2' }; private static final char[] ATTR_XLINK_HREF = new char[] { 'x', 'l', 'i', 'n', 'k', ':', 'h', 'r', 'e', 'f' }; private static final char[] ATTR_Y = new char[] { 'y' }; private static final char[] ATTR_Y1 = new char[] { 'y', '1' }; private static final char[] ATTR_Y2 = new char[] { 'y', '2' }; private static final char[] ELEMENT_CDATA = new char[] { '!', '[', 'C', 'D', 'A', 'T', 'A', '[' }; private static final char[] ELEMENT_CDATA_END = new char[] { ']', ']', '>' }; private static final char[] ELEMENT_CIRCLE = new char[] { 'c', 'i', 'r', 'c', 'l', 'e' }; private static final char[] ELEMENT_COMMENT = new char[] { '!', '-', '-' }; private static final char[] ELEMENT_COMMENT_END = new char[] { '-', '-', '>' }; private static final char[] ELEMENT_DESCRIPTION = new char[] { 'd', 'e', 's', 'c' }; private static final char[] ELEMENT_DEFS = new char[] { 'd', 'e', 'f', 's' }; private static final char[] ELEMENT_DOCTYPE = new char[] { '!', 'D', 'O', 'C', 'T', 'Y', 'P', 'E' }; private static final char[] ELEMENT_ELLIPSE = new char[] { 'e', 'l', 'l', 'i', 'p', 's', 'e' }; private static final char[] ELEMENT_GROUP = new char[] { 'g' }; private static final char[] ELEMENT_LINEAR_GRADIENT = new char[] { 'l', 'i', 'n', 'e', 'a', 'r', 'G', 'r', 'a', 'd', 'i', 'e', 'n', 't' }; private static final char[] ELEMENT_LINE = new char[] { 'l', 'i', 'n', 'e' }; private static final char[] ELEMENT_PATH = new char[] { 'p', 'a', 't', 'h' }; private static final char[] ELEMENT_POLYGON = new char[] { 'p', 'o', 'l', 'y', 'g', 'o', 'n' }; private static final char[] ELEMENT_POLYLINE = new char[] { 'p', 'o', 'l', 'y', 'l', 'i', 'n', 'e' }; private static final char[] ELEMENT_RADIAL_GRADIENT = new char[] { 'r', 'a', 'd', 'i', 'a', 'l', 'G', 'r', 'a', 'd', 'i', 'e', 'n', 't' }; private static final char[] ELEMENT_RECT = new char[] { 'r', 'e', 'c', 't' }; private static final char[] ELEMENT_SVG = new char[] { 's', 'v', 'g' }; private static final char[] ELEMENT_STYLE = new char[] { 's', 't', 'y', 'l', 'e' }; private static final char[] ELEMENT_TITLE = new char[] { 't', 'i', 't', 'l', 'e' }; private static final char[] ELEMENT_USE = new char[] { 'u', 's', 'e' }; private static final char[] ELEMENT_XML = new char[] { '?', 'x', 'm', 'l' }; // private static final String paramRegex = "[^\\d^\\.^-]+"; //$NON-NLS-1$ private static final String paramRegex = "[ ,]+"; //$NON-NLS-1$ private static final Matcher urlMatcher = Pattern.compile(" *url\\( *#(\\w+) *\\) *").matcher(""); //$NON-NLS-1$ //$NON-NLS-2$ private static void addArc(String[] sa, int ix, List<Byte> types, List<Float> points, boolean relative) { float x1 = points.get(points.size() - 2); float y1 = points.get(points.size() - 1); float rx = abs(Float.parseFloat(sa[ix++])); float ry = abs(Float.parseFloat(sa[ix++])); float phi = clampAngle(Float.parseFloat(sa[ix++])); boolean largeArc = (!sa[ix++].equals("0")); //$NON-NLS-1$ boolean sweep = (!sa[ix++].equals("0")); //$NON-NLS-1$ float x2 = Float.parseFloat(sa[ix++]); float y2 = Float.parseFloat(sa[ix++]); if(relative) { x2 += x1; y2 += y1; } if(x1 == x2 && y1 == y2) { return; } if(rx == 0 || ry == 0) { types.add((byte) SWT.PATH_LINE_TO); points.add(x2); points.add(y2); return; } double radPhi = toRadians(phi); double x0 = (cos(radPhi) * ((x1 - x2) / 2)) + (sin(radPhi) * ((y1 - y2) / 2)); double y0 = (-sin(radPhi) * ((x1 - x2) / 2)) + (cos(radPhi) * ((y1 - y2) / 2)); double lambda = ((x0 * x0) / (rx * rx)) + ((y0 * y0) / (ry * ry)); double radicand; if(lambda > 1) { rx *= sqrt(lambda); ry *= sqrt(lambda); radicand = 0; } else { radicand = ((rx * rx * ry * ry) - (rx * rx * y0 * y0) - (ry * ry * x0 * x0)) / ((rx * rx * y0 * y0) + (ry * ry * x0 * x0)); } if(radicand < 0) { rx *= sqrt(lambda); ry *= sqrt(lambda); radicand = 0; } int sign = (largeArc != sweep) ? 1 : -1; double cx0 = sign * sqrt(radicand) * rx * y0 / ry; double cy0 = sign * sqrt(radicand) * -ry * x0 / rx; double cx = (cos(radPhi) * cx0) - (sin(radPhi) * cy0) + ((x1 + x2) / 2); double cy = (sin(radPhi) * cx0) + (cos(radPhi) * cy0) + ((y1 + y2) / 2); double theta1 = getAngle(1, 0, (x0 - cx0) / rx, (y0 - cy0) / ry); double dTheta = getAngle((x0 - cx0) / rx, (y0 - cy0) / ry, (-x0 - cx0) / rx, (-y0 - cy0) / ry); double theta2 = theta1 + dTheta; theta1 = clampAngle(theta1); dTheta = clampAngle(dTheta); theta2 = clampAngle(theta2); if(!sweep) { dTheta = 360 - dTheta; } int increment = 5; int lines = round((float) dTheta) / increment; double theta = theta1; for(int i = 0; i < lines; i++) { sign = (sweep) ? 1 : -1; theta = clampAngle(theta + (sign * increment)); double radTheta = toRadians(theta); double x = cos(radPhi) * rx * cos(radTheta) - sin(radPhi) * ry * sin(radTheta) + cx; double y = sin(radPhi) * rx * cos(radTheta) + cos(radPhi) * ry * sin(radTheta) + cy; types.add((byte) SWT.PATH_LINE_TO); if(i == lines - 1) { points.add(x2); points.add(y2); } else { points.add((float) x); points.add((float) y); } } } private static void addPoint(List<Float> points, String s, boolean relative) { if(relative) { points.add(points.get(points.size() - 2) + Float.parseFloat(s)); } else { points.add(new Float(s)); } } private static double clampAngle(double deg) { if(deg < 0) { deg += 360; } else if(deg > 360) { deg -= 360; } return deg; } private static float clampAngle(float deg) { if(deg < 0) { deg += 360; } else if(deg > 360) { deg -= 360; } return deg; } private static int closer(char[] ca, int start, int end) { if(start >= 0) { char opener = ca[start]; char closer = closerChar(opener); int count = 1; for(int i = start+1; i < ca.length && i <= end; i++) { if(ca[i] == opener && ca[i] != closer) { count++; } else if(ca[i] == closer) { if(closer != '"' || ca[i-1] != '\\') { // check for escape char count--; if(count == 0) { return i; } } } else if(ca[i] == '"') { i = closer(ca, i, end); // just entered a string - get out of it } } } return -1; } private static char closerChar(char c) { switch(c) { case '<': return '>'; case '(': return ')'; case '{': return '}'; case '[': return ']'; case '"': return '"'; case '\'': return '\''; } return 0; } private static int findAll(char[] ca, int from, int to, char...cs) { for(int i = from; i >= 0 && i < ca.length && i <= to; i++) { if(ca[i] == cs[0]) { if(cs.length == 1) { return i; } for(int j = 1; j < cs.length && (i+j) <= to; j++) { if((i+j) == ca.length) { return -1; } if(ca[i+j] != cs[j]) { break; } if(j == cs.length-1) { return i; } } } } return -1; } private static int findAny(char[] ca, int from, int to, char...cs) { for(int i = from; i >= 0 && i < ca.length && i <= to; i++) { for(char c : cs) { if(ca[i] == c) { return i; } } } return -1; } /** * find the closer for the XML tag which begins with the given start position * ('<' should be the first char) * @param ca * @param start * @return */ private static int findClosingTag(char[] ca, int start, int end) { if(start >= 0 && start < ca.length && start < end) { int s1 = findAny(ca, start, end, ' ', '>'); if(s1 != -1) { char[] opener = new char[s1-start]; opener[0] = '<'; char[] closer = new char[s1-start+2]; closer[0] = '<'; closer[1] = '/'; closer[closer.length-1] = '>'; int i = start+1; for( ; i < s1; i++) { opener[i-start] = ca[i]; closer[i-start+1] = ca[i]; } int count1 = 1; int count2 = 1; for( ; i < ca.length; i++) { if(ca[i] == '<') { count1++; if(isNext(ca, i, opener)) { count2++; } else if(isNext(ca, i, closer)) { count2--; if(count2 == 0) { return i; } } else if(isNext(ca, i, ELEMENT_CDATA)) { i = findAll(ca, i+ELEMENT_CDATA.length, end, ELEMENT_CDATA_END); } } else if(ca[i] == '>') { if(ca[i-1] == '/') { count1--; } if(count1 == 0) { return i; } } else if(ca[i] == '"') { i = closer(ca, i, end); // just entered a string - get out of it } } } } return -1; } private static int findNextTag(char[] ca, int start, int end) { int s1 = findAll(ca, start, end, '<'); if(s1 != -1 && s1 < ca.length-1) { if(ca[s1+1] != '/') { return s1; } else { return findNextTag(ca, s1+1, end); } } return -1; } private static int forward(char[] ca, int from) { for(int i = from; i >= 0 && i < ca.length; i++) { if(!Character.isWhitespace(ca[i])) { return i; } } return -1; } private static double getAngle(double ux, double uy, double vx, double vy) { double dot = ux * vx + uy * vy; double au = hypot(ux, uy); double av = hypot(vx, vy); double alpha = dot / (au * av); if(alpha > 1) { alpha = 1; } else if(alpha < -1) { alpha = -1; } double theta = 180 * acos(alpha) / PI; if((ux * vy - uy * vx) < 0) { theta *= -1; } return theta; } private static String getAttrValue(char[] ca, int start, int end, char... name) { char[] search = new char[name.length + 2]; System.arraycopy(name, 0, search, 1, name.length); search[0] = ' '; search[search.length - 1] = '='; int s1 = findAll(ca, start, end, search); if(s1 != -1) { s1 = findAll(ca, s1, end, '"'); if(s1 != -1) { int s2 = closer(ca, s1, end); if(s1 != -1) { return new String(ca, s1 + 1, s2 - s1 - 1); } } } return null; } private static int[] getAttrValueRange(char[] ca, int start, int end, char... name) { char[] search = new char[name.length + 2]; System.arraycopy(name, 0, search, 1, name.length); search[0] = ' '; search[search.length - 1] = '='; int s1 = findAll(ca, start, end, search); if(s1 != -1) { s1 = findAll(ca, s1, end, '"'); if(s1 != -1) { int s2 = closer(ca, s1, end); if(s1 != -1) { return new int[] { s1 + 1, s2 - 1 }; } } } return new int[] { -1, -1 }; } private static Map<String, String> getClassStyles(SvgElement element, char[] ca, int start, int end) { String s = getAttrValue(ca, start, end, ATTR_CLASS); if(s != null) { Map<String, String> styles = new HashMap<String, String>(); String[] classes = s.trim().split(" +"); //$NON-NLS-1$ for(String c : classes) { Map<String, String> pairs = element.getFragment().getStyles("." + c); //$NON-NLS-1$ if(pairs != null) { styles.putAll(pairs); } } return styles; } return new HashMap<String, String>(0); } private static Integer getColorAsInt(String color) { if(color != null) { if(SvgColors.contains(color)) { return SvgColors.get(color); } else if('#' == color.charAt(0)) { if(color.length() == 4) { char[] ca = new char[6]; ca[0] = color.charAt(1); ca[1] = color.charAt(1); ca[2] = color.charAt(2); ca[3] = color.charAt(2); ca[4] = color.charAt(3); ca[5] = color.charAt(3); return Integer.parseInt(new String(ca), 16); } else if(color.length() == 7) { return Integer.parseInt(color.substring(1), 16); } } } return null; } private static Map<String, String> getIdStyles(SvgElement element, char[] ca, int start, int end) { String s = element.getId(); if(s != null) { Map<String, String> styles = new HashMap<String, String>(); Map<String, String> pairs = element.getFragment().getStyles("#" + s); //$NON-NLS-1$ if(pairs != null) { styles.putAll(pairs); } return styles; } return new HashMap<String, String>(0); } private static String getLink(String link) { urlMatcher.reset(link); if(urlMatcher.matches()) { return urlMatcher.group(1); } return null; } /** * Types: * <ul> * <li>matrix(<a> <b> <c> <d> <e> <f>)</li> * <li>translate(<tx> [<ty>])</li> * <li>scale(<sx> [<sy>])</li> * <li>rotate(<rotate-angle> [<cx> <cy>])</li> * <li>skewX(<skew-angle>)</li> * <li>skewY(<skew-angle>)</li> * </ul> * * @param str * @return */ private static SvgTransform getTransform(char[] ca, int[] range) { int s1 = range[0]; SvgTransform first = null; SvgTransform transform = null; while(s1 != -1 && s1 < range[1]) { int s2 = findAll(ca, s1, range[1], '('); int s3 = findAll(ca, s2, range[1], ')'); if(s1 != -1 && s2 != -1 && s3 != -1) { if(transform == null) { first = transform = new SvgTransform(); } else { transform.next = new SvgTransform(); transform = transform.next; } if(isEqual(ca, s1, s2 - 1, "matrix".toCharArray())) { //$NON-NLS-1$ transform.setData(Type.Matrix, new String(ca, s2 + 1, s3 - s2 - 1).split(paramRegex)); } else if(isEqual(ca, s1, s2 - 1, "translate".toCharArray())) { //$NON-NLS-1$ transform.setData(Type.Translate, new String(ca, s2 + 1, s3 - s2 - 1).split(paramRegex)); } else if(isEqual(ca, s1, s2 - 1, "scale".toCharArray())) { //$NON-NLS-1$ transform.setData(Type.Scale, new String(ca, s2 + 1, s3 - s2 - 1).split(paramRegex)); } else if(isEqual(ca, s1, s2 - 1, "rotate".toCharArray())) { //$NON-NLS-1$ transform.setData(Type.Rotate, new String(ca, s2 + 1, s3 - s2 - 1).split(paramRegex)); } else if(isEqual(ca, s1, s2 - 1, "skewx".toCharArray())) { //$NON-NLS-1$ transform.setData(Type.SkewX, new String(ca, s2 + 1, s3 - s2 - 1).split(paramRegex)); } else if(isEqual(ca, s1, s2 - 1, "skewy".toCharArray())) { //$NON-NLS-1$ transform.setData(Type.SkewY, new String(ca, s2 + 1, s3 - s2 - 1).split(paramRegex)); } } s1 = forward(ca, s3 + 1); } if(first != null) { return first; } else { return new SvgTransform(); } } private static String getValue(String name, Map<String, String> idStyles, Map<String, String> classStyles, Map<String, String> attrStyles, String attrValue) { return getValue(name, idStyles, classStyles, attrStyles, attrValue, null); } private static String getValue(String name, Map<String, String> idStyles, Map<String, String> classStyles, Map<String, String> attrStyles, String attrValue, String defaultValue) { if(attrValue != null) { return attrValue; } if(attrStyles.containsKey(name)) { return attrStyles.get(name); } if(classStyles.containsKey(name)) { return classStyles.get(name); } if(idStyles.containsKey(name)) { return idStyles.get(name); } return defaultValue; } private static boolean isEqual(char[] ca, int start, int end, char...test) { if(test.length != (end-start+1)) { return false; } for(int i = start, j = 0; i < end && j < test.length; i++, j++) { if(ca[i] != test[j]) { return false; } } return true; } private static boolean isNext(char[] ca, int start, char...test) { for(int i = start, j = 0; j < test.length; i++, j++) { if(ca[i] != test[j]) { return false; } } return true; } private static boolean isTag(char[] ca, int start, char[] tagName) { if(start >= 0 && start < ca.length && ca[start] == '<' ) { int i = start + 1; for(int j = 0; i < ca.length && j < tagName.length; i++, j++) { if(ca[i] != tagName[j]) { return false; } } if(i < ca.length) { return (ca[i] == ' ') || (ca[i] == '>'); } } return false; } static SvgDocument load(InputStream in) { StringBuilder sb = new StringBuilder(); BufferedInputStream bis = new BufferedInputStream(in); int i; try { while((i = bis.read()) != -1) { char c = (char) i; if(Character.isWhitespace(c)) { // replace all whitespace chars with a space char if(' ' != sb.charAt(sb.length() - 1)) { // no point in having multiple spaces sb.append(' '); } } else { sb.append(c); } } } catch(IOException e) { e.printStackTrace(); } char[] ca = new char[sb.length()]; sb.getChars(0, sb.length(), ca, 0); SvgDocument doc = new SvgDocument(); parse(doc, ca, 0, ca.length - 1); return doc; } static SvgDocument load(String src) { SvgDocument doc = new SvgDocument(); parse(doc, src.toCharArray(), 0, src.length() - 1); return doc; } private static void parse(SvgContainer container, char[] ca, int start, int end) { int s1 = start; while(s1 != -1 && s1 < end) { s1 = findNextTag(ca, s1, end); if(isTag(ca, s1, ELEMENT_GROUP)) { s1 = parseGroup(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_TITLE)) { s1 = parseTitle(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_DESCRIPTION)) { s1 = parseDescription(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_SVG)) { s1 = parseSvg(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_STYLE)) { s1 = parseStyle(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_USE)) { s1 = parseUse(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_PATH)) { s1 = parsePath(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_CIRCLE)) { s1 = parseCircle(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_ELLIPSE)) { s1 = parseEllipse(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_LINE)) { s1 = parseLine(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_POLYGON)) { s1 = parsePolygon(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_POLYLINE)) { s1 = parsePolyline(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_RECT)) { s1 = parseRectangle(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_LINEAR_GRADIENT)) { s1 = parseLinearGradient(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_RADIAL_GRADIENT)) { s1 = parseRadialGradient(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_DEFS)) { s1 = parseDefs(container, ca, s1, end); } else if(isTag(ca, s1, ELEMENT_XML) || isTag(ca, s1, ELEMENT_DOCTYPE)) { s1 = findAll(ca, s1, end, '>'); } else if(isTag(ca, s1, ELEMENT_COMMENT)) { s1 = findAll(ca, s1, end, ELEMENT_COMMENT_END); } else { if(s1 != -1) { int s2 = findAny(ca, s1, end, ' ', '>'); System.out.println("dunno: " + new String(ca, s1 + 1, s2 - s1 - 1)); //$NON-NLS-1$ } s1 = findClosingTag(ca, s1, end); } } } private static int parseCircle(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { int endAttrs = closer(ca, start, end); SvgShape element = new SvgShape(container, getAttrValue(ca, start, endAttrs, ATTR_ID)); element.pathData = new PathData(); element.pathData.points = new float[4]; element.pathData.points[0] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_CX), 0); element.pathData.points[1] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_CY), 0); element.pathData.points[2] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_R)); element.pathData.points[3] = element.pathData.points[2]; parseFill(element, ca, start, endAttrs); parseStroke(element, ca, start, endAttrs); element.transform = getTransform(ca, getAttrValueRange(ca, start, endAttrs, ATTR_TRANSFORM)); } return end; } private static void parseCss(SvgStyle element, char[] ca, int start, int end) { Map<String, Map<String, String>> styles = new HashMap<String, Map<String, String>>(); int s1 = forward(ca, start); int s = findAll(ca, s1, end, '{'); int s2 = reverse(ca, s - 1); String names; while(s1 != -1 && s2 != -1) { names = new String(ca, s1, s2 - s1 + 1); s2 = closer(ca, s, end); if(s2 != -1) { Map<String, String> pairs = parseStyles(ca, s + 1, s2 - 1); for(String name : names.split(" *, *")) { //$NON-NLS-1$ Map<String, String> existing = styles.get(name); if(existing != null) { Map<String, String> m = new HashMap<String, String>(); m.putAll(existing); m.putAll(pairs); styles.put(name, m); } else { styles.put(name, pairs); } } s1 = forward(ca, s2 + 1); s = findAll(ca, s1, end, '{'); s2 = reverse(ca, s - 1); } } element.styles = styles; } private static int parseDefs(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { int endAttrs = closer(ca, start, end); SvgContainer element = new SvgContainer(container, "defs"); //$NON-NLS-1$ element.transform = getTransform(ca, getAttrValueRange(ca, start, endAttrs, ATTR_TRANSFORM)); parse(element, ca, endAttrs, end); } return end; } private static int parseDescription(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { start = closer(ca, start, end); if(start != -1) { container.description = new String(ca, start + 1, end - start - 1); } } return end; } private static int parseEllipse(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { int endAttrs = closer(ca, start, end); SvgShape element = new SvgShape(container, getAttrValue(ca, start, endAttrs, ATTR_ID)); element.pathData = new PathData(); element.pathData.points = new float[4]; element.pathData.points[0] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_CX), 0); element.pathData.points[1] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_CY), 0); element.pathData.points[2] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_RX)); element.pathData.points[3] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_RY)); parseFill(element, ca, start, endAttrs); parseStroke(element, ca, start, endAttrs); element.transform = getTransform(ca, getAttrValueRange(ca, start, endAttrs, ATTR_TRANSFORM)); } return end; } private static void parseFill(SvgGraphic element, char[] ca, int start, int end) { Map<String, String> idStyles = getIdStyles(element, ca, start, end); Map<String, String> classStyles = getClassStyles(element, ca, start, end); Map<String, String> attrStyles = parseStyles(getAttrValue(ca, start, end, ATTR_STYLE)); String s = getValue("fill", idStyles, classStyles, attrStyles, getAttrValue(ca, start, end, ATTR_FILL)); //$NON-NLS-1$ parsePaint(element.fill, s); s = getValue("fill-opacity", idStyles, classStyles, attrStyles, getAttrValue(ca, start, end, ATTR_FILL_OPACITY)); //$NON-NLS-1$ element.fill.opacity = parseFloat(s); s = getValue("fill-rule", idStyles, classStyles, attrStyles, getAttrValue(ca, start, end, ATTR_FILL_RULE)); //$NON-NLS-1$ element.fill.rule = parseRule(s); } private static Float parseFloat(String s) { return parseFloat(s, null); } private static float parseFloat(String s, float defaultValue) { if(s == null) { return defaultValue; } else { return Float.parseFloat(s); } } private static Float parseFloat(String s, Float defaultValue) { if(s == null) { return defaultValue; } else { return new Float(s); } } private static int parseGradientStop(SvgGradient gradient, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { int endAttrs = closer(ca, start, end); SvgGradientStop stop = new SvgGradientStop(gradient, getAttrValue(ca, start, endAttrs, ATTR_ID)); Map<String, String> idStyles = getIdStyles(stop, ca, start, endAttrs); Map<String, String> classStyles = getClassStyles(stop, ca, start, endAttrs); Map<String, String> attrStyles = parseStyles(getAttrValue(ca, start, endAttrs, ATTR_STYLE)); String s = getValue("offset", idStyles, classStyles, attrStyles, getAttrValue(ca, start, endAttrs, ATTR_OFFSET)); //$NON-NLS-1$ stop.offset = parsePercentage(s, 0f, true); s = getValue("stop-color", idStyles, classStyles, attrStyles, getAttrValue(ca, start, endAttrs, ATTR_STOP_COLOR)); //$NON-NLS-1$ stop.color = getColorAsInt(s); s = getValue("stop-opacity", idStyles, classStyles, attrStyles, getAttrValue(ca, start, endAttrs, ATTR_STOP_OPACITY), "1"); //$NON-NLS-1$ //$NON-NLS-2$ stop.opacity = parseFloat(s); gradient.stops.add(stop); } return end; } private static int parseGroup(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { int endAttrs = closer(ca, start, end); SvgContainer element = new SvgContainer(container, getAttrValue(ca, start, endAttrs, ATTR_ID)); parseFill(element, ca, start, endAttrs); parseStroke(element, ca, start, endAttrs); element.transform = getTransform(ca, getAttrValueRange(ca, start, endAttrs, ATTR_TRANSFORM)); parse(element, ca, endAttrs, end); } return end; } // cm, em, ex, in, mm, pc, pt, px private static float parseLength(String s, String defaultString) { if(s == null) { s = defaultString; } if(s.endsWith("%")) { //$NON-NLS-1$ throw new UnsupportedOperationException("TODO parseLength: %"); //$NON-NLS-1$ } else if(s.endsWith("cm")) { //$NON-NLS-1$ final Point dpi = new Point(0, 0); Display.getDefault().syncExec(new Runnable() { public void run() { dpi.x = Display.getDefault().getDPI().x; } }); return Float.parseFloat(s.substring(0, s.length() - 2)) * dpi.x * 0.393700787f; } else if(s.endsWith("em")) { //$NON-NLS-1$ throw new UnsupportedOperationException("TODO parseLength: em"); //$NON-NLS-1$ } else if(s.endsWith("ex")) { //$NON-NLS-1$ throw new UnsupportedOperationException("TODO parseLength: ex"); //$NON-NLS-1$ } else if(s.endsWith("in")) { //$NON-NLS-1$ final Point dpi = new Point(0, 0); Display.getDefault().syncExec(new Runnable() { public void run() { dpi.x = Display.getDefault().getDPI().x; } }); return Float.parseFloat(s.substring(0, s.length() - 2)) * dpi.x; } else if(s.endsWith("mm")) { //$NON-NLS-1$ final Point dpi = new Point(0, 0); Display.getDefault().syncExec(new Runnable() { public void run() { dpi.x = Display.getDefault().getDPI().x; } }); return Float.parseFloat(s.substring(0, s.length() - 2)) * dpi.x * 0.0393700787f; } else if(s.endsWith("pc")) { //$NON-NLS-1$ throw new UnsupportedOperationException("TODO parseLength: pc"); //$NON-NLS-1$ } else if(s.endsWith("pt")) { //$NON-NLS-1$ throw new UnsupportedOperationException("TODO parseLength: pt"); //$NON-NLS-1$ } else if(s.endsWith("px")) { //$NON-NLS-1$ return Float.parseFloat(s.substring(0, s.length() - 2)); } else { return Float.parseFloat(s); } } private static int parseLine(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { int endAttrs = closer(ca, start, end); SvgShape element = new SvgShape(container, getAttrValue(ca, start, endAttrs, ATTR_ID)); element.pathData = new PathData(); element.pathData.types = new byte[2]; element.pathData.points = new float[4]; element.pathData.types[0] = (byte)SWT.PATH_MOVE_TO; element.pathData.points[0] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_X1), 0); element.pathData.points[1] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_Y1), 0); element.pathData.types[1] = (byte)SWT.PATH_LINE_TO; element.pathData.points[2] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_X2), 0); element.pathData.points[3] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_Y2), 0); parseFill(element, ca, start, endAttrs); parseStroke(element, ca, start, endAttrs); } return end; } private static int parseLinearGradient(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { int endAttrs = closer(ca, start, end); SvgGradient gradient = new SvgGradient(container, getAttrValue(ca, start, endAttrs, ATTR_ID)); gradient.data = new float[4]; gradient.data[0] = parsePercentage(getAttrValue(ca, start, endAttrs, ATTR_X1), 0f, false); gradient.data[1] = parsePercentage(getAttrValue(ca, start, endAttrs, ATTR_Y1), 0f, false); gradient.data[2] = parsePercentage(getAttrValue(ca, start, endAttrs, ATTR_X2), 1f, false); gradient.data[3] = parsePercentage(getAttrValue(ca, start, endAttrs, ATTR_Y2), 0f, false); gradient.setLinkId(getAttrValue(ca, start, endAttrs, ATTR_XLINK_HREF)); gradient.setSpreadMethod(getAttrValue(ca, start, endAttrs, ATTR_SPREAD_METHOD)); gradient.setUnits(getAttrValue(ca, start, endAttrs, ATTR_GRADIENT_UNITS)); gradient.transform = getTransform(ca, getAttrValueRange(ca, start, endAttrs, ATTR_GRADIENT_TRANSFORM)); int s1 = endAttrs; while(s1 != -1 && s1 < end) { s1 = findNextTag(ca, s1 + 1, end); if(isNext(ca, s1 + 1, ATTR_STOP)) { s1 = parseGradientStop(gradient, ca, s1, end); } } } return end; } private static String parseLinkId(String id) { if(id != null && id.length() > 2 && '#' == id.charAt(0)) { return id.substring(1); } return null; } private static void parsePaint(SvgPaint paint, String s) { if(s != null) { if("none".equals(s)) { //$NON-NLS-1$ paint.type = PaintType.None; } else if("currentColor".equals(s)) { //$NON-NLS-1$ paint.type = PaintType.Current; } else if(s.startsWith("url")) { //$NON-NLS-1$ paint.type = PaintType.Link; paint.linkId = getLink(s); } else { Integer i = getColorAsInt(s); if(i != null) { paint.type = PaintType.Color; paint.color = i; } else { paint.type = PaintType.None; System.out.println("dunno fill " + paint); //$NON-NLS-1$ } } } } private static int parsePath(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { int endAttrs = closer(ca, start, end); SvgShape element = new SvgShape(container, getAttrValue(ca, start, endAttrs, ATTR_ID)); parseFill(element, ca, start, endAttrs); parseStroke(element, ca, start, endAttrs); parsePathData(element, getAttrValue(ca, start, endAttrs, ATTR_D)); element.transform = getTransform(ca, getAttrValueRange(ca, start, endAttrs, ATTR_TRANSFORM)); } return end; } public static void parsePathData(SvgShape path, String data) { String[] sa = parsePathDataStrings(data); boolean relative; List<Byte> types = new ArrayList<Byte>(); List<Float> points = new ArrayList<Float>(); int i = -1; while(i < sa.length - 1) { i++; switch(sa[i].charAt(0)) { case 'M': case 'm': types.add((byte) SWT.PATH_MOVE_TO); relative = ('m' == sa[i].charAt(0)); addPoint(points, sa[++i], relative); addPoint(points, sa[++i], relative); break; case 'L': case 'l': types.add((byte) SWT.PATH_LINE_TO); relative = ('l' == sa[i].charAt(0)); addPoint(points, sa[++i], relative); addPoint(points, sa[++i], relative); break; case 'H': case 'h': types.add((byte) SWT.PATH_LINE_TO); relative = ('h' == sa[i].charAt(0)); addPoint(points, sa[++i], relative); points.add(points.get(points.size() - 2)); break; case 'V': case 'v': types.add((byte) SWT.PATH_LINE_TO); relative = ('v' == sa[i].charAt(0)); points.add(points.get(points.size() - 2)); addPoint(points, sa[++i], relative); break; case 'C': case 'c': types.add((byte) SWT.PATH_CUBIC_TO); relative = ('c' == sa[i].charAt(0)); addPoint(points, sa[++i], relative); addPoint(points, sa[++i], relative); addPoint(points, sa[++i], relative); addPoint(points, sa[++i], relative); addPoint(points, sa[++i], relative); addPoint(points, sa[++i], relative); break; case 'S': case 's': types.add((byte) SWT.PATH_CUBIC_TO); relative = ('s' == sa[i].charAt(0)); if(SWT.PATH_CUBIC_TO == types.get(types.size() - 2)) { float x2 = points.get(points.size() - 4); float y2 = points.get(points.size() - 3); float x = points.get(points.size() - 2); float y = points.get(points.size() - 1); float x1 = 2 * x - x2; float y1 = 2 * y - y2; points.add(x1); points.add(y1); } else { points.add(points.get(points.size() - 2)); points.add(points.get(points.size() - 2)); } addPoint(points, sa[++i], relative); addPoint(points, sa[++i], relative); addPoint(points, sa[++i], relative); addPoint(points, sa[++i], relative); break; case 'Q': case 'q': types.add((byte) SWT.PATH_QUAD_TO); relative = ('q' == sa[i].charAt(0)); addPoint(points, sa[++i], relative); addPoint(points, sa[++i], relative); addPoint(points, sa[++i], relative); addPoint(points, sa[++i], relative); break; case 'T': case 't': types.add((byte) SWT.PATH_QUAD_TO); relative = ('q' == sa[i].charAt(0)); if(SWT.PATH_QUAD_TO == types.get(types.size() - 2)) { float x2 = points.get(points.size() - 4); float y2 = points.get(points.size() - 3); float x = points.get(points.size() - 2); float y = points.get(points.size() - 1); float x1 = 2 * x - x2; float y1 = 2 * y - y2; points.add(x1); points.add(y1); } else { points.add(points.get(points.size() - 2)); points.add(points.get(points.size() - 2)); } addPoint(points, sa[++i], relative); addPoint(points, sa[++i], relative); break; case 'Z': case 'z': types.add((byte) SWT.PATH_CLOSE); break; case 'A': case 'a': relative = ('a' == sa[i].charAt(0)); addArc(sa, ++i, types, points, relative); i += 6; break; } } path.pathData = new PathData(); path.pathData.types = new byte[types.size()]; for(i = 0; i < types.size(); i++) { path.pathData.types[i] = types.get(i).byteValue(); } path.pathData.points = new float[points.size()]; for(i = 0; i < points.size(); i++) { path.pathData.points[i] = points.get(i).floatValue(); } } private static String[] parsePathDataStrings(String data) { List<String> strs = new ArrayList<String>(); StringBuilder sb = new StringBuilder(); char[] ca = data.toCharArray(); for(int i = 0; i < ca.length; i++) { char c = ca[i]; if('e' == c) { sb.append(c); if(i < ca.length - 1 && ca[i + 1] == '-') { i++; sb.append(ca[i]); } } else if(Character.isLetter(c)) { if(sb != null && sb.length() > 0) { strs.add(sb.toString()); } strs.add(Character.toString(c)); sb = new StringBuilder(); } else if('.' == c || Character.isDigit(c)) { sb.append(c); } else { if(sb != null && sb.length() > 0) { strs.add(sb.toString()); } sb = new StringBuilder(); if('-' == c) { sb.append(c); } } } if(sb != null && sb.length() > 0) { strs.add(sb.toString()); } return strs.toArray(new String[strs.size()]); } private static Float parsePercentage(String s, Float defaultValue, boolean clamp) { if(s != null) { Float offset; if('%' == s.charAt(s.length() - 1)) { offset = Float.parseFloat(s.substring(0, s.length() - 1)) / 100; } else { offset = Float.parseFloat(s); } if(clamp) { if(offset > 1) { offset = new Float(1); } else if(offset < 0) { offset = new Float(0); } } return offset; } return defaultValue; } private static float[] parsePoints(String s) { if(s != null) { String[] sa = s.trim().split("[ ,]"); //$NON-NLS-1$ float[] points = new float[sa.length]; for(int i = 0; i < sa.length; i++) { points[i] = parseFloat(sa[i]); } return points; } return new float[0]; } private static int parsePolygon(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { int endAttrs = closer(ca, start, end); SvgShape element = new SvgShape(container, getAttrValue(ca, start, endAttrs, ATTR_ID)); float[] linePoints = parsePoints(getAttrValue(ca, start, endAttrs, ATTR_POINTS)); element.pathData = new PathData(); element.pathData.types = new byte[1+(linePoints.length/2)]; element.pathData.points = new float[linePoints.length]; element.pathData.types[0] = (byte)SWT.PATH_MOVE_TO; element.pathData.points[0] = linePoints[0]; element.pathData.points[1] = linePoints[1]; int i = 2, j = 1; while(i < linePoints.length-1) { element.pathData.types[j++] = (byte)SWT.PATH_LINE_TO; element.pathData.points[i] = linePoints[i++]; element.pathData.points[i] = linePoints[i++]; } element.pathData.types[element.pathData.types.length-1] = (byte)SWT.PATH_CLOSE; parseFill(element, ca, start, endAttrs); parseStroke(element, ca, start, endAttrs); element.transform = getTransform(ca, getAttrValueRange(ca, start, endAttrs, ATTR_TRANSFORM)); } return end; } private static int parsePolyline(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { int endAttrs = closer(ca, start, end); SvgShape element = new SvgShape(container, getAttrValue(ca, start, endAttrs, ATTR_ID)); float[] linePoints = parsePoints(getAttrValue(ca, start, endAttrs, ATTR_POINTS)); element.pathData = new PathData(); element.pathData.types = new byte[linePoints.length/2]; element.pathData.points = new float[linePoints.length]; element.pathData.types[0] = (byte)SWT.PATH_MOVE_TO; element.pathData.points[0] = linePoints[0]; element.pathData.points[1] = linePoints[1]; int i = 2, j = 1; while(i < linePoints.length-1) { element.pathData.types[j++] = (byte)SWT.PATH_LINE_TO; element.pathData.points[i] = linePoints[i++]; element.pathData.points[i] = linePoints[i++]; } parseFill(element, ca, start, endAttrs); parseStroke(element, ca, start, endAttrs); element.transform = getTransform(ca, getAttrValueRange(ca, start, endAttrs, ATTR_TRANSFORM)); } return end; } private static int parseRadialGradient(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { int endAttrs = closer(ca, start, end); SvgGradient gradient = new SvgGradient(container, getAttrValue(ca, start, endAttrs, ATTR_ID)); gradient.data = new float[5]; gradient.data[0] = parsePercentage(getAttrValue(ca, start, endAttrs, ATTR_CX), null, false); gradient.data[1] = parsePercentage(getAttrValue(ca, start, endAttrs, ATTR_CY), null, false); gradient.data[2] = parsePercentage(getAttrValue(ca, start, endAttrs, ATTR_FX), gradient.data[0], false); gradient.data[3] = parsePercentage(getAttrValue(ca, start, endAttrs, ATTR_FY), gradient.data[1], false); gradient.data[4] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_R)); gradient.setLinkId(getAttrValue(ca, start, endAttrs, ATTR_XLINK_HREF)); gradient.setSpreadMethod(getAttrValue(ca, start, endAttrs, ATTR_SPREAD_METHOD)); gradient.setUnits(getAttrValue(ca, start, endAttrs, ATTR_GRADIENT_UNITS)); gradient.transform = getTransform(ca, getAttrValueRange(ca, start, endAttrs, ATTR_GRADIENT_TRANSFORM)); int s1 = endAttrs; while(s1 != -1 && s1 < end) { s1 = findNextTag(ca, s1 + 1, end); if(isNext(ca, s1 + 1, ATTR_STOP)) { s1 = parseGradientStop(gradient, ca, s1, end); } } } return end; } private static int parseRectangle(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { int endAttrs = closer(ca, start, end); SvgShape element = new SvgShape(container, getAttrValue(ca, start, endAttrs, ATTR_ID)); element.pathData = new PathData(); element.pathData.points = new float[6]; element.pathData.points[0] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_X)); element.pathData.points[1] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_Y)); element.pathData.points[2] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_WIDTH)); element.pathData.points[3] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_HEIGHT)); element.pathData.points[4] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_RX), 0); element.pathData.points[5] = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_RY), 0); parseFill(element, ca, start, endAttrs); parseStroke(element, ca, start, endAttrs); element.transform = getTransform(ca, getAttrValueRange(ca, start, endAttrs, ATTR_TRANSFORM)); } return end; } private static Integer parseRule(String s) { if(s != null) { if("evenodd".equals(s)) { //$NON-NLS-1$ return SWT.FILL_EVEN_ODD; } else if("winding".equals(s)) { //$NON-NLS-1$ return SWT.FILL_WINDING; } } return null; } private static void parseStroke(SvgGraphic element, char[] ca, int start, int end) { Map<String, String> idStyles = getIdStyles(element, ca, start, end); Map<String, String> classStyles = getClassStyles(element, ca, start, end); Map<String, String> attrStyles = parseStyles(getAttrValue(ca, start, end, ATTR_STYLE)); String s = getValue("stroke", idStyles, classStyles, attrStyles, getAttrValue(ca, start, end, ATTR_STROKE)); //$NON-NLS-1$ parsePaint(element.stroke, s); s = getValue("stroke-opacity", idStyles, classStyles, attrStyles, getAttrValue(ca, start, end, ATTR_STROKE_OPACITY)); //$NON-NLS-1$ element.stroke.opacity = parseFloat(s); s = getValue("stroke-width", idStyles, classStyles, attrStyles, getAttrValue(ca, start, end, ATTR_STROKE_WIDTH)); //$NON-NLS-1$ element.stroke.width = parseStrokeWidth(s); s = getValue("stroke-linecap", idStyles, classStyles, attrStyles, getAttrValue(ca, start, end, ATTR_STROKE_CAP)); //$NON-NLS-1$ element.stroke.lineCap = parseStrokeLineCap(s); s = getValue("stroke-linejoin", idStyles, classStyles, attrStyles, getAttrValue(ca, start, end, ATTR_STROKE_JOIN)); //$NON-NLS-1$ element.stroke.lineJoin = parseStrokeLineJoin(s); } private static Integer parseStrokeLineCap(String s) { if(s != null) { if("butt".equals(s)) { //$NON-NLS-1$ return SWT.CAP_FLAT; } else if("round".equals(s)) { //$NON-NLS-1$ return SWT.CAP_ROUND; } else if("square".equals(s)) { //$NON-NLS-1$ return SWT.CAP_SQUARE; } } return null; } private static Integer parseStrokeLineJoin(String s) { if(s != null) { if("bevel".equals(s)) { //$NON-NLS-1$ return SWT.JOIN_BEVEL; } else if("miter".equals(s)) { //$NON-NLS-1$ return SWT.JOIN_MITER; } else if("round".equals(s)) { //$NON-NLS-1$ return SWT.JOIN_ROUND; } } return null; } private static Float parseStrokeWidth(String s) { if(s != null) { if(s.endsWith("px")) { //$NON-NLS-1$ return new Float(s.substring(0, s.length() - 2)); } else { return new Float(s); } } return null; } private static int parseStyle(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { int endData = closer(ca, start, end); SvgStyle element = new SvgStyle(container); int cd1 = findAll(ca, start, end, ELEMENT_CDATA); if(cd1 != -1) { start = cd1 + ELEMENT_CDATA.length; endData = findAll(ca, start, end, ELEMENT_CDATA_END); } else { start = endData + 1; endData = end; } parseCss(element, ca, start, endData); } return end; } private static Map<String, String> parseStyles(char[] ca, int start, int end) { Map<String, String> styles = new HashMap<String, String>(); int len = end - start + 1; if(len > 0 && start + len <= ca.length) { String[] sa = new String(ca, start, end - start + 1).trim().split(" *; *"); //$NON-NLS-1$ for(String s : sa) { String[] sa2 = s.split(" *: *"); //$NON-NLS-1$ if(sa2.length == 2) { styles.put(sa2[0], sa2[1]); } } } return styles; } private static Map<String, String> parseStyles(String styles) { if(styles != null) { return parseStyles(styles.toCharArray(), 0, styles.length() - 1); } return new HashMap<String, String>(0); } private static int parseSvg(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { int endAttrs = closer(ca, start, end); SvgFragment element = new SvgFragment(container, getAttrValue(ca, start, endAttrs, ATTR_ID)); if(container != null) { // x and y have no effect on outermost svg fragments element.x = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_X)); element.y = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_Y)); } element.width = parseLength(getAttrValue(ca, start, endAttrs, ATTR_WIDTH), "100%"); //$NON-NLS-1$ element.height = parseLength(getAttrValue(ca, start, endAttrs, ATTR_HEIGHT), "100%"); //$NON-NLS-1$ element.viewBox = parseViewBox(getAttrValue(ca, start, endAttrs, ATTR_VIEWBOX)); // TODO element.preserveAspectRatio = parse(element, ca, endAttrs, end); } return end; } private static int parseTitle(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { start = closer(ca, start, end); if(start != -1) { container.title = new String(ca, start + 1, end - start - 1); } } return end; } private static int parseUse(SvgContainer container, char[] ca, int start, int end) { end = findClosingTag(ca, start, end); if(end != -1) { int endAttrs = closer(ca, start, end); SvgUse element = new SvgUse(container, getAttrValue(ca, start, endAttrs, ATTR_ID)); element.x = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_X), 0); element.y = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_Y), 0); element.w = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_WIDTH)); element.h = parseFloat(getAttrValue(ca, start, endAttrs, ATTR_HEIGHT)); element.linkId = parseLinkId(getAttrValue(ca, start, endAttrs, ATTR_XLINK_HREF)); element.transform = getTransform(ca, getAttrValueRange(ca, start, endAttrs, ATTR_TRANSFORM)); } return end; } private static float[] parseViewBox(String s) { if(s != null) { float[] vb = new float[4]; String[] sa = s.split(paramRegex); if(sa.length == 4) { vb[0] = Float.parseFloat(sa[0]); vb[1] = Float.parseFloat(sa[1]); vb[2] = Float.parseFloat(sa[2]); vb[3] = Float.parseFloat(sa[3]); return vb; } } return null; } private static int reverse(char[] ca, int from) { for(int i = from; i >= 0 && i < ca.length; i--) { if(!Character.isWhitespace(ca[i])) { return i; } } return -1; } private SvgLoader() { // class should never be instantiated } }