From 1c8c470b5d4d7a16510eaa355911fb9bddaec957 Mon Sep 17 00:00:00 2001 From: Lucas Cimon <925560+Lucas-C@users.noreply.github.com> Date: Thu, 24 Jun 2021 17:57:28 +0200 Subject: [PATCH] Fixed a bug when `fpdf.Template` was used to render QRCodes, due to a forced conversion to string - close #175 --- CHANGELOG.md | 5 ++++- fpdf/html.py | 6 +++--- fpdf/template.py | 1 - test/image/test_full_page_image.py | 4 ++-- test/requirements.txt | 1 + test/template/template_qrcode.pdf | Bin 0 -> 9515 bytes test/template/test_template.py | 33 +++++++++++++++++++++++++++++ 7 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 test/template/template_qrcode.pdf diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fb5f66f4..aafb93fb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/), and [PEP 440](https://www.python.org/dev/peps/pep-0440/). +## [2.4.2] - not released yet +- `HTMLMixin` / `HTML2FPDF`: support setting HTML font colors by name and short hex codes +- fixed a bug when `fpdf.Template` was used to render QRCodes, due to a forced conversion to string (#175) + ## [2.4.1] - 2021-06-12 ### Fixed - erroneous page breaks occured for full-width / full-height images @@ -21,7 +25,6 @@ and [PEP 440](https://www.python.org/dev/peps/pep-0440/). - the `h` (height) parameter of the `cell`, `multi_cell` & `write` methods gets a default value change, `None`, meaning to use the current font size - removed the useless `w` & `h` parameters of the `FPDF.text_annotation()` method ### Added -- Support setting HTML font colors by name and short hex codes - new `FPDF.add_action()` method, documented in the [Annotations section](https://pyfpdf.github.io/fpdf2/Annotations.html) - `FPDF.cell`: new optional `markdown=True` parameter that enables basic Markdown-like styling: `**bold**, __italics__, --underlined--` - `FPDF.cell`: new optional boolean `center` parameter that positions the cell horizontally diff --git a/fpdf/html.py b/fpdf/html.py index a8f25b0ce..65d57b369 100644 --- a/fpdf/html.py +++ b/fpdf/html.py @@ -169,7 +169,7 @@ def px2mm(px): return int(px) * 25.4 / 72 -def hex2dec(color="#000000"): +def color_as_decimal(color="#000000"): if not color: return None @@ -294,7 +294,7 @@ def _insert_td(self, data=""): else: align = self.td.get("align", "L")[0].upper() border = border and "LR" - bgcolor = hex2dec(self.td.get("bgcolor", self.tr.get("bgcolor", ""))) + bgcolor = color_as_decimal(self.td.get("bgcolor", self.tr.get("bgcolor", ""))) # parsing table header/footer (drawn later): if self.thead is not None: self.theader.append(((width, height, data, border, 0, align), bgcolor)) @@ -442,7 +442,7 @@ def handle_starttag(self, tag, attrs): # save previous font state: self.font_stack.append((self.font_face, self.font_size, self.font_color)) if "color" in attrs: - color = hex2dec(attrs["color"]) + color = color_as_decimal(attrs["color"]) self.font_color = color if "face" in attrs: face = attrs.get("face").lower() diff --git a/fpdf/template.py b/fpdf/template.py index ae44dab80..130c071f6 100644 --- a/fpdf/template.py +++ b/fpdf/template.py @@ -106,7 +106,6 @@ def __setitem__(self, name, value): raise FPDFException(f"Element not loaded, cannot set item: {name}") if not self.pg_no: raise FPDFException("No page open, you need to call add_page() first") - value = "" if value is None else str(value) self.texts[self.pg_no][name.lower()] = value # setitem shortcut (may be further extended) diff --git a/test/image/test_full_page_image.py b/test/image/test_full_page_image.py index 81895f2ac..7cb68d1be 100644 --- a/test/image/test_full_page_image.py +++ b/test/image/test_full_page_image.py @@ -8,7 +8,7 @@ IMAGE_PATH = HERE / "png_images/ba2b2b6e72ca0e4683bb640e2d5572f8.png" -def test_full_width_image(tmp_path): +def test_full_width_image(tmp_path): # issue-166 img = fpdf.image_parsing.get_img_info(IMAGE_PATH) pdf = fpdf.FPDF(format=(img["w"], img["h"])) pdf.set_margin(0) @@ -17,7 +17,7 @@ def test_full_width_image(tmp_path): assert_pdf_equal(pdf, HERE / "full_width_image.pdf", tmp_path) -def test_full_height_image(tmp_path): +def test_full_height_image(tmp_path): # issue-166 img = fpdf.image_parsing.get_img_info(IMAGE_PATH) pdf = fpdf.FPDF(format=(img["w"], img["h"])) pdf.set_margin(0) diff --git a/test/requirements.txt b/test/requirements.txt index be5f82fb2..4ac228320 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -3,3 +3,4 @@ pylint pytest pytest-cov pytest-timeout +qrcode diff --git a/test/template/template_qrcode.pdf b/test/template/template_qrcode.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d2b4a8c3169c339bf5f0a822d9382a2be54f6870 GIT binary patch literal 9515 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 literal 0 HcmV?d00001 diff --git a/test/template/test_template.py b/test/template/test_template.py index 91c191b72..8957cb74c 100644 --- a/test/template/test_template.py +++ b/test/template/test_template.py @@ -1,6 +1,9 @@ from pathlib import Path +import qrcode + from fpdf.template import Template + from ..conftest import assert_pdf_equal HERE = Path(__file__).resolve().parent @@ -149,3 +152,33 @@ def test_template_code39(tmp_path): # issue-161 tmpl = Template(format="A4", title="Sample Code 39 barcode", elements=elements) tmpl.add_page() assert_pdf_equal(tmpl, HERE / "template_code39.pdf", tmp_path) + + +def test_template_qrcode(tmp_path): # issue-175 + elements = [ + { + "name": "barcode_0", + "type": "I", + "x1": 50, + "y1": 50, + "x2": 100, + "y2": 100, + "priority": 0, + "text": None, + }, + { + "name": "barcode_1", + "type": "I", + "x1": 150, + "y1": 150, + "x2": 200, + "y2": 200, + "priority": 0, + "text": None, + }, + ] + tmpl = Template(format="letter", elements=elements) + tmpl.add_page() + tmpl["barcode_0"] = qrcode.make("Test 0").get_image() + tmpl["barcode_1"] = qrcode.make("Test 1").get_image() + assert_pdf_equal(tmpl, HERE / "template_qrcode.pdf", tmp_path)