diff --git a/svgpathtools/__init__.py b/svgpathtools/__init__.py
index 109374a..bb01801 100644
--- a/svgpathtools/__init__.py
+++ b/svgpathtools/__init__.py
@@ -13,6 +13,7 @@ from .polytools import polyroots, polyroots01, rational_limit, real, imag
from .misctools import hex2rgb, rgb2hex
from .smoothing import smoothed_path, smoothed_joint, is_differentiable, kinks
from .document import Document, CONVERSIONS, CONVERT_ONLY_PATHS, SVG_GROUP_TAG, SVG_NAMESPACE
+from .svg_io_sax import SaxDocument
try:
from .svg_to_paths import svg2paths, svg2paths2
diff --git a/svgpathtools/svg_io_sax.py b/svgpathtools/svg_io_sax.py
new file mode 100644
index 0000000..1c46f51
--- /dev/null
+++ b/svgpathtools/svg_io_sax.py
@@ -0,0 +1,198 @@
+"""(Experimental) replacement for import/export functionality SAX
+
+"""
+
+# External dependencies
+from __future__ import division, absolute_import, print_function
+import os
+from xml.etree.ElementTree import iterparse, Element, ElementTree, SubElement
+
+# Internal dependencies
+from .parser import parse_path
+from .parser import parse_transform
+from .svg_to_paths import (path2pathd, ellipse2pathd, line2pathd,
+ polyline2pathd, polygon2pathd, rect2pathd)
+from .misctools import open_in_browser
+from .path import *
+
+# To maintain forward/backward compatibility
+try:
+ str = basestring
+except NameError:
+ pass
+
+NAME_SVG = "svg"
+ATTR_VERSION = "version"
+VALUE_SVG_VERSION = "1.1"
+ATTR_XMLNS = "xmlns"
+VALUE_XMLNS = "http://www.w3.org/2000/svg"
+ATTR_XMLNS_LINK = "xmlns:xlink"
+VALUE_XLINK = "http://www.w3.org/1999/xlink"
+ATTR_XMLNS_EV = "xmlns:ev"
+VALUE_XMLNS_EV = "http://www.w3.org/2001/xml-events"
+ATTR_WIDTH = "width"
+ATTR_HEIGHT = "height"
+ATTR_VIEWBOX = "viewBox"
+NAME_PATH = "path"
+ATTR_DATA = "d"
+ATTR_FILL = "fill"
+ATTR_STROKE = "stroke"
+ATTR_STROKE_WIDTH = "stroke-width"
+ATTR_TRANSFORM = "transform"
+VALUE_NONE = "none"
+
+
+class SaxDocument:
+ def __init__(self, filename):
+ """A container for a SAX SVG light tree objects document.
+
+ This class provides functions for extracting SVG data into Path objects.
+
+ Args:
+ filename (str): The filename of the SVG file
+ """
+ self.root_values = {}
+ self.tree = []
+ # remember location of original svg file
+ if filename is not None and os.path.dirname(filename) == '':
+ self.original_filename = os.path.join(os.getcwd(), filename)
+ else:
+ self.original_filename = filename
+
+ if filename is not None:
+ self.sax_parse(filename)
+
+ def sax_parse(self, filename):
+ self.root_values = {}
+ self.tree = []
+ stack = []
+ values = {}
+ matrix = None
+ for event, elem in iterparse(filename, events=('start', 'end')):
+ if event == 'start':
+ stack.append((values, matrix))
+ if matrix is not None:
+ matrix = matrix.copy() # copy of matrix
+ current_values = values
+ values = {}
+ values.update(current_values) # copy of dictionary
+ attrs = elem.attrib
+ values.update(attrs)
+ name = elem.tag[28:]
+ if "style" in attrs:
+ for equate in attrs["style"].split(";"):
+ equal_item = equate.split(":")
+ values[equal_item[0]] = equal_item[1]
+ if "transform" in attrs:
+ transform_matrix = parse_transform(attrs["transform"])
+ if matrix is None:
+ matrix = np.identity(3)
+ matrix = transform_matrix.dot(matrix)
+ if "svg" == name:
+ current_values = values
+ values = {}
+ values.update(current_values)
+ self.root_values = current_values
+ continue
+ elif "g" == name:
+ continue
+ elif 'path' == name:
+ values['d'] = path2pathd(values)
+ elif 'circle' == name:
+ values["d"] = ellipse2pathd(values)
+ elif 'ellipse' == name:
+ values["d"] = ellipse2pathd(values)
+ elif 'line' == name:
+ values["d"] = line2pathd(values)
+ elif 'polyline' == name:
+ values["d"] = polyline2pathd(values['points'])
+ elif 'polygon' == name:
+ values["d"] = polygon2pathd(values['points'])
+ elif 'rect' == name:
+ values["d"] = rect2pathd(values)
+ else:
+ continue
+ values["matrix"] = matrix
+ values["name"] = name
+ self.tree.append(values)
+ else:
+ v = stack.pop()
+ values = v[0]
+ matrix = v[1]
+
+ def flatten_all_paths(self):
+ flat = []
+ for values in self.tree:
+ pathd = values['d']
+ matrix = values['matrix']
+ parsed_path = parse_path(pathd)
+ if matrix is not None:
+ transform(parsed_path, matrix)
+ flat.append(parsed_path)
+ return flat
+
+ def get_pathd_and_matrix(self):
+ flat = []
+ for values in self.tree:
+ pathd = values['d']
+ matrix = values['matrix']
+ flat.append((pathd, matrix))
+ return flat
+
+ def generate_dom(self):
+ root = Element(NAME_SVG)
+ root.set(ATTR_VERSION, VALUE_SVG_VERSION)
+ root.set(ATTR_XMLNS, VALUE_XMLNS)
+ root.set(ATTR_XMLNS_LINK, VALUE_XLINK)
+ root.set(ATTR_XMLNS_EV, VALUE_XMLNS_EV)
+ width = self.root_values.get(ATTR_WIDTH, None)
+ height = self.root_values.get(ATTR_HEIGHT, None)
+ if width is not None:
+ root.set(ATTR_WIDTH, width)
+ if height is not None:
+ root.set(ATTR_HEIGHT, height)
+ viewbox = self.root_values.get(ATTR_VIEWBOX, None)
+ if viewbox is not None:
+ root.set(ATTR_VIEWBOX, viewbox)
+ identity = np.identity(3)
+ for values in self.tree:
+ pathd = values.get('d', '')
+ matrix = values.get('matrix', None)
+ # path_value = parse_path(pathd)
+
+ path = SubElement(root, NAME_PATH)
+ if matrix is not None and not np.all(np.equal(matrix, identity)):
+ matrix_string = "matrix("
+ matrix_string += " "
+ matrix_string += str(matrix[0][0])
+ matrix_string += " "
+ matrix_string += str(matrix[1][0])
+ matrix_string += " "
+ matrix_string += str(matrix[0][1])
+ matrix_string += " "
+ matrix_string += str(matrix[1][1])
+ matrix_string += " "
+ matrix_string += str(matrix[0][2])
+ matrix_string += " "
+ matrix_string += str(matrix[1][2])
+ matrix_string += ")"
+ path.set(ATTR_TRANSFORM, matrix_string)
+ if ATTR_DATA in values:
+ path.set(ATTR_DATA, values[ATTR_DATA])
+ if ATTR_FILL in values:
+ path.set(ATTR_FILL, values[ATTR_FILL])
+ if ATTR_STROKE in values:
+ path.set(ATTR_STROKE, values[ATTR_STROKE])
+ return ElementTree(root)
+
+ def save(self, filename):
+ with open(filename, 'wb') as output_svg:
+ dom_tree = self.generate_dom()
+ dom_tree.write(output_svg)
+
+ def display(self, filename=None):
+ """Displays/opens the doc using the OS's default application."""
+ if filename is None:
+ filename = 'display_temp.svg'
+ self.save(filename)
+ open_in_browser(filename)
diff --git a/test/display_temp.svg b/test/display_temp.svg
new file mode 100644
index 0000000..a03b577
--- /dev/null
+++ b/test/display_temp.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/groups.svg b/test/groups.svg
index 1787617..119ff71 100644
--- a/test/groups.svg
+++ b/test/groups.svg
@@ -70,7 +70,8 @@
+ transform="
+ translate(20, 30)">
+
\ No newline at end of file