From 3f147c7d16b691a363143519c2cf623af781cb39 Mon Sep 17 00:00:00 2001 From: Lucas Cimon <925560+Lucas-C@users.noreply.github.com> Date: Mon, 20 Nov 2023 11:52:21 +0100 Subject: [PATCH] Supporting tags from SVG files --- fpdf/svg.py | 68 ++++++++++++------ test/svg/generated_pdf/text-samples.pdf | Bin 0 -> 1241 bytes test/svg/parameters.py | 1 + .../svg_sources/embedded-raster-images.svg | 2 +- test/svg/svg_sources/text-samples.svg | 13 ++++ 5 files changed, 60 insertions(+), 24 deletions(-) create mode 100644 test/svg/generated_pdf/text-samples.pdf create mode 100644 test/svg/svg_sources/text-samples.svg diff --git a/fpdf/svg.py b/fpdf/svg.py index 590c7c1fc..379c2908d 100644 --- a/fpdf/svg.py +++ b/fpdf/svg.py @@ -342,21 +342,14 @@ def new_path(tag, clipping_path: bool = False): if clipping_path: path = ClippingPath() apply_styles(path, tag) - return path @classmethod def rect(cls, tag, clipping_path: bool = False): """Convert an SVG into a PDF path.""" # svg rect is wound clockwise - if "x" in tag.attrib: - x = resolve_length(tag.attrib["x"]) - else: - x = 0 - if "y" in tag.attrib: - y = resolve_length(tag.attrib["y"]) - else: - y = 0 + x = resolve_length(tag.attrib.get("x", 0)) + y = resolve_length(tag.attrib.get("y", 0)) width = tag.attrib.get("width", "0") if width.endswith("%"): width = Percent(width[:-1]) @@ -397,7 +390,6 @@ def rect(cls, tag, clipping_path: bool = False): ry = height / 2 path = cls.new_path(tag, clipping_path) - path.rectangle(x, y, width, height, rx, ry) return path @@ -409,7 +401,6 @@ def circle(cls, tag, clipping_path: bool = False): r = float(tag.attrib["r"]) path = cls.new_path(tag, clipping_path) - path.circle(cx, cy, r) return path @@ -447,34 +438,24 @@ def line(cls, tag): y2 = float(tag.attrib["y2"]) path = cls.new_path(tag) - path.move_to(x1, y1) path.line_to(x2, y2) - return path @classmethod def polyline(cls, tag): """Convert an SVG into a PDF path.""" - points = tag.attrib["points"] - path = cls.new_path(tag) - - points = "M" + points + points = "M" + tag.attrib["points"] svg_path_converter(path, points) - return path @classmethod def polygon(cls, tag, clipping_path: bool = False): """Convert an SVG into a PDF path.""" - points = tag.attrib["points"] - path = cls.new_path(tag, clipping_path) - - points = "M" + points + "Z" + points = "M" + tag.attrib["points"] + "Z" svg_path_converter(path, points) - return path @@ -878,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"): @@ -947,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) @@ -987,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 0000000000000000000000000000000000000000..8124b0b1f2689e7e649bae89e632024875c74d59 GIT binary patch literal 1241 zcmah}O=uHA6b7jx+k-`_! z1wmT$AmY`7e`pUDi{i!7n+HXOS`?+AAUzaOP{f0H(Kq>NmK5wOSlkqCp$EKTQgrA-vE8O@NqW!7>cR?U-sWR?sA641~=ebOos#3oEETSlp1 zR?QBg@RIc+vQBiv8h4eU5F6-Cf=%Kee>Emw&){Ud4;lbGZ~`<`vm~)&mFx_5P{<91f_CAcCF?lUyg{%HJsLW}3eH$r%&>#l z94Ds`FLE@>AvV?vO!-Foa|J+|bw*LS>>v`}q`FUlvY~NN}+lRikUHzdwVztEb z_1!zC!@aA+`(7^3f86S2jP6~nxo0b5T4w01r+xm6G57w($SAqD*0mJszg=Mrv@cO2ndnQJt}%QM7K$W&`?NXyxv$sW&Qy~w_}a~ literal 0 HcmV?d00001 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! + +