Skip to content

Commit

Permalink
Configurable exported viewbox
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
lgcat76 committed Dec 17, 2021
1 parent 45b82fa commit 45138f8
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 14 deletions.
29 changes: 19 additions & 10 deletions cairosvg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,62 +38,71 @@
def svg2svg(bytestring=None, *, file_obj=None, url=None, dpi=96,
parent_width=None, parent_height=None, scale=1, unsafe=False,
background_color=None, negate_colors=False, invert_images=False,
write_to=None, output_width=None, output_height=None):
write_to=None, output_width=None, output_height=None,
viewbox_id=None):
return surface.SVGSurface.convert(
bytestring=bytestring, file_obj=file_obj, url=url, dpi=dpi,
parent_width=parent_width, parent_height=parent_height, scale=scale,
background_color=background_color,
negate_colors=negate_colors, invert_images=invert_images,
unsafe=unsafe, write_to=write_to, output_width=output_width,
output_height=output_height)
output_height=output_height, viewbox_id=viewbox_id)


def svg2png(bytestring=None, *, file_obj=None, url=None, dpi=96,
parent_width=None, parent_height=None, scale=1, unsafe=False,
background_color=None, negate_colors=False, invert_images=False,
write_to=None, output_width=None, output_height=None):
write_to=None, output_width=None, output_height=None,
viewbox_id=None):
return surface.PNGSurface.convert(
bytestring=bytestring, file_obj=file_obj, url=url, dpi=dpi,
parent_width=parent_width, parent_height=parent_height, scale=scale,
background_color=background_color, negate_colors=negate_colors,
invert_images=invert_images, unsafe=unsafe, write_to=write_to,
output_width=output_width, output_height=output_height)
output_width=output_width, output_height=output_height,
viewbox_id=viewbox_id)


def svg2pdf(bytestring=None, *, file_obj=None, url=None, dpi=96,
parent_width=None, parent_height=None, scale=1, unsafe=False,
background_color=None, negate_colors=False, invert_images=False,
write_to=None, output_width=None, output_height=None):
write_to=None, output_width=None, output_height=None,
viewbox_id=None):
return surface.PDFSurface.convert(
bytestring=bytestring, file_obj=file_obj, url=url, dpi=dpi,
parent_width=parent_width, parent_height=parent_height, scale=scale,
background_color=background_color, negate_colors=negate_colors,
invert_images=invert_images, unsafe=unsafe, write_to=write_to,
output_width=output_width, output_height=output_height)
output_width=output_width, output_height=output_height,
viewbox_id=viewbox_id)


def svg2ps(bytestring=None, *, file_obj=None, url=None, dpi=96,
parent_width=None, parent_height=None, scale=1, unsafe=False,
background_color=None, negate_colors=False, invert_images=False,
write_to=None, output_width=None, output_height=None):
write_to=None, output_width=None, output_height=None,
viewbox_id=None):
return surface.PSSurface.convert(
bytestring=bytestring, file_obj=file_obj, url=url, dpi=dpi,
parent_width=parent_width, parent_height=parent_height, scale=scale,
background_color=background_color, negate_colors=negate_colors,
invert_images=invert_images, unsafe=unsafe, write_to=write_to,
output_width=output_width, output_height=output_height)
output_width=output_width, output_height=output_height,
viewbox_id=viewbox_id)


def svg2eps(bytestring=None, *, file_obj=None, url=None, dpi=96,
parent_width=None, parent_height=None, scale=1, unsafe=False,
background_color=None, negate_colors=False, invert_images=False,
write_to=None, output_width=None, output_height=None):
write_to=None, output_width=None, output_height=None,
viewbox_id=None):
return surface.EPSSurface.convert(
bytestring=bytestring, file_obj=file_obj, url=url, dpi=dpi,
parent_width=parent_width, parent_height=parent_height, scale=scale,
background_color=background_color, negate_colors=negate_colors,
invert_images=invert_images, unsafe=unsafe, write_to=write_to,
output_width=output_width, output_height=output_height)
output_width=output_width, output_height=output_height,
viewbox_id=viewbox_id)


if __debug__:
Expand Down
6 changes: 5 additions & 1 deletion cairosvg/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ def main(argv=None, stdout=None, stdin=None):
parser.add_argument(
'--output-height', default=None, type=float,
help='desired output height in pixels')
parser.add_argument(
'--viewbox-id', default=None,
help='export viewbox defined by the element with the given id')

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

Expand All @@ -61,7 +64,8 @@ def main(argv=None, stdout=None, stdin=None):
'negate_colors': options.negate_colors,
'invert_images': options.invert_images,
'output_width': options.output_width,
'output_height': options.output_height}
'output_height': options.output_height,
'viewbox_id': options.viewbox_id}
stdin = stdin or sys.stdin
stdout = stdout or sys.stdout
kwargs['write_to'] = (
Expand Down
13 changes: 13 additions & 0 deletions cairosvg/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,3 +386,16 @@ def size(surface, string, reference='xy'):

# Unknown size
return 0


def find_child(node, element_id, default=None):
if element_id is not None:
if node.element.id == element_id:
return node

for child in node.children:
found = find_child(child, element_id)
if found is not None:
return found

return default
21 changes: 18 additions & 3 deletions cairosvg/surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@

import cairocffi as cairo

from .bounding_box import calculate_bounding_box
from .colors import color, negate_color
from .defs import (
apply_filter_after_painting, apply_filter_before_painting, clip_path,
filter_, gradient_or_pattern, linear_gradient, marker, mask, paint_mask,
parse_all_defs, pattern, prepare_filter, radial_gradient, use)
from .helpers import (
UNITS, PointError, clip_rect, node_format, normalize, paint,
UNITS, PointError, clip_rect, find_child, node_format, normalize, paint,
preserve_ratio, size, transform)
from .image import image, invert_image
from .parser import Tree
Expand Down Expand Up @@ -96,6 +97,7 @@ class Surface(object):
@classmethod
def convert(cls, bytestring=None, *, file_obj=None, url=None, dpi=96,
parent_width=None, parent_height=None, scale=1, unsafe=False,
viewbox_id=None,
background_color=None, negate_colors=False,
invert_images=False, write_to=None, output_width=None,
output_height=None, **kwargs):
Expand All @@ -115,6 +117,7 @@ def convert(cls, bytestring=None, *, file_obj=None, url=None, dpi=96,
:param scale: The ouptut scaling factor.
:param unsafe: A boolean allowing XML entities and very large files
(WARNING: vulnerable to XXE attacks and various DoS).
:param viewbox_id: SVG element id defining the area to export.
Specifiy the output with:
Expand All @@ -132,6 +135,7 @@ def convert(cls, bytestring=None, *, file_obj=None, url=None, dpi=96,
instance = cls(
tree, output, dpi, None, parent_width, parent_height, scale,
output_width, output_height, background_color,
viewbox_id=viewbox_id if viewbox_id else None,
map_rgba=negate_color if negate_colors else None,
map_image=invert_image if invert_images else None)
instance.finish()
Expand All @@ -141,7 +145,8 @@ def convert(cls, bytestring=None, *, file_obj=None, url=None, dpi=96,
def __init__(self, tree, output, dpi, parent_surface=None,
parent_width=None, parent_height=None,
scale=1, output_width=None, output_height=None,
background_color=None, map_rgba=None, map_image=None):
background_color=None, map_rgba=None, map_image=None,
viewbox_id=None):
"""Create the surface from a filename or a file-like object.
The rendered content is written to ``output`` which can be a filename,
Expand Down Expand Up @@ -179,7 +184,17 @@ def __init__(self, tree, output, dpi, parent_surface=None,
self.dpi = dpi
self.font_size = size(self, '12pt')
self.stroke_and_fill = True
width, height, viewbox = node_format(self, tree)

width, height, viewbox = (0, 0, None)
viewbox_node = find_child(tree, viewbox_id)
if viewbox_node:
viewbox = calculate_bounding_box(self, viewbox_node)
if viewbox:
width, height = viewbox[2:]

if viewbox is None:
width, height, viewbox = node_format(self, tree)

if viewbox is None:
viewbox = (0, 0, width, height)

Expand Down

0 comments on commit 45138f8

Please sign in to comment.