-
Notifications
You must be signed in to change notification settings - Fork 3
/
svg_to_outlines.py
123 lines (99 loc) · 4.92 KB
/
svg_to_outlines.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
from xml.etree import ElementTree
from PySide6.QtGui import QGuiApplication
from PySide6.QtGui import QPainter
from PySide6.QtSvg import QSvgRenderer
from PySide6.QtCore import QXmlStreamReader
from svgoutline.svg_utils import (
namespaces,
get_svg_page_size,
lines_polylines_and_polygons_to_paths,
)
from svgoutline.outline_painter import OutlinePaintDevice
# Tell ElementTree to use the conventional namespace aliases for the basic
# namespaces used by SVG. This is not strictly necessary for generating valid
# SVG files but some incorrectly written clients may misbehave (notably Qt's
# QSvg) if these names are not used.
try:
register_xml_namespace = ElementTree.register_namespace
except AttributeError:
def register_xml_namespace(prefix, uri):
ElementTree._namespace_map[uri] = prefix
for prefix, uri in namespaces.items():
register_xml_namespace(prefix, uri)
def svg_to_outlines(root, width_mm=None, height_mm=None, pixels_per_mm=5.0):
"""
Given an SVG as a Python ElementTree, return a set of straight line
segments which approximate the outlines in that SVG when rendered.
Occlusion is not accounted for in the returned list of outlines. Even if
one shape is completely occluded by another, both of their outlines will be
reported. Simillarly, overlapping lines will also be passed through.
.. note::
This function internally uses the QSvg library from Qt to render the
provided SVG to a special painting backend which captures the output
lines. As a consequence, SVG feature support is as good or as bad as
that library. QSvg is generally regarded as a high quality and
relatively complete SVG implementation and so most SVG features should
be supported.
.. note::
Due to its internal use of Qt, a PySide6.QtGui.QGuiApplication will be
created if one has not already been created. Non-Qt users and most Qt
users should not be affected by this.
Parameters
----------
root : ElementTree
The SVG whose outlines should be extracted.
width_mm, height_mm : float or None
The page size to render the SVG at (in milimeters). If omitted, this
will be determined automatically from the SVG's width and height
attributes. These arguments are mandatory for SVGs lacking these
attributes.
pixels_per_mm : float
Curved outlines will be approximated by straight lines in the output.
This parameter controls how exactly curves will be approximated.
Specifically, the curve approximation will be at least fine enough for
rasterised versions of the lines to 'look right' at the specified pixel
density.
Returns
-------
[((r, g, b, a) or None, width, [(x, y), ...]), ...]
A list of polylines described by (colour, line) pairs.
The 'colour' values define the colour used to draw the line (if a solid
colour was used) or None (if a gradient or patterned stroke was used).
Colours are given as four-tuples in RGBA order with values from 0.0 to
1.0.
The 'width' value gives the line width used to draw the line (given in
mm). If the shape being drawn has a non-uniform scaling applied, this
value may not be meaningful and its actual value should be considered
undefined.
The 'line' part of each tuple defines the outline as a series of (x, y)
coordinates (given in mm) which describe a continuous polyline. These
polylines may be considered open. Closed lines in the input SVG will
result in polylines where the first and last coordinate are identical.
Lines may go beyond the bounds of the designated page size (as in the
input SVG).
"""
# This method internally uses various parts of Qt which require that a Qt
# application exists. If one does not exist, one will be created.
if QGuiApplication.instance() is None:
QGuiApplication()
# Determine the page size from the document if necessary
if width_mm is None or height_mm is None:
width_mm, height_mm = get_svg_page_size(root)
# Convert all <line>, <polyline> and <polygon> elements to <path>s to
# work-around PySide bug PYSIDE-891. (See comments in
# :py:mod:`svgoutline.outline_painter`.)
root = lines_polylines_and_polygons_to_paths(root)
# Load the SVG into QSvg
xml_stream_reader = QXmlStreamReader()
xml_stream_reader.addData(ElementTree.tostring(root, "unicode"))
svg_renderer = QSvgRenderer()
svg_renderer.load(xml_stream_reader)
# Paint the SVG into the OutlinePaintDevice which will capture the set of
# line segments which make up the SVG as rendered.
outline_paint_device = OutlinePaintDevice(width_mm, height_mm, pixels_per_mm)
painter = QPainter(outline_paint_device)
try:
svg_renderer.render(painter)
finally:
painter.end()
return outline_paint_device.getOutlines()