updated from master

preserve-order
Andrew Port 2022-07-09 18:19:17 -03:00
commit 64aa21684c
6 changed files with 203 additions and 25 deletions

View File

@ -3,7 +3,7 @@ import codecs
import os import os
VERSION = '1.4.4' VERSION = '1.5.0'
AUTHOR_NAME = 'Andy Port' AUTHOR_NAME = 'Andy Port'
AUTHOR_EMAIL = 'AndyAPort@gmail.com' AUTHOR_EMAIL = 'AndyAPort@gmail.com'
GITHUB = 'https://github.com/mathandy/svgpathtools' GITHUB = 'https://github.com/mathandy/svgpathtools'

View File

@ -17,6 +17,6 @@ from .document import (Document, CONVERSIONS, CONVERT_ONLY_PATHS,
from .svg_io_sax import SaxDocument from .svg_io_sax import SaxDocument
try: try:
from .svg_to_paths import svg2paths, svg2paths2 from .svg_to_paths import svg2paths, svg2paths2, svgstr2paths
except ImportError: except ImportError:
pass pass

View File

@ -41,6 +41,7 @@ import xml.etree.ElementTree as etree
from xml.etree.ElementTree import Element, SubElement, register_namespace from xml.etree.ElementTree import Element, SubElement, register_namespace
from xml.dom.minidom import parseString from xml.dom.minidom import parseString
import warnings import warnings
from io import StringIO
from tempfile import gettempdir from tempfile import gettempdir
from time import time from time import time
@ -54,9 +55,13 @@ from .path import *
# To maintain forward/backward compatibility # To maintain forward/backward compatibility
try: try:
str = basestring string = basestring
except NameError: except NameError:
pass string = str
try:
from os import PathLike
except ImportError:
PathLike = string
# Let xml.etree.ElementTree know about the SVG namespace # Let xml.etree.ElementTree know about the SVG namespace
SVG_NAMESPACE = {'svg': 'http://www.w3.org/2000/svg'} SVG_NAMESPACE = {'svg': 'http://www.w3.org/2000/svg'}
@ -235,13 +240,14 @@ class Document:
The output Path objects will be transformed based on their parent groups. The output Path objects will be transformed based on their parent groups.
Args: 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.
""" """
# remember location of original svg file # strings are interpreted as file location everything else is treated as
self.original_filepath = filepath # file-like object and passed to the xml parser directly
if filepath is not None and os.path.dirname(filepath) == '': from_filepath = isinstance(filepath, string) or isinstance(filepath, PathLike)
self.original_filepath = os.path.join(os.getcwd(), filepath) self.original_filepath = os.path.abspath(filepath) if from_filepath else None
if filepath is None: if filepath is None:
self.tree = etree.ElementTree(Element('svg')) self.tree = etree.ElementTree(Element('svg'))
@ -251,6 +257,14 @@ class Document:
self.root = self.tree.getroot() self.root = self.tree.getroot()
@classmethod
def from_svg_string(cls, svg_string):
"""Constructor for creating a Document object from a string."""
# 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, def paths(self, group_filter=lambda x: True,
path_filter=lambda x: True, path_conversions=CONVERSIONS): path_filter=lambda x: True, path_conversions=CONVERSIONS):
"""Returns a list of all paths in the document. """Returns a list of all paths in the document.
@ -263,7 +277,7 @@ class Document:
def paths_from_group(self, group, recursive=True, group_filter=lambda x: True, def paths_from_group(self, group, recursive=True, group_filter=lambda x: True,
path_filter=lambda x: True, path_conversions=CONVERSIONS): path_filter=lambda x: True, path_conversions=CONVERSIONS):
if all(isinstance(s, str) for s in group): if all(isinstance(s, string) for s in group):
# If we're given a list of strings, assume it represents a # If we're given a list of strings, assume it represents a
# nested sequence # nested sequence
group = self.get_group(group) group = self.get_group(group)
@ -308,7 +322,7 @@ class Document:
path_svg = path.d() path_svg = path.d()
elif is_path_segment(path): elif is_path_segment(path):
path_svg = Path(path).d() path_svg = Path(path).d()
elif isinstance(path, str): elif isinstance(path, string):
# Assume this is a valid d-string. # Assume this is a valid d-string.
# TODO: Should we sanity check the input string? # TODO: Should we sanity check the input string?
path_svg = path path_svg = path

View File

@ -4,10 +4,15 @@ The main tool being the svg2paths() function."""
# External dependencies # External dependencies
from __future__ import division, absolute_import, print_function from __future__ import division, absolute_import, print_function
from xml.dom.minidom import parse from xml.dom.minidom import parse
from os import path as os_path, getcwd import os
from io import StringIO
import re import re
from sys import version_info from sys import version_info
try:
from os import PathLike as FilePathLike
except ImportError:
FilePathLike = str
# Internal dependencies # Internal dependencies
from .parser import parse_path from .parser import parse_path
@ -146,18 +151,51 @@ parser_dict = {
} }
def svg2paths( def svg2paths(svg_file_location,
svg_file_location, return_svg_attributes=False,
return_svg_attributes=False, convert_circles_to_paths=True,
convert_circles_to_paths=True, convert_ellipses_to_paths=True,
convert_ellipses_to_paths=True, convert_lines_to_paths=True,
convert_lines_to_paths=True, convert_polylines_to_paths=True,
convert_polylines_to_paths=True, convert_polygons_to_paths=True,
convert_polygons_to_paths=True, convert_rectangles_to_paths=True):
convert_rectangles_to_paths=True, """Converts an SVG into a list of Path objects and attribute dictionaries.
):
if os_path.dirname(svg_file_location) == "": Converts an SVG file into a list of Path objects and a list of
svg_file_location = os_path.join(getcwd(), svg_file_location) dictionaries containing their attributes. This currently supports
SVG Path, Line, Polyline, Polygon, Circle, and Ellipse elements.
Args:
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.
convert_circles_to_paths: Set to False to exclude SVG-Circle
elements (converted to Paths). By default circles are included as
paths of two `Arc` objects.
convert_ellipses_to_paths (bool): Set to False to exclude SVG-Ellipse
elements (converted to Paths). By default ellipses are included as
paths of two `Arc` objects.
convert_lines_to_paths (bool): Set to False to exclude SVG-Line elements
(converted to Paths)
convert_polylines_to_paths (bool): Set to False to exclude SVG-Polyline
elements (converted to Paths)
convert_polygons_to_paths (bool): Set to False to exclude SVG-Polygon
elements (converted to Paths)
convert_rectangles_to_paths (bool): Set to False to exclude SVG-Rect
elements (converted to Paths).
Returns:
list: The list of Path objects.
list: The list of corresponding path attribute dictionaries.
dict (optional): A dictionary of svg-attributes (see `svg2paths2()`).
"""
# strings are interpreted as file location everything else is treated as
# file-like object and passed to the xml parser directly
from_filepath = isinstance(svg_file_location, str) or isinstance(svg_file_location, FilePathLike)
svg_file_location = os.path.abspath(svg_file_location) if from_filepath else svg_file_location
doc = parse(svg_file_location) doc = parse(svg_file_location)
@ -219,3 +257,26 @@ def svg2paths2(svg_file_location,
convert_polylines_to_paths=convert_polylines_to_paths, convert_polylines_to_paths=convert_polylines_to_paths,
convert_polygons_to_paths=convert_polygons_to_paths, convert_polygons_to_paths=convert_polygons_to_paths,
convert_rectangles_to_paths=convert_rectangles_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)

54
test/test_document.py Normal file
View File

@ -0,0 +1,54 @@
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 sys import version_info
class TestDocument(unittest.TestCase):
def test_from_file_path_string(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_path(self):
"""Test reading svg from file provided as path"""
if version_info >= (3, 6):
import pathlib
doc = Document(pathlib.Path(__file__).parent / '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)

View File

@ -1,10 +1,14 @@
from __future__ import division, absolute_import, print_function from __future__ import division, absolute_import, print_function
import unittest import unittest
from svgpathtools import * 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 os.path import join, dirname
from sys import version_info
from svgpathtools.svg_to_paths import rect2pathd from svgpathtools.svg_to_paths import rect2pathd
class TestSVG2Paths(unittest.TestCase): class TestSVG2Paths(unittest.TestCase):
def test_svg2paths_polygons(self): def test_svg2paths_polygons(self):
@ -57,4 +61,49 @@ class TestSVG2Paths(unittest.TestCase):
non_rounded = {"x":"10", "y":"10", "width":"100","height":"100"} 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') 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"} 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") 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_string(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_path(self):
"""Test reading svg from file provided as pathlib POSIXPath"""
if version_info >= (3, 6):
import pathlib
paths, _ = svg2paths(pathlib.Path(__file__).parent / '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)