From e4ba503c7009130a34376a453bd9c7b334804f01 Mon Sep 17 00:00:00 2001 From: Lonami Exo <totufals@hotmail.com> Date: Tue, 21 Mar 2017 16:56:30 +0100 Subject: [PATCH 1/2] Add SAX parsing code for .svg files --- .../java/com/simplemobiletools/draw/Svg.java | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/app/src/main/java/com/simplemobiletools/draw/Svg.java b/app/src/main/java/com/simplemobiletools/draw/Svg.java index cdd8df0..4aac03c 100644 --- a/app/src/main/java/com/simplemobiletools/draw/Svg.java +++ b/app/src/main/java/com/simplemobiletools/draw/Svg.java @@ -1,18 +1,32 @@ package com.simplemobiletools.draw; +import android.graphics.Color; import android.graphics.drawable.ColorDrawable; +import android.sax.Element; +import android.sax.RootElement; +import android.sax.StartElementListener; +import android.util.Xml; import com.simplemobiletools.draw.actions.Action; +import org.xml.sax.Attributes; + import java.io.BufferedWriter; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStreamWriter; +import java.io.Serializable; import java.io.Writer; +import java.util.ArrayList; import java.util.Map; public class Svg { + + //region Saving + public static void saveSvg(File output, MyCanvas canvas) throws Exception { int backgroundColor = ((ColorDrawable) canvas.getBackground()).getColor(); @@ -57,4 +71,105 @@ public class Svg { writer.write(String.valueOf(options.strokeWidth)); writer.write("\" stroke-linecap=\"round\"/>"); } + + //endregion + + //region Loading + + public static void parseSvg(File file) throws Exception { + InputStream is = null; + final SSvg svg = new SSvg(); + try { + is = new FileInputStream(file); + + // Actual parsing (http://stackoverflow.com/a/4828765) + final String ns = "http://www.w3.org/2000/svg"; + RootElement root = new RootElement(ns, "svg"); + Element rectElement = root.getChild(ns, "rect"); + final Element pathElement = root.getChild(ns, "path"); + + root.setStartElementListener(new StartElementListener() { + @Override + public void start(Attributes attributes) { + int width = Integer.parseInt(attributes.getValue("width")); + int height = Integer.parseInt(attributes.getValue("height")); + svg.setSize(width, height); + } + }); + + rectElement.setStartElementListener(new StartElementListener() { + @Override + public void start(Attributes attributes) { + int width = Integer.parseInt(attributes.getValue("width")); + int height = Integer.parseInt(attributes.getValue("height")); + int color = Color.parseColor(attributes.getValue("fill")); + if (svg.background != null) + throw new UnsupportedOperationException("Unsupported SVG, should only have one <rect>."); + + svg.background = new SRect(width, height, color); + } + }); + + pathElement.setStartElementListener(new StartElementListener() { + public void start(Attributes attributes) { + String d = attributes.getValue("d"); + int color = Color.parseColor(attributes.getValue("stroke")); + float width = Float.parseFloat(attributes.getValue("stroke-width")); + svg.paths.add(new SPath(d, color, width)); + } + }); + + // Once the parsing is set up, parse this InputStream + Xml.parse(is, Xml.Encoding.UTF_8, root.getContentHandler()); + } finally { + if (is != null) + is.close(); + } + } + + //region Svg serializable classes + + private static class SSvg implements Serializable { + int width; + int height; + SRect background; + final ArrayList<SPath> paths; + + SSvg() { + paths = new ArrayList<>(); + } + + void setSize(int w, int h) { + width = w; + height = h; + } + } + + private static class SRect implements Serializable { + final int width; + final int height; + final int color; + + SRect(int w, int h, int c) { + width = w; + height = h; + color = c; + } + } + + private static class SPath implements Serializable { + String data; + int color; + float strokeWidth; + + SPath(String d, int c, float w) { + data = d; + color = c; + strokeWidth = w; + } + } + + //endregion + + //endregion } From d4c041d06e45df5e5300aaf9bfa9f0d7550f1056 Mon Sep 17 00:00:00 2001 From: Lonami Exo <totufals@hotmail.com> Date: Tue, 21 Mar 2017 17:36:56 +0100 Subject: [PATCH 2/2] Add .svg loading logic --- .../com/simplemobiletools/draw/MyCanvas.java | 5 +++ .../com/simplemobiletools/draw/MyPath.java | 36 +++++++++++++++++++ .../java/com/simplemobiletools/draw/Svg.java | 18 +++++++++- .../simplemobiletools/draw/actions/Line.java | 16 ++++++++- .../simplemobiletools/draw/actions/Move.java | 16 ++++++++- .../simplemobiletools/draw/actions/Quad.java | 21 ++++++++++- 6 files changed, 108 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/simplemobiletools/draw/MyCanvas.java b/app/src/main/java/com/simplemobiletools/draw/MyCanvas.java index c3071fd..6fcc6e1 100644 --- a/app/src/main/java/com/simplemobiletools/draw/MyCanvas.java +++ b/app/src/main/java/com/simplemobiletools/draw/MyCanvas.java @@ -98,6 +98,11 @@ public class MyCanvas extends View { return mPaths; } + public void addPath(MyPath path, PaintOptions options) { + mPaths.put(path, options); + pathsUpdated(); + } + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); diff --git a/app/src/main/java/com/simplemobiletools/draw/MyPath.java b/app/src/main/java/com/simplemobiletools/draw/MyPath.java index df70de8..680ec96 100644 --- a/app/src/main/java/com/simplemobiletools/draw/MyPath.java +++ b/app/src/main/java/com/simplemobiletools/draw/MyPath.java @@ -10,6 +10,7 @@ import com.simplemobiletools.draw.actions.Quad; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; +import java.security.InvalidParameterException; import java.util.LinkedList; import java.util.List; @@ -26,12 +27,47 @@ class MyPath extends Path implements Serializable { } } + public void readObject(String pathData) throws InvalidParameterException { + String[] tokens = pathData.split("\\s+"); + for (int i = 0; i < tokens.length; ++i) { + switch (tokens[i].charAt(0)) { + case 'M': + addAction(new Move(tokens[i])); + break; + case 'L': + addAction(new Line(tokens[i])); + break; + case 'Q': + // Quad actions are of the following form: + // "Qx1,y1 x2,y2" + // Since we split the tokens by whitespace, we need to join them again + if (i+1 >= tokens.length) + throw new InvalidParameterException("Error parsing the data for a Quad."); + + addAction(new Quad(tokens[i]+" "+tokens[i+1])); + ++i; + break; + } + } + } + @Override public void reset() { actions.clear(); super.reset(); } + private void addAction(Action action) { + if (action instanceof Move) { + moveTo(((Move)action).x, ((Move)action).y); + } else if (action instanceof Line) { + lineTo(((Line)action).x, ((Line)action).y); + } else if (action instanceof Quad) { + final Quad q = (Quad)action; + quadTo(q.x1, q.y1, q.x2, q.y2); + } + } + @Override public void moveTo(float x, float y) { actions.add(new Move(x, y)); diff --git a/app/src/main/java/com/simplemobiletools/draw/Svg.java b/app/src/main/java/com/simplemobiletools/draw/Svg.java index 4aac03c..d2092c5 100644 --- a/app/src/main/java/com/simplemobiletools/draw/Svg.java +++ b/app/src/main/java/com/simplemobiletools/draw/Svg.java @@ -76,7 +76,22 @@ public class Svg { //region Loading - public static void parseSvg(File file) throws Exception { + public static void loadSvg(File file, MyCanvas canvas) throws Exception { + SSvg svg = parseSvg(file); + + canvas.clearCanvas(); + canvas.setBackgroundColor(svg.background.color); + + for (SPath sp : svg.paths) { + MyPath path = new MyPath(); + path.readObject(sp.data); + PaintOptions options = new PaintOptions(sp.color, sp.strokeWidth); + + canvas.addPath(path, options); + } + } + + public static SSvg parseSvg(File file) throws Exception { InputStream is = null; final SSvg svg = new SSvg(); try { @@ -125,6 +140,7 @@ public class Svg { if (is != null) is.close(); } + return svg; } //region Svg serializable classes diff --git a/app/src/main/java/com/simplemobiletools/draw/actions/Line.java b/app/src/main/java/com/simplemobiletools/draw/actions/Line.java index e5ca3d6..89b09fe 100644 --- a/app/src/main/java/com/simplemobiletools/draw/actions/Line.java +++ b/app/src/main/java/com/simplemobiletools/draw/actions/Line.java @@ -4,10 +4,24 @@ import android.graphics.Path; import java.io.IOException; import java.io.Writer; +import java.security.InvalidParameterException; public final class Line implements Action { - private final float x, y; + public final float x, y; + + public Line(String data) { + if (data.startsWith("L")) + throw new InvalidParameterException("The Line data should start with 'L'."); + + try { + String[] xy = data.substring(1).split(","); + x = Float.parseFloat(xy[0].trim()); + y = Float.parseFloat(xy[1].trim()); + } catch (Exception ignored) { + throw new InvalidParameterException("Error parsing the given Line data."); + } + } public Line(float x, float y) { this.x = x; diff --git a/app/src/main/java/com/simplemobiletools/draw/actions/Move.java b/app/src/main/java/com/simplemobiletools/draw/actions/Move.java index 4ec88ee..1a976e7 100644 --- a/app/src/main/java/com/simplemobiletools/draw/actions/Move.java +++ b/app/src/main/java/com/simplemobiletools/draw/actions/Move.java @@ -4,10 +4,24 @@ import android.graphics.Path; import java.io.IOException; import java.io.Writer; +import java.security.InvalidParameterException; public final class Move implements Action { - private final float x, y; + public final float x, y; + + public Move(String data) throws InvalidParameterException { + if (data.startsWith("M")) + throw new InvalidParameterException("The Move data should start with 'M'."); + + try { + String[] xy = data.substring(1).split(","); + x = Float.parseFloat(xy[0].trim()); + y = Float.parseFloat(xy[1].trim()); + } catch (Exception ignored) { + throw new InvalidParameterException("Error parsing the given Move data."); + } + } public Move(float x, float y) { this.x = x; diff --git a/app/src/main/java/com/simplemobiletools/draw/actions/Quad.java b/app/src/main/java/com/simplemobiletools/draw/actions/Quad.java index 28d53ff..cb0b1c7 100644 --- a/app/src/main/java/com/simplemobiletools/draw/actions/Quad.java +++ b/app/src/main/java/com/simplemobiletools/draw/actions/Quad.java @@ -4,10 +4,29 @@ import android.graphics.Path; import java.io.IOException; import java.io.Writer; +import java.security.InvalidParameterException; public final class Quad implements Action { - private final float x1, y1, x2, y2; + public final float x1, y1, x2, y2; + + public Quad(String data) { + if (data.startsWith("Q")) + throw new InvalidParameterException("The Quad data should start with 'Q'."); + + try { + String[] parts = data.split("\\s+"); + String[] xy1 = parts[0].substring(1).split(","); + String[] xy2 = parts[1].split(","); // No need to skip the 'Q' here + + x1 = Float.parseFloat(xy1[0].trim()); + y1 = Float.parseFloat(xy1[1].trim()); + x2 = Float.parseFloat(xy2[0].trim()); + y2 = Float.parseFloat(xy2[1].trim()); + } catch (Exception ignored) { + throw new InvalidParameterException("Error parsing the given Quad data."); + } + } public Quad(float x1, float y1, float x2, float y2) { this.x1 = x1;