Skip to content

Commit 612fc32

Browse files
committed
Configurable exported viewbox
The area of the SVG that will be exported can be specified by giving the id of an SVG element. Its bounding box will be used to configure the area to export.
1 parent 45b82fa commit 612fc32

File tree

4 files changed

+54
-14
lines changed

4 files changed

+54
-14
lines changed

cairosvg/__init__.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,62 +38,70 @@
3838
def svg2svg(bytestring=None, *, file_obj=None, url=None, dpi=96,
3939
parent_width=None, parent_height=None, scale=1, unsafe=False,
4040
background_color=None, negate_colors=False, invert_images=False,
41-
write_to=None, output_width=None, output_height=None):
41+
write_to=None, output_width=None, output_height=None, viewbox_id=None):
4242
return surface.SVGSurface.convert(
4343
bytestring=bytestring, file_obj=file_obj, url=url, dpi=dpi,
4444
parent_width=parent_width, parent_height=parent_height, scale=scale,
4545
background_color=background_color,
4646
negate_colors=negate_colors, invert_images=invert_images,
4747
unsafe=unsafe, write_to=write_to, output_width=output_width,
48-
output_height=output_height)
48+
output_height=output_height, viewbox_id=viewbox_id)
4949

5050

5151
def svg2png(bytestring=None, *, file_obj=None, url=None, dpi=96,
5252
parent_width=None, parent_height=None, scale=1, unsafe=False,
5353
background_color=None, negate_colors=False, invert_images=False,
54-
write_to=None, output_width=None, output_height=None):
54+
write_to=None, output_width=None, output_height=None,
55+
viewbox_id=None):
5556
return surface.PNGSurface.convert(
5657
bytestring=bytestring, file_obj=file_obj, url=url, dpi=dpi,
5758
parent_width=parent_width, parent_height=parent_height, scale=scale,
5859
background_color=background_color, negate_colors=negate_colors,
5960
invert_images=invert_images, unsafe=unsafe, write_to=write_to,
60-
output_width=output_width, output_height=output_height)
61+
output_width=output_width, output_height=output_height,
62+
viewbox_id=viewbox_id)
6163

6264

6365
def svg2pdf(bytestring=None, *, file_obj=None, url=None, dpi=96,
6466
parent_width=None, parent_height=None, scale=1, unsafe=False,
6567
background_color=None, negate_colors=False, invert_images=False,
66-
write_to=None, output_width=None, output_height=None):
68+
write_to=None, output_width=None, output_height=None,
69+
viewbox_id=None):
6770
return surface.PDFSurface.convert(
6871
bytestring=bytestring, file_obj=file_obj, url=url, dpi=dpi,
6972
parent_width=parent_width, parent_height=parent_height, scale=scale,
7073
background_color=background_color, negate_colors=negate_colors,
7174
invert_images=invert_images, unsafe=unsafe, write_to=write_to,
72-
output_width=output_width, output_height=output_height)
75+
output_width=output_width, output_height=output_height,
76+
viewbox_id=viewbox_id)
7377

7478

7579
def svg2ps(bytestring=None, *, file_obj=None, url=None, dpi=96,
7680
parent_width=None, parent_height=None, scale=1, unsafe=False,
7781
background_color=None, negate_colors=False, invert_images=False,
78-
write_to=None, output_width=None, output_height=None):
82+
write_to=None, output_width=None, output_height=None,
83+
viewbox_id=None):
7984
return surface.PSSurface.convert(
8085
bytestring=bytestring, file_obj=file_obj, url=url, dpi=dpi,
8186
parent_width=parent_width, parent_height=parent_height, scale=scale,
8287
background_color=background_color, negate_colors=negate_colors,
8388
invert_images=invert_images, unsafe=unsafe, write_to=write_to,
84-
output_width=output_width, output_height=output_height)
89+
output_width=output_width, output_height=output_height,
90+
viewbox_id=viewbox_id)
8591

8692

8793
def svg2eps(bytestring=None, *, file_obj=None, url=None, dpi=96,
8894
parent_width=None, parent_height=None, scale=1, unsafe=False,
8995
background_color=None, negate_colors=False, invert_images=False,
90-
write_to=None, output_width=None, output_height=None):
96+
write_to=None, output_width=None, output_height=None,
97+
viewbox_id=None):
9198
return surface.EPSSurface.convert(
9299
bytestring=bytestring, file_obj=file_obj, url=url, dpi=dpi,
93100
parent_width=parent_width, parent_height=parent_height, scale=scale,
94101
background_color=background_color, negate_colors=negate_colors,
95102
invert_images=invert_images, unsafe=unsafe, write_to=write_to,
96-
output_width=output_width, output_height=output_height)
103+
output_width=output_width, output_height=output_height,
104+
viewbox_id=viewbox_id)
97105

98106

99107
if __debug__:

cairosvg/__main__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ def main(argv=None, stdout=None, stdin=None):
5050
parser.add_argument(
5151
'--output-height', default=None, type=float,
5252
help='desired output height in pixels')
53+
parser.add_argument(
54+
'--viewbox-id', default=None,
55+
help='export viewbox defined by the element with the given id')
5356

5457
parser.add_argument('-o', '--output', default='-', help='output filename')
5558

@@ -61,7 +64,8 @@ def main(argv=None, stdout=None, stdin=None):
6164
'negate_colors': options.negate_colors,
6265
'invert_images': options.invert_images,
6366
'output_width': options.output_width,
64-
'output_height': options.output_height}
67+
'output_height': options.output_height,
68+
'viewbox_id': options.viewbox_id}
6569
stdin = stdin or sys.stdin
6670
stdout = stdout or sys.stdout
6771
kwargs['write_to'] = (

cairosvg/helpers.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,3 +386,16 @@ def size(surface, string, reference='xy'):
386386

387387
# Unknown size
388388
return 0
389+
390+
391+
def find_child(node, element_id, default=None):
392+
if element_id is not None:
393+
if node.element.id == element_id:
394+
return node
395+
396+
for child in node.children:
397+
found = find_child(child, element_id)
398+
if found is not None:
399+
return found
400+
401+
return default

cairosvg/surface.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
88

99
import cairocffi as cairo
1010

11+
from .bounding_box import calculate_bounding_box
1112
from .colors import color, negate_color
1213
from .defs import (
1314
apply_filter_after_painting, apply_filter_before_painting, clip_path,
1415
filter_, gradient_or_pattern, linear_gradient, marker, mask, paint_mask,
1516
parse_all_defs, pattern, prepare_filter, radial_gradient, use)
1617
from .helpers import (
1718
UNITS, PointError, clip_rect, node_format, normalize, paint,
18-
preserve_ratio, size, transform)
19+
preserve_ratio, size, transform, find_child)
1920
from .image import image, invert_image
2021
from .parser import Tree
2122
from .path import draw_markers, path
@@ -96,6 +97,7 @@ class Surface(object):
9697
@classmethod
9798
def convert(cls, bytestring=None, *, file_obj=None, url=None, dpi=96,
9899
parent_width=None, parent_height=None, scale=1, unsafe=False,
100+
viewbox_id=None,
99101
background_color=None, negate_colors=False,
100102
invert_images=False, write_to=None, output_width=None,
101103
output_height=None, **kwargs):
@@ -115,6 +117,7 @@ def convert(cls, bytestring=None, *, file_obj=None, url=None, dpi=96,
115117
:param scale: The ouptut scaling factor.
116118
:param unsafe: A boolean allowing XML entities and very large files
117119
(WARNING: vulnerable to XXE attacks and various DoS).
120+
:param viewbox_id: SVG element id defining the area to export.
118121
119122
Specifiy the output with:
120123
@@ -132,6 +135,7 @@ def convert(cls, bytestring=None, *, file_obj=None, url=None, dpi=96,
132135
instance = cls(
133136
tree, output, dpi, None, parent_width, parent_height, scale,
134137
output_width, output_height, background_color,
138+
viewbox_id=viewbox_id if viewbox_id else None,
135139
map_rgba=negate_color if negate_colors else None,
136140
map_image=invert_image if invert_images else None)
137141
instance.finish()
@@ -141,7 +145,8 @@ def convert(cls, bytestring=None, *, file_obj=None, url=None, dpi=96,
141145
def __init__(self, tree, output, dpi, parent_surface=None,
142146
parent_width=None, parent_height=None,
143147
scale=1, output_width=None, output_height=None,
144-
background_color=None, map_rgba=None, map_image=None):
148+
background_color=None, map_rgba=None, map_image=None,
149+
viewbox_id=None):
145150
"""Create the surface from a filename or a file-like object.
146151
147152
The rendered content is written to ``output`` which can be a filename,
@@ -179,7 +184,17 @@ def __init__(self, tree, output, dpi, parent_surface=None,
179184
self.dpi = dpi
180185
self.font_size = size(self, '12pt')
181186
self.stroke_and_fill = True
182-
width, height, viewbox = node_format(self, tree)
187+
188+
width, height, viewbox = (0, 0, None)
189+
viewbox_node = find_child(tree, viewbox_id)
190+
if viewbox_node:
191+
viewbox = calculate_bounding_box(self, viewbox_node)
192+
if viewbox:
193+
width, height = viewbox[2:]
194+
195+
if viewbox is None:
196+
width, height, viewbox = node_format(self, tree)
197+
183198
if viewbox is None:
184199
viewbox = (0, 0, width, height)
185200

0 commit comments

Comments
 (0)