Skip to content

Commit d437cbb

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 d437cbb

File tree

4 files changed

+55
-14
lines changed

4 files changed

+55
-14
lines changed

cairosvg/__init__.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,62 +38,71 @@
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,
42+
viewbox_id=None):
4243
return surface.SVGSurface.convert(
4344
bytestring=bytestring, file_obj=file_obj, url=url, dpi=dpi,
4445
parent_width=parent_width, parent_height=parent_height, scale=scale,
4546
background_color=background_color,
4647
negate_colors=negate_colors, invert_images=invert_images,
4748
unsafe=unsafe, write_to=write_to, output_width=output_width,
48-
output_height=output_height)
49+
output_height=output_height, viewbox_id=viewbox_id)
4950

5051

5152
def svg2png(bytestring=None, *, file_obj=None, url=None, dpi=96,
5253
parent_width=None, parent_height=None, scale=1, unsafe=False,
5354
background_color=None, negate_colors=False, invert_images=False,
54-
write_to=None, output_width=None, output_height=None):
55+
write_to=None, output_width=None, output_height=None,
56+
viewbox_id=None):
5557
return surface.PNGSurface.convert(
5658
bytestring=bytestring, file_obj=file_obj, url=url, dpi=dpi,
5759
parent_width=parent_width, parent_height=parent_height, scale=scale,
5860
background_color=background_color, negate_colors=negate_colors,
5961
invert_images=invert_images, unsafe=unsafe, write_to=write_to,
60-
output_width=output_width, output_height=output_height)
62+
output_width=output_width, output_height=output_height,
63+
viewbox_id=viewbox_id)
6164

6265

6366
def svg2pdf(bytestring=None, *, file_obj=None, url=None, dpi=96,
6467
parent_width=None, parent_height=None, scale=1, unsafe=False,
6568
background_color=None, negate_colors=False, invert_images=False,
66-
write_to=None, output_width=None, output_height=None):
69+
write_to=None, output_width=None, output_height=None,
70+
viewbox_id=None):
6771
return surface.PDFSurface.convert(
6872
bytestring=bytestring, file_obj=file_obj, url=url, dpi=dpi,
6973
parent_width=parent_width, parent_height=parent_height, scale=scale,
7074
background_color=background_color, negate_colors=negate_colors,
7175
invert_images=invert_images, unsafe=unsafe, write_to=write_to,
72-
output_width=output_width, output_height=output_height)
76+
output_width=output_width, output_height=output_height,
77+
viewbox_id=viewbox_id)
7378

7479

7580
def svg2ps(bytestring=None, *, file_obj=None, url=None, dpi=96,
7681
parent_width=None, parent_height=None, scale=1, unsafe=False,
7782
background_color=None, negate_colors=False, invert_images=False,
78-
write_to=None, output_width=None, output_height=None):
83+
write_to=None, output_width=None, output_height=None,
84+
viewbox_id=None):
7985
return surface.PSSurface.convert(
8086
bytestring=bytestring, file_obj=file_obj, url=url, dpi=dpi,
8187
parent_width=parent_width, parent_height=parent_height, scale=scale,
8288
background_color=background_color, negate_colors=negate_colors,
8389
invert_images=invert_images, unsafe=unsafe, write_to=write_to,
84-
output_width=output_width, output_height=output_height)
90+
output_width=output_width, output_height=output_height,
91+
viewbox_id=viewbox_id)
8592

8693

8794
def svg2eps(bytestring=None, *, file_obj=None, url=None, dpi=96,
8895
parent_width=None, parent_height=None, scale=1, unsafe=False,
8996
background_color=None, negate_colors=False, invert_images=False,
90-
write_to=None, output_width=None, output_height=None):
97+
write_to=None, output_width=None, output_height=None,
98+
viewbox_id=None):
9199
return surface.EPSSurface.convert(
92100
bytestring=bytestring, file_obj=file_obj, url=url, dpi=dpi,
93101
parent_width=parent_width, parent_height=parent_height, scale=scale,
94102
background_color=background_color, negate_colors=negate_colors,
95103
invert_images=invert_images, unsafe=unsafe, write_to=write_to,
96-
output_width=output_width, output_height=output_height)
104+
output_width=output_width, output_height=output_height,
105+
viewbox_id=viewbox_id)
97106

98107

99108
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)