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;