diff --git a/fpdf/svg.py b/fpdf/svg.py index e51ec49fb..04259f7a6 100644 --- a/fpdf/svg.py +++ b/fpdf/svg.py @@ -859,6 +859,8 @@ def handle_defs(self, defs): self.build_path(child) elif child.tag in xmlns_lookup("svg", "image"): self.build_image(child) + elif child.tag in xmlns_lookup("svg", "text"): + self.build_text(child) elif child.tag in shape_tags: self.build_shape(child) elif child.tag in xmlns_lookup("svg", "clipPath"): @@ -928,6 +930,8 @@ def build_group(self, group, pdf_group=None): pdf_group.add_item(self.build_xref(child)) elif child.tag in xmlns_lookup("svg", "image"): pdf_group.add_item(self.build_image(child)) + elif child.tag in xmlns_lookup("svg", "text"): + pdf_group.add_item(self.build_text(child)) else: LOGGER.debug("Unsupported SVG tag: <%s>", child.tag) @@ -968,6 +972,43 @@ def apply_clipping_path(self, stylable, svg_element): clipping_path_id = re.search(r"url\((\#\w+)\)", clipping_path) stylable.clipping_path = self.cross_references[clipping_path_id[1]] + @force_nodocument + def build_text(self, text): + if "dx" in text.attrib or "dy" in text.attrib: + raise NotImplementedError( + '"dx" / "dy" defined on is currently not supported (but contributions are welcome!)' + ) + if "lengthAdjust" in text.attrib: + raise NotImplementedError( + '"lengthAdjust" defined on is currently not supported (but contributions are welcome!)' + ) + if "rotate" in text.attrib: + raise NotImplementedError( + '"rotate" defined on is currently not supported (but contributions are welcome!)' + ) + if "style" in text.attrib: + raise NotImplementedError( + '"style" defined on is currently not supported (but contributions are welcome!)' + ) + if "textLength" in text.attrib: + raise NotImplementedError( + '"textLength" defined on is currently not supported (but contributions are welcome!)' + ) + if "transform" in text.attrib: + raise NotImplementedError( + '"transform" defined on is currently not supported (but contributions are welcome!)' + ) + font_family = text.attrib.get("font-family") + font_size = text.attrib.get("font-size") + # TODO: reuse code from line_break & text_region modules. + # We could either: + # 1. handle text regions in this module (svg), with a dedicated SVGText class. + # 2. handle text regions in the drawing module, maybe by defining a PaintedPath.text() method. + # This may be the best approach, as we would benefit from the global transformation performed in SVGObject.transform_to_rect_viewport() + svg_text = None + self.update_xref(text.attrib.get("id"), svg_text) + return svg_text + @force_nodocument def build_image(self, image): href = None diff --git a/test/svg/generated_pdf/text-samples.pdf b/test/svg/generated_pdf/text-samples.pdf new file mode 100644 index 000000000..8124b0b1f Binary files /dev/null and b/test/svg/generated_pdf/text-samples.pdf differ diff --git a/test/svg/parameters.py b/test/svg/parameters.py index 0f15916be..7fd63e5ea 100644 --- a/test/svg/parameters.py +++ b/test/svg/parameters.py @@ -775,6 +775,7 @@ def Gs(**kwargs): svgfile("use-image-def.svg"), id="Use xlink:href to insert an from ", ), + pytest.param(svgfile("text-samples.svg"), id=" tests"), ) svg_path_edge_cases = ( diff --git a/test/svg/svg_sources/embedded-raster-images.svg b/test/svg/svg_sources/embedded-raster-images.svg index ffef5b6ed..ddb936169 100644 --- a/test/svg/svg_sources/embedded-raster-images.svg +++ b/test/svg/svg_sources/embedded-raster-images.svg @@ -1,7 +1,7 @@ - Example image.svg - embedding raster images + Example embedded-raster-images.svg diff --git a/test/svg/svg_sources/text-samples.svg b/test/svg/svg_sources/text-samples.svg new file mode 100644 index 000000000..281f15112 --- /dev/null +++ b/test/svg/svg_sources/text-samples.svg @@ -0,0 +1,13 @@ + + + Example text-samples.svg + + + + My + cat + is + Grumpy! + +