diff --git a/svgpathtools/__init__.py b/svgpathtools/__init__.py index f043731..7e5da65 100644 --- a/svgpathtools/__init__.py +++ b/svgpathtools/__init__.py @@ -17,6 +17,6 @@ from .document import (Document, CONVERSIONS, CONVERT_ONLY_PATHS, from .svg_io_sax import SaxDocument try: - from .svg_to_paths import svg2paths, svg2paths2 + from .svg_to_paths import svg2paths, svg2paths2, svgstr2paths except ImportError: pass diff --git a/svgpathtools/document.py b/svgpathtools/document.py index 1dd9077..3b98599 100644 --- a/svgpathtools/document.py +++ b/svgpathtools/document.py @@ -41,6 +41,7 @@ import xml.etree.ElementTree as etree from xml.etree.ElementTree import Element, SubElement, register_namespace from xml.dom.minidom import parseString import warnings +from io import StringIO from tempfile import gettempdir from time import time @@ -235,13 +236,19 @@ class Document: The output Path objects will be transformed based on their parent groups. Args: - filepath (str): The filepath of the DOM-style object. + filepath (str or file-like): The filepath of the + DOM-style object or a file-like object containing it. """ + self.original_filepath = None - # remember location of original svg file - self.original_filepath = filepath - if filepath is not None and os.path.dirname(filepath) == '': - self.original_filepath = os.path.join(os.getcwd(), filepath) + # strings are interpreted as file location everything else is treated as + # file-like object and passed to the xml parser directly + if isinstance(filepath, str): + # remember location of original svg file if any + self.original_filepath = filepath + if os.path.dirname(filepath) == '': + self.original_filepath = os.path.join( + os.getcwd(), filepath) if filepath is None: self.tree = etree.ElementTree(Element('svg')) @@ -251,6 +258,16 @@ class Document: self.root = self.tree.getroot() + @classmethod + def from_svg_string(cls, svg_string): + """Factory method for creating a document from a string holding a svg + object + """ + # wrap string into StringIO object + svg_file_obj = StringIO(svg_string) + # create document from file object + return Document(svg_file_obj) + def paths(self, group_filter=lambda x: True, path_filter=lambda x: True, path_conversions=CONVERSIONS): """Returns a list of all paths in the document. diff --git a/svgpathtools/svg_to_paths.py b/svgpathtools/svg_to_paths.py index f4ca07b..dec0e44 100644 --- a/svgpathtools/svg_to_paths.py +++ b/svgpathtools/svg_to_paths.py @@ -5,6 +5,7 @@ The main tool being the svg2paths() function.""" from __future__ import division, absolute_import, print_function from xml.dom.minidom import parse from os import path as os_path, getcwd +from io import StringIO import re # Internal dependencies @@ -144,7 +145,9 @@ def svg2paths(svg_file_location, SVG Path, Line, Polyline, Polygon, Circle, and Ellipse elements. Args: - svg_file_location (string): the location of the svg file + svg_file_location (string or file-like object): the location of the + svg file on disk or a file-like object containing the content of a + svg file return_svg_attributes (bool): Set to True and a dictionary of svg-attributes will be extracted and returned. See also the `svg2paths2()` function. @@ -168,8 +171,12 @@ def svg2paths(svg_file_location, list: The list of corresponding path attribute dictionaries. dict (optional): A dictionary of svg-attributes (see `svg2paths2()`). """ - if os_path.dirname(svg_file_location) == '': - svg_file_location = os_path.join(getcwd(), svg_file_location) + # strings are interpreted as file location everything else is treated as + # file-like object and passed to the xml parser directly + if isinstance(svg_file_location, str): + if os_path.dirname(svg_file_location) == '': + svg_file_location = os_path.join( + getcwd(), svg_file_location) doc = parse(svg_file_location) @@ -249,3 +256,26 @@ def svg2paths2(svg_file_location, convert_polylines_to_paths=convert_polylines_to_paths, convert_polygons_to_paths=convert_polygons_to_paths, convert_rectangles_to_paths=convert_rectangles_to_paths) + + +def svgstr2paths(svg_string, + return_svg_attributes=False, + convert_circles_to_paths=True, + convert_ellipses_to_paths=True, + convert_lines_to_paths=True, + convert_polylines_to_paths=True, + convert_polygons_to_paths=True, + convert_rectangles_to_paths=True): + """Convenience function; identical to svg2paths() except that it takes the + svg object as string. See svg2paths() docstring for more + info.""" + # wrap string into StringIO object + svg_file_obj = StringIO(svg_string) + return svg2paths(svg_file_location=svg_file_obj, + return_svg_attributes=return_svg_attributes, + convert_circles_to_paths=convert_circles_to_paths, + convert_ellipses_to_paths=convert_ellipses_to_paths, + convert_lines_to_paths=convert_lines_to_paths, + convert_polylines_to_paths=convert_polylines_to_paths, + convert_polygons_to_paths=convert_polygons_to_paths, + convert_rectangles_to_paths=convert_rectangles_to_paths) diff --git a/test/test_document.py b/test/test_document.py new file mode 100644 index 0000000..d32bb3b --- /dev/null +++ b/test/test_document.py @@ -0,0 +1,44 @@ +from __future__ import division, absolute_import, print_function +import unittest +from svgpathtools import * +from io import StringIO +from io import open # overrides build-in open for compatibility with python2 +from os.path import join, dirname + +class TestDocument(unittest.TestCase): + def test_from_file_path(self): + """ Test reading svg from file provided as path """ + doc = Document(join(dirname(__file__), 'polygons.svg')) + + self.assertEqual(len(doc.paths()), 2) + + def test_from_file_object(self): + """ Test reading svg from file object that has already been opened """ + with open(join(dirname(__file__), 'polygons.svg'), 'r') as file: + doc = Document(file) + + self.assertEqual(len(doc.paths()), 2) + + def test_from_stringio(self): + """ Test reading svg object contained in a StringIO object """ + with open(join(dirname(__file__), 'polygons.svg'), + 'r', encoding='utf-8') as file: + # read entire file into string + file_content = file.read() + # prepare stringio object + file_as_stringio = StringIO(file_content) + + doc = Document(file_as_stringio) + + self.assertEqual(len(doc.paths()), 2) + + def test_from_string(self): + """ Test reading svg object contained in a string""" + with open(join(dirname(__file__), 'polygons.svg'), + 'r', encoding='utf-8') as file: + # read entire file into string + file_content = file.read() + + doc = Document.from_svg_string(file_content) + + self.assertEqual(len(doc.paths()), 2) diff --git a/test/test_svg2paths.py b/test/test_svg2paths.py index 5a0dab0..67a5751 100644 --- a/test/test_svg2paths.py +++ b/test/test_svg2paths.py @@ -1,6 +1,8 @@ from __future__ import division, absolute_import, print_function import unittest from svgpathtools import * +from io import StringIO +from io import open # overrides build-in open for compatibility with python2 from os.path import join, dirname from svgpathtools.svg_to_paths import rect2pathd @@ -57,4 +59,41 @@ class TestSVG2Paths(unittest.TestCase): non_rounded = {"x":"10", "y":"10", "width":"100","height":"100"} self.assertEqual(rect2pathd(non_rounded), 'M10.0 10.0 L 110.0 10.0 L 110.0 110.0 L 10.0 110.0 z') rounded = {"x":"10", "y":"10", "width":"100","height":"100", "rx":"15", "ry": "12"} - self.assertEqual(rect2pathd(rounded), "M 25.0 10.0 L 95.0 10.0 A 15.0 12.0 0 0 1 110.0 22.0 L 110.0 98.0 A 15.0 12.0 0 0 1 95.0 110.0 L 25.0 110.0 A 15.0 12.0 0 0 1 10.0 98.0 L 10.0 22.0 A 15.0 12.0 0 0 1 25.0 10.0 z") \ No newline at end of file + self.assertEqual(rect2pathd(rounded), "M 25.0 10.0 L 95.0 10.0 A 15.0 12.0 0 0 1 110.0 22.0 L 110.0 98.0 A 15.0 12.0 0 0 1 95.0 110.0 L 25.0 110.0 A 15.0 12.0 0 0 1 10.0 98.0 L 10.0 22.0 A 15.0 12.0 0 0 1 25.0 10.0 z") + + def test_from_file_path(self): + """ Test reading svg from file provided as path """ + paths, _ = svg2paths(join(dirname(__file__), 'polygons.svg')) + + self.assertEqual(len(paths), 2) + + def test_from_file_object(self): + """ Test reading svg from file object that has already been opened """ + with open(join(dirname(__file__), 'polygons.svg'), 'r') as file: + paths, _ = svg2paths(file) + + self.assertEqual(len(paths), 2) + + def test_from_stringio(self): + """ Test reading svg object contained in a StringIO object """ + with open(join(dirname(__file__), 'polygons.svg'), + 'r', encoding='utf-8') as file: + # read entire file into string + file_content = file.read() + # prepare stringio object + file_as_stringio = StringIO(file_content) + + paths, _ = svg2paths(file_as_stringio) + + self.assertEqual(len(paths), 2) + + def test_from_string(self): + """ Test reading svg object contained in a string """ + with open(join(dirname(__file__), 'polygons.svg'), + 'r', encoding='utf-8') as file: + # read entire file into string + file_content = file.read() + + paths, _ = svgstr2paths(file_content) + + self.assertEqual(len(paths), 2)