From 5fb4da53dd8e1a5055135eff53243bb8d63fe936 Mon Sep 17 00:00:00 2001 From: ilkoch008 <36165975+ilkoch008@users.noreply.github.com> Date: Fri, 12 Jan 2024 17:34:31 +0300 Subject: [PATCH 1/3] add simple parser for switch --- svglib/svglib.py | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/svglib/svglib.py b/svglib/svglib.py index ac9bedf..c8b71cc 100755 --- a/svglib/svglib.py +++ b/svglib/svglib.py @@ -21,11 +21,10 @@ import os import pathlib import re +import tempfile import shlex import shutil -from io import BytesIO from collections import defaultdict, namedtuple -from PIL import Image as PILImage from reportlab.pdfbase.pdfmetrics import stringWidth from reportlab.pdfgen.canvas import FILL_EVEN_ODD, FILL_NON_ZERO @@ -36,6 +35,7 @@ ) from reportlab.lib import colors from reportlab.lib.units import pica, toLength +from reportlab.lib.utils import haveImages try: from reportlab.graphics.transform import mmult @@ -320,11 +320,11 @@ def convertLength(self, svgAttr, em_base=DEFAULT_FONT_SIZE, attr_name=None, defa elif text.endswith("pc"): return float(text[:-2]) * pica elif text.endswith("pt"): - return float(text[:-2]) + return float(text[:-2]) * 1.25 elif text.endswith("em"): return float(text[:-2]) * em_base elif text.endswith("px"): - return float(text[:-2]) * 0.75 + return float(text[:-2]) elif text.endswith("ex"): # The x-height of the text must be assumed to be 0.5em tall when the # text cannot be measured. @@ -555,6 +555,9 @@ def renderNode(self, node, parent=None): parent.add(item) elif name == "clipPath": item = self.renderG(node) + elif name == "switch": # process only default case + item = self.renderSwitch(node) + parent.add(item) elif name in self.handled_shapes: if name == 'image': # We resolve the image target at renderer level because it can point @@ -653,10 +656,7 @@ def get_shape_from_node(node): elif isinstance(shape, Path): return ClippingPath(copy_from=shape) elif shape: - logger.error( - "Unsupported shape type %s for clipping", - shape.__class__.__name__ - ) + logging.error("Unsupported shape type %s for clipping", shape.__class__.__name__) def print_unused_attributes(self, node): if logger.level > logging.DEBUG: @@ -679,7 +679,7 @@ def xlink_href_target(self, node, group=None): Return either: - a tuple (renderer, node) when the the xlink:href attribute targets a vector file or node - - a PIL Image object representing the image file + - the path to an image file for any raster image targets - None if any problem occurs """ # Bare 'href' was introduced in SVG 2. @@ -690,10 +690,16 @@ def xlink_href_target(self, node, group=None): # First handle any raster embedded image data match = re.match(r"^data:image/(jpe?g|png);base64", xlink_href) if match: + img_format = match.groups()[0] image_data = base64.decodebytes(xlink_href[(match.span(0)[1] + 1):].encode('ascii')) - bytes_stream = BytesIO(image_data) - - return PILImage.open(bytes_stream) + file_indicator, path = tempfile.mkstemp(suffix=f'.{img_format}') + with open(path, 'wb') as fh: + fh.write(image_data) + # Close temporary file (as opened by tempfile.mkstemp) + os.close(file_indicator) + # this needs to be removed later, not here... + # if exists(path): os.remove(path) + return path # From here, we can assume this is a path. if '#' in xlink_href: @@ -826,6 +832,14 @@ def renderG(self, node, clipping=None): return gr + def renderSwitch(self, node): + gr = Group() + for child in node.iter_children(): + if child.getAttribute('requiredFeatures') == '': + self.renderNode(child, parent=gr) + break + return gr + def renderStyle(self, node): self.attrConverter.css_rules.add_styles(node.text or "") @@ -1251,6 +1265,12 @@ def convertPath(self, node): return gr def convertImage(self, node): + if not haveImages: + logger.warning( + "Unable to handle embedded images. Maybe the pillow library is missing?" + ) + return None + x, y, width, height = self.convert_length_attrs(node, 'x', 'y', 'width', 'height') image = node._resolved_target image = Image(int(x), int(y + height), int(width), int(height), image) From 8e3b74ee4686ec77d1d2edbfb218eda2db187608 Mon Sep 17 00:00:00 2001 From: ilkoch008 <36165975+ilkoch008@users.noreply.github.com> Date: Fri, 12 Jan 2024 17:45:22 +0300 Subject: [PATCH 2/3] revert wrong changes --- svglib/svglib.py | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/svglib/svglib.py b/svglib/svglib.py index c8b71cc..965db40 100755 --- a/svglib/svglib.py +++ b/svglib/svglib.py @@ -21,10 +21,11 @@ import os import pathlib import re -import tempfile import shlex import shutil +from io import BytesIO from collections import defaultdict, namedtuple +from PIL import Image as PILImage from reportlab.pdfbase.pdfmetrics import stringWidth from reportlab.pdfgen.canvas import FILL_EVEN_ODD, FILL_NON_ZERO @@ -35,7 +36,6 @@ ) from reportlab.lib import colors from reportlab.lib.units import pica, toLength -from reportlab.lib.utils import haveImages try: from reportlab.graphics.transform import mmult @@ -320,11 +320,11 @@ def convertLength(self, svgAttr, em_base=DEFAULT_FONT_SIZE, attr_name=None, defa elif text.endswith("pc"): return float(text[:-2]) * pica elif text.endswith("pt"): - return float(text[:-2]) * 1.25 + return float(text[:-2]) elif text.endswith("em"): return float(text[:-2]) * em_base elif text.endswith("px"): - return float(text[:-2]) + return float(text[:-2]) * 0.75 elif text.endswith("ex"): # The x-height of the text must be assumed to be 0.5em tall when the # text cannot be measured. @@ -656,7 +656,10 @@ def get_shape_from_node(node): elif isinstance(shape, Path): return ClippingPath(copy_from=shape) elif shape: - logging.error("Unsupported shape type %s for clipping", shape.__class__.__name__) + logger.error( + "Unsupported shape type %s for clipping", + shape.__class__.__name__ + ) def print_unused_attributes(self, node): if logger.level > logging.DEBUG: @@ -679,7 +682,7 @@ def xlink_href_target(self, node, group=None): Return either: - a tuple (renderer, node) when the the xlink:href attribute targets a vector file or node - - the path to an image file for any raster image targets + - a PIL Image object representing the image file - None if any problem occurs """ # Bare 'href' was introduced in SVG 2. @@ -690,16 +693,10 @@ def xlink_href_target(self, node, group=None): # First handle any raster embedded image data match = re.match(r"^data:image/(jpe?g|png);base64", xlink_href) if match: - img_format = match.groups()[0] image_data = base64.decodebytes(xlink_href[(match.span(0)[1] + 1):].encode('ascii')) - file_indicator, path = tempfile.mkstemp(suffix=f'.{img_format}') - with open(path, 'wb') as fh: - fh.write(image_data) - # Close temporary file (as opened by tempfile.mkstemp) - os.close(file_indicator) - # this needs to be removed later, not here... - # if exists(path): os.remove(path) - return path + bytes_stream = BytesIO(image_data) + + return PILImage.open(bytes_stream) # From here, we can assume this is a path. if '#' in xlink_href: @@ -1265,12 +1262,6 @@ def convertPath(self, node): return gr def convertImage(self, node): - if not haveImages: - logger.warning( - "Unable to handle embedded images. Maybe the pillow library is missing?" - ) - return None - x, y, width, height = self.convert_length_attrs(node, 'x', 'y', 'width', 'height') image = node._resolved_target image = Image(int(x), int(y + height), int(width), int(height), image) From 8c8c0d28f8767e4cc3ed90cb2aad27899ba48e4f Mon Sep 17 00:00:00 2001 From: ilkoch008 <36165975+ilkoch008@users.noreply.github.com> Date: Mon, 15 Jan 2024 10:01:37 +0300 Subject: [PATCH 3/3] Update README.rst --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 86cbaf3..2b89423 100644 --- a/README.rst +++ b/README.rst @@ -87,7 +87,8 @@ Known limitations of supported attributes is still limited - clipping is limited to single paths, no mask support - color gradients are not supported (limitation of reportlab) -- SVG ``ForeignObject`` elements are not supported. +- SVG ``ForeignObject`` elements are not supported +- limited support for SVG ``switch`` elements. Examples