1- #!/usr/bin/env python
21"""A library for reading and converting SVG.
32
43This is a converter from SVG to RLG (ReportLab Graphics) drawings.
2423import shlex
2524import shutil
2625from collections import defaultdict , namedtuple
27- from io import BytesIO
2826from importlib .metadata import version
27+ from io import BytesIO
2928
3029from PIL import Image as PILImage
31-
32- from reportlab .pdfbase .pdfmetrics import stringWidth
33- from reportlab .pdfgen .canvas import FILL_EVEN_ODD , FILL_NON_ZERO
34- from reportlab .pdfgen .pdfimages import PDFImage
3530from reportlab .graphics .shapes import (
3631 _CLOSEPATH ,
3732 Circle ,
4136 Image ,
4237 Line ,
4338 Path ,
44- PolyLine ,
4539 Polygon ,
40+ PolyLine ,
4641 Rect ,
4742 SolidShape ,
4843 String ,
4944)
5045from reportlab .lib import colors
5146from reportlab .lib .units import pica , toLength
47+ from reportlab .pdfbase .pdfmetrics import stringWidth
48+ from reportlab .pdfgen .canvas import FILL_EVEN_ODD , FILL_NON_ZERO
49+ from reportlab .pdfgen .pdfimages import PDFImage
5250
5351try :
5452 from reportlab .graphics .transform import mmult
5553except ImportError :
5654 # Before Reportlab 3.5.61
5755 from reportlab .graphics .shapes import mmult
5856
59- from lxml import etree
6057import cssselect2
6158import tinycss2
62-
63- from .utils import (
64- bezier_arc_from_end_points ,
65- convert_quadratic_to_cubic_path ,
66- normalise_svg_path ,
67- )
59+ from lxml import etree
6860
6961from .fonts import (
70- get_global_font_map ,
7162 DEFAULT_FONT_NAME ,
72- DEFAULT_FONT_WEIGHT ,
73- DEFAULT_FONT_STYLE ,
7463 DEFAULT_FONT_SIZE ,
64+ DEFAULT_FONT_STYLE ,
65+ DEFAULT_FONT_WEIGHT ,
66+ get_global_font_map ,
67+ )
68+ from .fonts import (
69+ find_font as _fonts_find_font ,
7570)
7671
77- # To keep backward compatibility, since those functions where previously part of the svglib module
72+ # To keep backward compatibility, since those functions where previously part of
73+ # the svglib module
7874from .fonts import (
7975 register_font as _fonts_register_font ,
80- find_font as _fonts_find_font ,
76+ )
77+ from .utils import (
78+ bezier_arc_from_end_points ,
79+ convert_quadratic_to_cubic_path ,
80+ normalise_svg_path ,
8181)
8282
8383
@@ -625,7 +625,8 @@ def renderNode(self, node, parent=None):
625625 # and simplify further analyses of generated document
626626 item .setProperties ({"svgid" : nid })
627627 # labels are used in inkscape to name specific groups as layers
628- # preserving them simplify extraction of feature from the generated document
628+ # preserving them simplify extraction of feature from the generated
629+ # document
629630 label_attrs = [v for k , v in node .attrib .items () if "label" in k ]
630631 if len (label_attrs ) == 1 :
631632 (label ,) = label_attrs
@@ -905,7 +906,8 @@ def renderUse(self, node, group=None, clipping=None):
905906 if clipping :
906907 group .add (clipping )
907908 if len (node .getchildren ()) == 0 :
908- # Append a copy of the referenced node as the <use> child (if not already done)
909+ # Append a copy of the referenced node as the <use> child (if not
910+ # already done)
909911 node .append (copy .deepcopy (item ))
910912 self .renderNode (list (node .iter_children ())[- 1 ], parent = group )
911913 self .apply_node_attr_to_group (node , group )
@@ -1089,7 +1091,8 @@ def convertText(self, node):
10891091
10901092 frag_lengths .append (stringWidth (text , ff , fs ))
10911093
1092- # When x, y, dx, or dy is a list, we calculate position for each char of text.
1094+ # When x, y, dx, or dy is a list, we calculate position for each char of
1095+ # text.
10931096 if any (isinstance (val , list ) for val in (x1 , y1 , dx , dy )):
10941097 if has_x :
10951098 xlist = x1 if isinstance (x1 , list ) else [x1 ]
@@ -1225,9 +1228,12 @@ def convertPath(self, node):
12251228 x0 , y0 = points [- 2 :]
12261229 x1 , y1 , xn , yn = nums
12271230 last_quadratic_cp = (x1 , y1 )
1228- (x0 , y0 ), (x1 , y1 ), (x2 , y2 ), (xn , yn ) = (
1229- convert_quadratic_to_cubic_path ((x0 , y0 ), (x1 , y1 ), (xn , yn ))
1230- )
1231+ (
1232+ (x0 , y0 ),
1233+ (x1 , y1 ),
1234+ (x2 , y2 ),
1235+ (xn , yn ),
1236+ ) = convert_quadratic_to_cubic_path ((x0 , y0 ), (x1 , y1 ), (xn , yn ))
12311237 path .curveTo (x1 , y1 , x2 , y2 , xn , yn )
12321238 elif op == "T" :
12331239 if last_quadratic_cp is not None :
@@ -1238,9 +1244,12 @@ def convertPath(self, node):
12381244 xi , yi = x0 + (x0 - xp ), y0 + (y0 - yp )
12391245 last_quadratic_cp = (xi , yi )
12401246 xn , yn = nums
1241- (x0 , y0 ), (x1 , y1 ), (x2 , y2 ), (xn , yn ) = (
1242- convert_quadratic_to_cubic_path ((x0 , y0 ), (xi , yi ), (xn , yn ))
1243- )
1247+ (
1248+ (x0 , y0 ),
1249+ (x1 , y1 ),
1250+ (x2 , y2 ),
1251+ (xn , yn ),
1252+ ) = convert_quadratic_to_cubic_path ((x0 , y0 ), (xi , yi ), (xn , yn ))
12441253 path .curveTo (x1 , y1 , x2 , y2 , xn , yn )
12451254
12461255 # quadratic bezier, relative
@@ -1249,9 +1258,12 @@ def convertPath(self, node):
12491258 x1 , y1 , xn , yn = nums
12501259 x1 , y1 , xn , yn = x0 + x1 , y0 + y1 , x0 + xn , y0 + yn
12511260 last_quadratic_cp = (x1 , y1 )
1252- (x0 , y0 ), (x1 , y1 ), (x2 , y2 ), (xn , yn ) = (
1253- convert_quadratic_to_cubic_path ((x0 , y0 ), (x1 , y1 ), (xn , yn ))
1254- )
1261+ (
1262+ (x0 , y0 ),
1263+ (x1 , y1 ),
1264+ (x2 , y2 ),
1265+ (xn , yn ),
1266+ ) = convert_quadratic_to_cubic_path ((x0 , y0 ), (x1 , y1 ), (xn , yn ))
12551267 path .curveTo (x1 , y1 , x2 , y2 , xn , yn )
12561268 elif op == "t" :
12571269 if last_quadratic_cp is not None :
@@ -1263,9 +1275,12 @@ def convertPath(self, node):
12631275 xn , yn = x0 + xn , y0 + yn
12641276 xi , yi = x0 + (x0 - xp ), y0 + (y0 - yp )
12651277 last_quadratic_cp = (xi , yi )
1266- (x0 , y0 ), (x1 , y1 ), (x2 , y2 ), (xn , yn ) = (
1267- convert_quadratic_to_cubic_path ((x0 , y0 ), (xi , yi ), (xn , yn ))
1268- )
1278+ (
1279+ (x0 , y0 ),
1280+ (x1 , y1 ),
1281+ (x2 , y2 ),
1282+ (xn , yn ),
1283+ ) = convert_quadratic_to_cubic_path ((x0 , y0 ), (xi , yi ), (xn , yn ))
12691284 path .curveTo (x1 , y1 , x2 , y2 , xn , yn )
12701285
12711286 # elliptical arc
@@ -1342,7 +1357,8 @@ def applyTransformOnGroup(self, transform, group):
13421357 group .scale (* values )
13431358 elif op == "translate" :
13441359 if isinstance (values , (int , float )):
1345- # From the SVG spec: If <ty> is not provided, it is assumed to be zero.
1360+ # From the SVG spec: If <ty> is not provided, it is assumed to
1361+ # be zero.
13461362 values = values , 0
13471363 group .translate (* values )
13481364 elif op == "rotate" :
@@ -1441,11 +1457,11 @@ def applyStyleOnShape(self, shape, node, only_explicit=False):
14411457 shape .fillColor .alpha = shape .fillOpacity
14421458 if getattr (shape , "strokeWidth" , None ) == 0 :
14431459 # Quoting from the PDF 1.7 spec:
1444- # A line width of 0 denotes the thinnest line that can be rendered at device
1445- # resolution: 1 device pixel wide. However, some devices cannot reproduce 1-pixel
1446- # lines, and on high-resolution devices, they are nearly invisible. Since the
1447- # results of rendering such zero-width lines are device-dependent, their use
1448- # is not recommended.
1460+ # A line width of 0 denotes the thinnest line that can be rendered at
1461+ # device resolution: 1 device pixel wide. However, some devices cannot
1462+ # reproduce 1-pixel lines, and on high-resolution devices, they are
1463+ # nearly invisible. Since the results of rendering such zero-width
1464+ # lines are device-dependent, their use is not recommended.
14491465 shape .strokeColor = None
14501466
14511467
@@ -1584,8 +1600,8 @@ def monkeypatch_reportlab():
15841600 ReportLab always use 'Even-Odd' filling mode for paths, this patch forces
15851601 RL to honor the path fill rule mode (possibly 'Non-Zero Winding') instead.
15861602 """
1587- from reportlab .pdfgen .canvas import Canvas
15881603 from reportlab .graphics import shapes
1604+ from reportlab .pdfgen .canvas import Canvas
15891605
15901606 original_renderPath = shapes ._renderPath
15911607
0 commit comments