mirror of
				https://github.com/SimpleMobileTools/Simple-Draw.git
				synced 2025-06-05 21:59:17 +02:00 
			
		
		
		
	Merge pull request #51 from XyLoNaMiyX/master
Allow opening existing .svg files
This commit is contained in:
		| @@ -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); | ||||
|   | ||||
| @@ -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)); | ||||
|   | ||||
| @@ -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,121 @@ public class Svg { | ||||
|         writer.write(String.valueOf(options.strokeWidth)); | ||||
|         writer.write("\" stroke-linecap=\"round\"/>"); | ||||
|     } | ||||
|  | ||||
|     //endregion | ||||
|  | ||||
|     //region Loading | ||||
|  | ||||
|     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 { | ||||
|             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(); | ||||
|         } | ||||
|         return svg; | ||||
|     } | ||||
|  | ||||
|     //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 | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user