Clone in a terminal:
Commit 8ca8a1c
Johan Mattsson - 2014-03-26T07:02:29+00:00
8ca8a1c26ee38dcdea3876570ef43f6bd12b8b78johan.mattsson.m@gmail.com

Improved SVG parser The SVG code has been rewritten and two separate ways of parsing SVG files has been added. The first instruction is interpreted differently in Inkscape and Illustrator.

Changed files (8)

Diff

diff --git a/libbirdfont/BirdFont.vala b/libbirdfont/BirdFont.valaindex 103bdb7..5ee8263 100644--- a/libbirdfont/BirdFont.vala+++ b/libbirdfont/BirdFont.vala@@ -253,7 +253,7 @@ public class BirdFont { 		current_glyph = new Glyph (""); 		 		experimental = args.has_argument ("--test");-		show_coordinates = args.has_argument ("--show-coordinates");+		show_coordinates = args.has_argument ("--show-coordinates") || experimental; 		fatal_wanings = args.has_argument ("--fatal-warning"); 		win32 = (arg[0].index_of (".exe") > -1) || arg[0] == "wine"; diff --git a/libbirdfont/BirdFontFile.vala b/libbirdfont/BirdFontFile.valaindex f48f85a..ddd794b 100644--- a/libbirdfont/BirdFontFile.vala+++ b/libbirdfont/BirdFontFile.vala@@ -56,7 +56,6 @@ class BirdFontFile { 		font.font_file = "typeface.bf"; 		tr = new TextReader.for_doc (xml_data, ""); 		ok = load_xml (tr);-		 		Parser.cleanup (); 		 		return ok;diff --git a/libbirdfont/ClipTool.vala b/libbirdfont/ClipTool.valaindex 4436969..c9d1ddb 100644--- a/libbirdfont/ClipTool.vala+++ b/libbirdfont/ClipTool.vala@@ -93,10 +93,7 @@ public class ClipTool : Tool { 		if (bf_clipboard_data) { 			import_birdfont_clipboard (data); 		} else if (data != "") {-			new_paths = ImportSvg.import_svg_data (data);-			foreach (Path p in new_paths.paths) {-				((!)destination).active_paths.append (p);-			}+			ImportSvg.import_svg_data (data); 		} 		 		((!)destination).update_view ();			diff --git a/libbirdfont/EditPointHandle.vala b/libbirdfont/EditPointHandle.valaindex 4087014..f261b83 100644--- a/libbirdfont/EditPointHandle.vala+++ b/libbirdfont/EditPointHandle.vala@@ -177,7 +177,7 @@ public class EditPointHandle  { 		EditPointHandle h; 		 		if (unlikely (type == PointType.NONE)) {-			warning ("Invalit type.");+			warning ("Invalid type."); 		} 		 		if (type == PointType.QUADRATIC) {diff --git a/libbirdfont/ImportSvg.vala b/libbirdfont/ImportSvg.valaindex 010effc..e670abc 100644--- a/libbirdfont/ImportSvg.vala+++ b/libbirdfont/ImportSvg.vala@@ -1,5 +1,5 @@ /*-    Copyright (C) 2012, 2013 Johan Mattsson+    Copyright (C) 2012, 2013, 2014 Johan Mattsson      This library is free software; you can redistribute it and/or modify      it under the terms of the GNU Lesser General Public License as @@ -15,11 +15,23 @@ using Xml; using Math;  namespace BirdFont {-	++public enum SvgFormat {+	INKSCAPE,+	ILLUSTRATOR+}+ public class ImportSvg {+	+	SvgFormat format = SvgFormat.INKSCAPE;+	 	public ImportSvg () { 	} 	+	public void set_format (SvgFormat f) {+		format = f;+	}+	 	public static void import () { 		string? p; 		string path;@@ -29,101 +41,93 @@ public class ImportSvg { 		if (p == null) { 			return; 		}+		 		path = (!) p; 		import_svg (path); 	} 	-	public static PathList import_svg_data (string xml_data) {+	public static void import_svg_data (string xml_data) { 		PathList path_list = new PathList ();-		PathList pl;-		Path p;-		string svg;-		string xml;-		Glyph glyph;-		-		if (BirdFont.win32) {-			glyph = MainWindow.get_current_glyph ();-			-			// Paths-			xml = xml_data.replace ("id", "__");-			foreach (string svg_data in xml.split ("d=\"")) {-				svg = svg_data.substring (0, svg_data.index_of ("\""));+		Glyph glyph; +		Xml.Doc* doc;+		Xml.Node* root = null;+		string[] lines = xml_data.split ("\n");+		string xml_document;+		bool has_format = false;+		StringBuilder sb = new StringBuilder ();+		ImportSvg parser = new ImportSvg ();+		TextReader tr;++		try {+			foreach (string l in lines) {+				if (l.index_of ("<!--") > -1 && l.index_of ("Illustrator") > -1) {+					parser.set_format (SvgFormat.ILLUSTRATOR);+					has_format = true;+				}+				+				if (l.index_of ("<!--") > -1 && l.index_of ("Inkscape") > -1) {+					parser.set_format (SvgFormat.INKSCAPE);+					has_format = true;+				}	 				-				if (svg.has_prefix ("M") || svg.has_prefix ("m")) {-					pl = parse_svg_data (svg, glyph);-					path_list.append (pl);+				// FIXME: libxml2 (2.7.8) refuses to parse svg files created with Adobe Illustrator on +				// windows. This is a way around it.+				if (l.index_of ("<!") == -1 && l.index_of ("]>") == -1) {+					sb.append (l);+					sb.append ("\n"); 				} 			} 			-			// Polygons-			xml = xml_data;-			foreach (string svg_data in xml.split ("<polygon points=\"")) {-				svg = svg_data.substring (0, svg_data.index_of ("\""));-				p = parse_polygon_data (svg, glyph);-				path_list.paths.append (p);-			}			-		} else {-			Xml.Doc* doc;-			Xml.Node* root = null;-			-			Parser.init ();-			-			doc = Parser.parse_doc (xml_data);-			root = doc->get_root_element ();-			return_val_if_fail (root != null, path_list);-			path_list = parse_svg_file (root);+			xml_document = sb.str;+		} catch (GLib.Error e) {+			warning (e.message);+		}++		if (!has_format) {+			warning ("No format identifier found.");+		} +		Parser.init ();+		+		tr = new TextReader.for_doc (xml_document, "");+		tr.read ();+		root = tr.expand ();+				+		if (root == null) {+			warning ("Failed to load SVG file"); 			delete doc;-			Parser.cleanup ();+			return;+		}++		path_list = parser.parse_svg_file (root);++		delete doc;+		Parser.cleanup ();+	+		glyph = MainWindow.get_current_glyph ();+		foreach (Path p in path_list.paths) {+			glyph.add_path (p); 		} 		-		return path_list;+		foreach (Path p in path_list.paths) {+			glyph.add_active_path (p);+			p.update_region_boundaries ();+		}+		+		glyph.close_path ();	 	} 	 	public static void import_svg (string path) {-		// FIXME: libxml2 (2.7.8) refuses to parse svg files created with Adobe Illustrator on -		// windows. This is a way around it.-		if (BirdFont.win32) {-			try {-				File f = File.new_for_path (path);-				DataInputStream dis = new DataInputStream (f.read ());-				string xml_data;-				string? line;-				StringBuilder sb = new StringBuilder ();--				while ((line = dis.read_line (null)) != null) {-					sb.append ((!) line);-					sb.append ("\n");-				}-				-				xml_data = sb.str;-				import_svg_data (xml_data);-			} catch (GLib.Error e) {-				warning (e.message);-			}-		} else {-			Xml.Doc* doc;-			Xml.Node* root = null;-			-			Parser.init ();-			-			doc = Parser.parse_file (path);-			root = doc->get_root_element ();-					-			if (root == null) {-				warning ("Failed to load SVG file");-				delete doc;-				return;-			}--			parse_svg_file (root);--			delete doc;-			Parser.cleanup ();+		string svg_data;+		try {+			FileUtils.get_contents (path, out svg_data);+		} catch (GLib.Error e) {+			warning (e.message); 		}+		import_svg_data (svg_data); 	} 	-	private static PathList parse_svg_file (Xml.Node* root) {+	private PathList parse_svg_file (Xml.Node* root) { 		Xml.Node* node; 		PathList pl = new PathList (); 		@@ -146,7 +150,7 @@ public class ImportSvg { 		return pl; 	} 	-	private static void parse_layer (Xml.Node* node, PathList pl) {+	private void parse_layer (Xml.Node* node, PathList pl) { 		return_if_fail (node != null); 				 		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {@@ -164,7 +168,7 @@ public class ImportSvg { 		} 	} 	-	private static void parse_polygon (Xml.Node* node, PathList pl) {+	private void parse_polygon (Xml.Node* node, PathList pl) { 		string attr_name = ""; 		string attr_content; 		Glyph glyph = MainWindow.get_current_glyph ();@@ -181,7 +185,7 @@ public class ImportSvg { 		}		 	} 	-	private static void parse_path (Xml.Node* node, PathList pl) {+	private void parse_path (Xml.Node* node, PathList pl) { 		string attr_name = ""; 		string attr_content; 		Glyph glyph = MainWindow.get_current_glyph ();@@ -240,29 +244,24 @@ public class ImportSvg { 	} 	 	/** -	 * Add svg paths to a glyph.-	 *  	 * @param d svg data 	 * @param glyph add paths to this glyph-	 * @param svg_glyph parse svg glyph (origo in lower left corner)+	 * @param svg_glyph parse svg glyph with origo in lower left corner 	 *  	 * @return the new paths 	 */-	public static PathList parse_svg_data (string d, Glyph glyph, bool svg_glyph = false, double units = 1) {-		string[] c;-		string[] command;-		int ci = 0;+	public PathList parse_svg_data (string d, Glyph glyph, bool svg_glyph = false, double units = 1) { 		double px = 0; 		double py = 0; 		double px2 = 0; 		double py2 = 0; 		double cx = 0; 		double cy = 0;-		double[] p;-		int pi = 0; 		string data; 		Font font; 		PathList path_list = new PathList ();+		BezierPoints[] bezier_points;+		string[] c; 		 		if (d.index_of ("z") == -1) { // ignore all open paths 			return path_list;@@ -272,20 +271,22 @@ public class ImportSvg { 		 		data = add_separators (d); 		c = data.split (" ");-		p = new double[2 * c.length];-		command = new string[2 * c.length];+		bezier_points = new BezierPoints[2 * c.length + 1]; 		-		for (int i = 0; i < 2 * c.length; i++) {-			command[i] = "";-			p[i] = 0;+		for (int i = 0; i < 2 * c.length + 1; i++) {+			bezier_points[i] = new BezierPoints (); 		} 		+		int bi = 0;+		 		// parse path-		for (int i = 0; i < c.length; i++) {	+		int i = -1;+		while (++i < c.length) {	 			if (c[i] == "m") {-				while (is_point (c[i + 1])) {-					command[ci++] = "M";-+				while (is_point (c[i + 1])) { // FIXME: check array bounds+					bezier_points[bi].type = 'M';+					bezier_points[bi].svg_type = 'm';+					 					px += parse_double (c[++i]); 					 					if (svg_glyph) {@@ -294,15 +295,15 @@ public class ImportSvg { 						py += -parse_double (c[++i]); 					} 					-					p[pi++] = px;-					p[pi++] = py;+					bezier_points[bi].x0 = px;+					bezier_points[bi].y0 = py;+					bi++; 				}-			}--			if (c[i] == "M") {+			} else if (c[i] == "M") { 				while (is_point (c[i + 1])) {-					command[ci++] = "M";-+					bezier_points[bi].type = 'M';+					bezier_points[bi].svg_type = 'M';+					 					px = parse_double (c[++i]); 					 					if (svg_glyph) {@@ -311,67 +312,67 @@ public class ImportSvg { 						py = -parse_double (c[++i]); 					} 					-					p[pi++] = px;-					p[pi++] = py;+					bezier_points[bi].x0 = px;+					bezier_points[bi].y0 = py;+					bi++; 				}-			}--			if (c[i] == "h") {+			} else if (c[i] == "h") { 				while (is_point (c[i + 1])) {-					command[ci++] = "L";-+					bezier_points[bi].type = 'L';+					bezier_points[bi].svg_type = 'h';+					 					px += parse_double (c[++i]); -					p[pi++] = px;-					p[pi++] = py;+					bezier_points[bi].x0 = px;+					bezier_points[bi].y0 = py;+					bi++; 				}-			}--			if (c[i] == "H") {+			} else if (c[i] == "H") { 				while (is_point (c[i + 1])) {-					command[ci++] = "L";-+					bezier_points[bi].type = 'L';+					bezier_points[bi].svg_type = 'H';+					 					px = parse_double (c[++i]); -					p[pi++] = px;-					p[pi++] = py;+					bezier_points[bi].x0 = px;+					bezier_points[bi].y0 = py;+					bi++; 				}-			}--			if (c[i] == "v") {+			} else if (c[i] == "v") { 				while (is_point (c[i + 1])) {-					command[ci++] = "L";-					+					bezier_points[bi].type = 'L';+					bezier_points[bi].svg_type = 'v';+										 					if (svg_glyph) { 						py = py + parse_double (c[++i]); 					} else { 						py = py + -parse_double (c[++i]); 					} 					-					p[pi++] = px;-					p[pi++] = py;+					bezier_points[bi].x0 = px;+					bezier_points[bi].y0 = py;+					bi++; 				}				-			}--			if (c[i] == "V") {+			} else if (c[i] == "V") { 				while (is_point (c[i + 1])) {-					command[ci++] = "L";-					+					bezier_points[bi].type = 'L';+					bezier_points[bi].svg_type = 'V';+										 					if (svg_glyph) { 						py = parse_double (c[++i]); 					} else { 						py = -parse_double (c[++i]); 					} 					-					p[pi++] = px;-					p[pi++] = py;+					bezier_points[bi].x0 = px;+					bezier_points[bi].y0 = py;+					bi++; 				}-			}-						-			if (c[i] == "l") {+			} else if (c[i] == "l") { 				while (is_point (c[i + 1])) {-					command[ci++] = "L";-					+					bezier_points[bi].type = 'L';+					bezier_points[bi].svg_type = 'l';+										 					cx = px + parse_double (c[++i]); 					 					if (svg_glyph) {@@ -380,18 +381,18 @@ public class ImportSvg { 						cy = py + -parse_double (c[++i]); 					} 					-					p[pi++] = cx;-					p[pi++] = cy;-					 					px = cx; 					py = cy;++					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+					bi++; 				}-			}-			-			if (c[i] == "L") {+			} else if (c[i] == "L") { 				while (is_point (c[i + 1])) {-					command[ci++] = "L";-					+					bezier_points[bi].type = 'L';+					bezier_points[bi].svg_type = 'L';+										 					cx = parse_double (c[++i]); 					 					if (svg_glyph) {@@ -400,18 +401,18 @@ public class ImportSvg { 						cy = -parse_double (c[++i]); 					} 					-					p[pi++] = cx;-					p[pi++] = cy;-					 					px = cx;-					py = cy;					+					py = cy;+					+					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+					bi++;				 				}	-			}-						-			if (c[i] == "c") {+			} else if (c[i] == "c") { 				while (is_point (c[i + 1])) {-					command[ci++] = "C";-					+					bezier_points[bi].type = 'C';+					bezier_points[bi].svg_type = 'C';+										 					cx = px + parse_double (c[++i]); 					 					if (svg_glyph) {@@ -420,9 +421,9 @@ public class ImportSvg { 						cy = py + -parse_double (c[++i]); 					} 					-					p[pi++] = cx;-					p[pi++] = cy;-					+					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+										 					cx = px + parse_double (c[++i]); 					 					if (svg_glyph) {@@ -433,10 +434,10 @@ public class ImportSvg { 					 					px2 = cx; 					py2 = cy;-					-					p[pi++] = px2;-					p[pi++] = py2;-					+										+					bezier_points[bi].x1 = px2;+					bezier_points[bi].y1 = py2;+										 					cx = px + parse_double (c[++i]); 					 					if (svg_glyph) {@@ -445,19 +446,19 @@ public class ImportSvg { 						cy = py + -parse_double (c[++i]); 					} 					-					p[pi++] = cx;-					p[pi++] = cy;-					+					bezier_points[bi].x2 = cx;+					bezier_points[bi].y2 = cy;+										 					px = cx; 					py = cy;+					+					bi++; 				}-			}-			-			if (c[i] == "C") {-+			} else if (c[i] == "C") { 				while (is_point (c[i + 1])) {-					command[ci++] = "C";-					+					bezier_points[bi].type = 'C';+					bezier_points[bi].svg_type = 'C';+										 					cx = parse_double (c[++i]); 					 					if (svg_glyph) {@@ -466,8 +467,8 @@ public class ImportSvg { 						cy = -parse_double (c[++i]); 					} 									-					p[pi++] = cx;-					p[pi++] = cy;+					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy; 					 					cx = parse_double (c[++i]); 					@@ -480,9 +481,9 @@ public class ImportSvg { 					px2 = cx; 					py2 = cy; 					-					p[pi++] = cx;-					p[pi++] = cy;-					+					bezier_points[bi].x1 = cx;+					bezier_points[bi].y1 = cy;+										 					cx = parse_double (c[++i]); 					 					if (svg_glyph) {@@ -491,19 +492,19 @@ public class ImportSvg { 						cy = -parse_double (c[++i]); 					} 					-					p[pi++] = cx;-					p[pi++] = cy;-					+					bezier_points[bi].x2 = cx;+					bezier_points[bi].y2 = cy;+										 					px = cx;-					py = cy;					+					py = cy;+					+					bi++;				 				}	-			}--			// quadratic-			if (c[i] == "q") {+			} else if (c[i] == "q") { 				while (is_point (c[i + 1])) {-					command[ci++] = "Q";-					+					bezier_points[bi].type = 'Q';+					bezier_points[bi].svg_type = 'q';+										 					cx = px + parse_double (c[++i]); 					 					if (svg_glyph) {@@ -512,9 +513,9 @@ public class ImportSvg { 						cy = py + -parse_double (c[++i]); 					} 					-					p[pi++] = cx;-					p[pi++] = cy;-					+					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+										 					px2 = cx; 					py2 = cy; 										@@ -526,19 +527,20 @@ public class ImportSvg { 						cy = py + -parse_double (c[++i]); 					} 					-					p[pi++] = cx;-					p[pi++] = cy;-					+					bezier_points[bi].x1 = cx;+					bezier_points[bi].y1 = cy;+										 					px = cx; 					py = cy;+					+					bi++; 				}-			}-			-			if (c[i] == "Q") {+			} else if (c[i] == "Q") {  				while (is_point (c[i + 1])) {-					command[ci++] = "Q";-					+					bezier_points[bi].type = 'Q';+					bezier_points[bi].svg_type = 'Q';+										 					cx = parse_double (c[++i]); 					 					if (svg_glyph) {@@ -547,9 +549,9 @@ public class ImportSvg { 						cy = -parse_double (c[++i]); 					} 					-					p[pi++] = cx;-					p[pi++] = cy;-+					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+					 					px2 = cx; 					py2 = cy; 										@@ -561,25 +563,26 @@ public class ImportSvg { 						cy = -parse_double (c[++i]); 					} 					-					p[pi++] = cx;-					p[pi++] = cy;-					 					px = cx;-					py = cy;			-				}	-			}+					py = cy; -			if (c[i] == "t") {+					bezier_points[bi].x1 = cx;+					bezier_points[bi].y1 = cy;+										+					bi++;					+				}	+			} else if (c[i] == "t") { 				while (is_point (c[i + 1])) {-					command[ci++] = "Q";-					+					bezier_points[bi].type = 'Q';+					bezier_points[bi].svg_type = 't';+										 					// the first point is the reflection 					cx = 2 * px - px2; 					cy = 2 * py - py2; // if (svg_glyph) ? 					-					p[pi++] = cx;-					p[pi++] = cy;-+					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+					 					px2 = cx; 					py2 = cy; 										@@ -594,21 +597,23 @@ public class ImportSvg { 					px = cx; 					py = cy; 					-					p[pi++] = px;-					p[pi++] = py;+					bezier_points[bi].x1 = px;+					bezier_points[bi].y1 = py;+										+					bi++; 				}-			}--			if (c[i] == "T") {+			} else if (c[i] == "T") { 				while (is_point (c[i + 1])) {-					command[ci++] = "Q";-					+					bezier_points[bi].type = 'Q';+					bezier_points[bi].svg_type = 'T';+										 					// the reflection 					cx = 2 * px - px2; 					cy = 2 * py - py2; // if (svg_glyph) ?-					p[pi++] = cx;-					p[pi++] = cy;-+					+					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+										 					px2 = cx; 					py2 = cy; 					@@ -623,21 +628,23 @@ public class ImportSvg { 					px = cx; 					py = cy; 					-					p[pi++] = px;-					p[pi++] = py;					+					bezier_points[bi].x1 = px;+					bezier_points[bi].y1 = py;+										+					bi++;				 				}-			}--			if (c[i] == "s") {+			} else if (c[i] == "s") { 				while (is_point (c[i + 1])) {-					command[ci++] = "C";-					+					bezier_points[bi].type = 'C';+					bezier_points[bi].svg_type = 's';+										 					// the first point is the reflection 					cx = 2 * px - px2; 					cy = 2 * py - py2; // if (svg_glyph) ?-					p[pi++] = cx;-					p[pi++] = cy;-										++					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+															 					cx = px + parse_double (c[++i]); 					 					if (svg_glyph) {@@ -649,9 +656,9 @@ public class ImportSvg { 					px2 = cx; 					py2 = cy; 					-					p[pi++] = px2;-					p[pi++] = py2;-+					bezier_points[bi].x1 = px2;+					bezier_points[bi].y1 = py2;+					 					cx = px + parse_double (c[++i]); 					 					if (svg_glyph) {@@ -660,24 +667,26 @@ public class ImportSvg { 						cy = py + -parse_double (c[++i]); 					} 					-					p[pi++] = cx;-					p[pi++] = cy;-							+					bezier_points[bi].x2 = cx;+					bezier_points[bi].y2 = cy;+												 					px = cx; 					py = cy;+					+					bi++; 				}-			}--			if (c[i] == "S") {+			} else if (c[i] == "S") { 				while (is_point (c[i + 1])) {-					command[ci++] = "C";-					+					bezier_points[bi].type = 'C';+					bezier_points[bi].svg_type = 'S';+										 					// the reflection 					cx = 2 * px - px2; 					cy = 2 * py - py2; // if (svg_glyph) ?			-					p[pi++] = cx;-					p[pi++] = cy; +					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+					 					// the other two are regular cubic points 					cx = parse_double (c[++i]); 					@@ -690,8 +699,8 @@ public class ImportSvg { 					px2 = cx; 					py2 = cy; 					-					p[pi++] = px2;-					p[pi++] = py2;+					bezier_points[bi].x1 = px2;+					bezier_points[bi].y1 = py2; 					 					cx = parse_double (c[++i]); 					@@ -701,172 +710,312 @@ public class ImportSvg { 						cy = -parse_double (c[++i]); 					} 					-					p[pi++] = cx;-					p[pi++] = cy;+					bezier_points[bi].x2 = cx;+					bezier_points[bi].y2 = cy; 					 					px = cx;-					py = cy;					+					py = cy;	+					+					bi++;				 				}-			}-						-			if (c[i] == "z") {-				command[ci++] = "z";-			}--			if (c[i] == "Z") {-				command[ci++] = "z";+			} else if (c[i] == "z") {+				bezier_points[bi].type = 'z';+				bezier_points[bi].svg_type = 'z';+				+				bi++;+			} else if (c[i] == "Z") {+				bezier_points[bi].type = 'z';+				bezier_points[bi].svg_type = 'z';+									+				bi++;+			} else if (c[i] == "") {+			} else if (c[i] == " ") {+			} else {+				warning (@"Unknown instruction: $(c[i])"); 			} 		} -		Path path = new Path ();+		move_and_resize (bezier_points, bi, svg_glyph, units, glyph); -		// resize all points-		for (int i = 0; i < pi; i++) {-			p[i] *= units;+		if (format == SvgFormat.ILLUSTRATOR) {+			path_list = create_paths_inkscape (bezier_points, bi);+		} else {+			path_list = create_paths_illustrator (bezier_points, bi); 		}-					-		// move and resize-		if (svg_glyph) {-			// move only y -			for (int i = 0; i < pi; i += 2) {-				p[i] += glyph.left_limit;-				p[i+1] += font.base_line;++		// TODO: Find out if it is possible to tie handles.+		return path_list;+	}++	void move_and_resize (BezierPoints[] b, int num_b, bool svg_glyph, double units, Glyph glyph) {+		Font font = BirdFont.get_current_font ();+		+		for (int i = 0; i < num_b; i++) {+			// resize all points+			b[i].x0 *= units;+			b[i].y0 *= units;+			b[i].x1 *= units;+			b[i].y1 *= units;+			b[i].x2 *= units;+			b[i].y2 *= units;++			// move and resize+			if (svg_glyph) {+				// move only y +				b[i].x0 += glyph.left_limit;+				b[i].y0 += font.base_line;+				b[i].x1 += glyph.left_limit;+				b[i].y1 += font.base_line;+				b[i].x2 += glyph.left_limit;+				b[i].y2 += font.base_line;+			} else {+				b[i].x0 += glyph.left_limit;+				b[i].y0 += font.top_position;+				b[i].x1 += glyph.left_limit;+				b[i].y1 += font.top_position;+				b[i].x2 += glyph.left_limit;+				b[i].y2 += font.top_position; 			}-		} else {-			for (int i = 0; i < pi; i += 2) {-				p[i] += glyph.left_limit;-				p[i+1] += font.top_position;+		}+	}+	+	void find_last_handle (int start_index, BezierPoints[] b, int num_b, out double left_x, out double left_y, out PointType last_type) {+		BezierPoints last = new BezierPoints ();+		+		left_x = 0;+		left_y = 0;+		last_type = PointType.NONE;+		+		for (int i = start_index; i < num_b; i++) {+			switch (b[i].type) {+				case 'Q':+					break;+				case 'C':+					break;+				case 'z':+					if (b[i - 1].type == 'Q') {+						return_if_fail (i >= 1);+						warning ("Unexpected qubic points in Inkscape SVG");+						left_x = b[i - 1].x0;+						left_y = b[i - 1].y0;+						last_type = PointType.QUADRATIC;+					} else if (b[i - 1].type == 'C') {+						return_if_fail (i >= 1);+						left_x = b[i - 1].x1;+						left_y = b[i - 1].y1;+						last_type = PointType.CUBIC;+					} else if (b[i - 1].type == 'L' || last.type == 'M') {+						return_if_fail (i >= 2); // FIXME: -2 can be C or L+						left_x = b[i - 2].x0 + (b[i - 1].x0 - b[i - 2].x0) / 3.0;+						left_y = b[i - 2].y0 + (b[i - 1].y0 - b[i - 2].y0) / 3.0;+						last_type = PointType.LINE_CUBIC;+					} else {+						warning (@"Unexpected type. $(b[i - 1])\n");+					}+					return;+				default:+					break; 			}+			+			last = b[i];+		}+		+		warning ("Expecting z");+	}++	PathList create_paths_inkscape (BezierPoints[] b, int num_b) {+		double last_x;+		double last_y; +		PointType last_type;+		Path path;+		PathList path_list = new PathList ();+		EditPoint ep = new EditPoint ();+		+		path = new Path ();+		+		if (num_b == 0) {+			warning ("No SVG data");+			return path_list;+		}++		if (b[0].type != 'M') {+			warning ("Path must begin with M or m.");+			return path_list; 		} 		-		// add points-		int ic = 0;-		double x0, x1, x2;-		double y0, y1, y2;-		EditPoint ep1, ep2;-		double lx, ly;-		for (int i = 0; i < ci; i++) {-			if (is_null (command[i]) || command[i] == "") {+		find_last_handle (0, b, num_b, out last_x, out last_y, out last_type);++		for (int i = 0; i < num_b; i++) {+			if (b[i].type == '\0') { 				warning ("Parser error."); 				return path_list; 			}-			-			if (command[i] == "M") {-				path.add (p[ic++], p[ic++]);-			}-			-			if (command[i] == "L") {-				path.add (p[ic++], p[ic++]);-				ep1 = path.points.last ().data;-				ep1.recalculate_linear_handles ();-			}-			-			if (command[i] == "Q") {-				x0 = p[ic++];-				y0 = p[ic++];-				x1 = p[ic++];-				y1 = p[ic++];--				if (is_null (path.points.last ().data)) {-					warning ("Paths must begin with M");-					return path_list;-				}++			if (b[i].type == 'z') {+				path.close ();+				path.create_list ();+				path.recalculate_linear_handles ();+				path_list.add (path);+				path = new Path (); 				-				ep1 = path.points.last ().data;-				ep1.recalculate_linear_handles ();-				ep1.get_right_handle ().type = PointType.QUADRATIC;-				ep1.get_right_handle ().move_to_coordinate (x0, y0);-				ep1.type = PointType.QUADRATIC;		--				path.add (x1, y1);--				ep2 = path.points.last ().data;-				ep2.recalculate_linear_handles ();-				ep2.get_left_handle ().type = PointType.QUADRATIC;-				ep2.get_left_handle ().move_to_coordinate (x0, y0);-				ep2.type = PointType.QUADRATIC;		+				if (i + 1 >= num_b) {+					break;+				} else {+					find_last_handle (i + 1, b, num_b, out last_x, out last_y, out last_type);+				} 			}+	+			return_val_if_fail (i + 1 < num_b, path_list);+	+			if (b[i].type == 'M') {+				ep = path.add (b[i].x0, b[i].y0);+				ep.set_point_type (PointType.CUBIC); +				ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);+				+				if (i == 0) {+					ep.get_left_handle ().set_point_type (last_type);+					ep.get_left_handle ().move_to_coordinate (last_x, last_y);+				} else {+					if (b[i - 1].type == 'C') {+						ep.get_left_handle ().set_point_type (PointType.CUBIC);+						ep.get_left_handle ().move_to_coordinate (b[i + 1].x1, b[i + 1].y1);+					} +					+					if (b[i + 1].type == 'C') {+						ep.get_right_handle ().set_point_type (PointType.CUBIC);+						ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);+					} else if (b[i + 1].type == 'L' || b[i + 1].type == 'M') {+						ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);					+					}+				}+			} -			// all the (ep2.get_left_handle ().length == 0) is needed to parse corners in illustrator correctly -			// it should be a different point type.			-			if (command[i] == "C") {-				x0 = p[ic++];-				y0 = p[ic++];-				x1 = p[ic++];-				y1 = p[ic++];-				x2 = p[ic++];-				y2 = p[ic++];+			if (b[i].type == 'L') {+				return_val_if_fail (i != 0, path_list); 				-				if (is_null (path.points.last ().data)) {-					warning ("Paths must begin with M");-					return path_list;+				ep = path.add (b[i].x0, b[i].y0);+				ep.set_point_type (PointType.CUBIC);+				ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);+				ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);++				if (b[i + 1].type == 'L' || b[i + 1].type == 'M' || b[i + 1].type == 'z') {+					ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);+				}++				if (b[i -1].type == 'L' || b[i - 1].type == 'M') {+					ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC); 				}+			}+			+			if (b[i].type == 'Q') {+				warning ("Inkscape does not support quadratic control points.");+			}+	+			if (b[i].type == 'C') {+				return_val_if_fail (i != 0, path_list); -				// start with line handles-				ep1 = path.points.last ().data;+				ep.set_point_type (PointType.CUBIC); 				-				if (ep1.get_right_handle ().length != 0) {-					ep1.get_right_handle ().type = PointType.LINE_CUBIC;-					ep1.type = PointType.LINE_CUBIC;-					-					lx = ep1.x + ((x2 - ep1.x) / 3);-					ly = ep1.y + ((y2 - ep1.y) / 3);-													-					ep1.get_right_handle ().move_to_coordinate (lx, ly);-					ep1.recalculate_linear_handles ();-				}			+				ep.get_right_handle ().set_point_type (PointType.CUBIC);+				ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0); 				-				// set curve handles-				ep1 = path.points.last ().data;-				if (ep1.get_right_handle ().length != 0 || path.points.length () == 1) {-					ep1.recalculate_linear_handles ();-					ep1.get_right_handle ().type = PointType.CUBIC;-					ep1.get_right_handle ().move_to_coordinate (x0, y0);-					ep1.type = PointType.CUBIC;-				} else {-					ep1.get_right_handle ().type = PointType.CUBIC;-					ep1.get_right_handle ().length = 0;+				if (b[i + 1].type != 'z') {+					ep = path.add (b[i].x2, b[i].y2);++					ep.get_left_handle ().set_point_type (PointType.CUBIC);+					ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1); 				}+			}+		}+		+		if (path.points.length () > 0) {+			warning ("Open path.");+			path_list.add (path);+		}+		+		return path_list;+	} -				path.add (x2, y2);-					-				ep2 = path.points.last ().data;-				ep2.recalculate_linear_handles ();-				ep2.type = PointType.CUBIC;-				ep2.get_left_handle ().type = PointType.CUBIC;+	PathList create_paths_illustrator (BezierPoints[] b, int num_b) {+		double first_x = 0;+		double first_y = 0; +		Path path;+		PathList path_list = new PathList ();+		EditPoint ep;+		bool first_point = true;+		+		path = new Path ();+		+		if (num_b == 0) {+			warning ("No SVG data");+			return path_list;+		}+		+		for (int i = 0; i < num_b; i++) {+			if (b[i].type == '\0') {+				warning ("Parser error.");+				return path_list;+			} else if (b[i].type == 'z') {+				path.close ();+				path.create_list ();+				path.recalculate_linear_handles ();+				path_list.add (path);+				path = new Path ();+				first_point = true;+			} else if (b[i].type == 'M') {+				first_x = b[i].x0;+				first_y = b[i].y0;+			}  else if (b[i].type == 'L') {+				ep = path.add (b[i].x0, b[i].y0);+				ep.set_point_type (PointType.LINE_CUBIC); // TODO: quadratic+				ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC); 				-				ep2.get_left_handle ().move_to_coordinate (x1, y1);+				if (b[i -1].type == 'L' || first_point) {+					ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);+				} 				-				if (ep2.get_left_handle ().length == 0) {-					ep2.get_right_handle ().length = 0;+				if (b[i + 1].type == 'C') {+					ep.get_right_handle ().set_point_type (PointType.CUBIC);+					ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0); 				}-			}-			-			if (command[i] == "z") {-				// last point is first-				ep1 = path.points.last ().data;-				ep2 = path.points.first ().data;--				if (ep1.x == ep2.x && ep1.y == ep2.y) {-					ep2.left_handle.angle = ep1.left_handle.angle;-					ep2.left_handle.length = ep1.left_handle.length;-					ep2.left_handle.type = ep1.left_handle.type;-					path.points.remove_link (path.points.last ());+				+				first_point = false;+			} else if (b[i].type == 'Q') {+				warning ("Illustrator does not support quadratic control points.");+				print (@"$(b[i])\n");+			} else if (b[i].type == 'C') {+				if (first_point) {+					first_x = b[i].x0;+					first_y = b[i].y0; 				}+		+				ep = path.add (b[i].x2, b[i].y2);+				ep.set_point_type (PointType.CUBIC); -				glyph.add_path (path);-				glyph.close_path ();+				ep.get_right_handle ().set_point_type (PointType.CUBIC);+				ep.get_left_handle ().set_point_type (PointType.CUBIC); 				-				path_list.paths.append (path);-				path = new Path ();+				ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1);+				ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);+				+				first_point = false;+			} else {+				warning ("Unknown control point type.");+				print (@"$(b[i])\n"); 			} 		}-	-		// TODO: Find out if it is possible to tie handles.+		+		if (path.points.length () > 0) {+			warning ("Open path.");+			path_list.add (path);+		}+		 		return path_list; 	} 	+	// TODO: implement a standard svg parser+	 	static double parse_double (string? s) { 		if (is_null (s)) { 			warning ("Got null instead of expected string.");@@ -890,7 +1039,7 @@ public class ImportSvg { 		return double.try_parse ((!) s); 	} 	-	static Path parse_polygon_data (string polygon_points, Glyph glyph) {+	Path parse_polygon_data (string polygon_points, Glyph glyph) { 		string data = add_separators (polygon_points); 		string[] c = data.split (" "); 		Path path = new Path ();@@ -900,10 +1049,27 @@ public class ImportSvg { 		} 		 		glyph.add_path (path);+		path.close ();+		path.create_list (); 		glyph.close_path (); 		 		return path; 	}++	private class BezierPoints {+		public unichar type = '\0';+		public unichar svg_type = '\0';+		public double x0  = 0;+		public double y0 = 0;+		public double x1 = 0;+		public double y1 = 0;+		public double x2 = 0;+		public double y2 = 0;+		+		public string to_string () {+			return @"$((!)type.to_string ()) $x0,$y0 $x1,$y1 $x2,$y2 SVG:$((!)svg_type.to_string ())";+		}+	} }  }diff --git a/libbirdfont/OpenFontFormat/KernTable.vala b/libbirdfont/OpenFontFormat/KernTable.valaindex a77339b..46ff9d9 100644--- a/libbirdfont/OpenFontFormat/KernTable.vala+++ b/libbirdfont/OpenFontFormat/KernTable.vala@@ -168,13 +168,6 @@ public class KernTable : Table { 		fd.pad (); 		this.font_data = fd; 	}--	void print_all () {-		print ("All pairs in kern table:\n");-		foreach (Kern k in kerning) {-			print (@"$(k.left)  $(k.right)  $(k.kerning)\n");-		}-	} }  diff --git a/libbirdfont/SvgFont.vala b/libbirdfont/SvgFont.valaindex b0143c2..4e87270 100644--- a/libbirdfont/SvgFont.vala+++ b/libbirdfont/SvgFont.vala@@ -233,6 +233,7 @@ class SvgFont : GLib.Object { 		Glyph glyph; 		double advance = font_advance; 		string ligature = "";+		ImportSvg parser = new ImportSvg ();  		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) { 			attr_name = prop->name;@@ -265,7 +266,7 @@ class SvgFont : GLib.Object { 		}  		glyph = new Glyph (glyph_name, unicode_value);-		ImportSvg.parse_svg_data (svg, glyph, true, units);			+		parser.parse_svg_data (svg, glyph, true, units);			 		glyph.right_limit = glyph.left_limit + advance * units; 		 		if (ligature != "") {diff --git a/libbirdfont/TestCases.vala b/libbirdfont/TestCases.valaindex e33d577..9cea8a7 100644--- a/libbirdfont/TestCases.vala+++ b/libbirdfont/TestCases.vala@@ -116,15 +116,31 @@ class TestCases {  	public static void test_parse_quadratic_paths () { 		Glyph g;+		ImportSvg parser = new ImportSvg ();+		 		test_open_next_glyph (); 		+		parser.set_format (SvgFormat.INKSCAPE);+		 		g = MainWindow.get_current_glyph ();-		ImportSvg.parse_svg_data ("M20,300 Q400,50 600,300 T1000,300Q1200 50 1400 300Q1600 50 1800 600 L 1800 700 L 200 700 z", g);+		parser.parse_svg_data ("M20,300 Q400,50 600,300 T1000,300Q1200 50 1400 300Q1600 50 1800 600 L 1800 700 L 200 700 z", g); 		Toolbox.select_tool_by_name ("full_glyph"); 		 		g = MainWindow.get_current_glyph ();-		ImportSvg.parse_svg_data ("M300 400 h-200 l0 1000 h200z", g, true);+		parser.parse_svg_data ("M300 400 h-200 l0 1000 h200z", g, true); 		Toolbox.select_tool_by_name ("full_glyph");+++		parser.set_format (SvgFormat.ILLUSTRATOR);+		+		g = MainWindow.get_current_glyph ();+		parser.parse_svg_data ("M20,300 Q400,50 600,300 T1000,300Q1200 50 1400 300Q1600 50 1800 600 L 1800 700 L 200 700 z", g);+		Toolbox.select_tool_by_name ("full_glyph");+		+		g = MainWindow.get_current_glyph ();+		parser.parse_svg_data ("M300 400 h-200 l0 1000 h200z", g, true);+		Toolbox.select_tool_by_name ("full_glyph");+		 	}  	public static void test_illustrator_import () {

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112