From fb9af3dcd2a05b53015aabfa3e060eba717f752e Mon Sep 17 00:00:00 2001 From: Lucas Cimon <925560+Lucas-C@users.noreply.github.com> Date: Tue, 28 Sep 2021 22:04:48 +0200 Subject: [PATCH] HTML headings relative sizes can now be configured through an optional `heading_sizes` parameter (#237) --- CHANGELOG.md | 28 +++++++++++------------- fpdf/html.py | 16 +++++++++----- test/html/html_custom_heading_sizes.pdf | Bin 0 -> 2006 bytes test/html/html_headings_line_height.pdf | Bin 2845 -> 2840 bytes test/html/test_html.py | 18 +++++++++++++-- 5 files changed, 39 insertions(+), 23 deletions(-) create mode 100644 test/html/html_custom_heading_sizes.pdf diff --git a/CHANGELOG.md b/CHANGELOG.md index b8760b502..194a4819e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,32 +11,31 @@ and [PEP 440](https://www.python.org/dev/peps/pep-0440/). ### Added - markdown support in `multi_cell()`, thanks to Yeshi Namkhai - base 64 images can now be provided to `FPDF.image`, thanks to @MWhatsUp -- documentation on how to generate datamatrix barcodes using the `pystrich` lib: [documentation section](https://pyfpdf.github.io/fpdf2/Barcodes.html#datamatrix), thanks to @MWhatsUp +- documentation on how to generate datamatrix barcodes using the `pystrich` lib: [documentation section](https://pyfpdf.github.io/fpdf2/Barcodes.html#datamatrix), + thanks to @MWhatsUp +- `write_html`: headings (`

`, `

`...) relative sizes can now be configured through an optional `heading_sizes` parameter ### Fixed -- line height of HTML headings (`

`, `

`...) that were not scaled properly with the font size -- HTML headings (`

`, `

`...) can now contain non-ASCII characters without triggering a `UnicodeEncodeError` -- when using `Template`, CSV column types are now safely parsed, thanks to @gmischler +- `write_html`: headings (`

`, `

`...) can now contain non-ASCII characters without triggering a `UnicodeEncodeError` +- `Template`: CSV column types are now safely parsed, thanks to @gmischler ### Changed -- some `FPDF` should not be used inside a `rotation` context, or things can get broken. - This is now forbidden: an exception is raised in case there is such risk. +- `write_html`: the line height of headings (`

`, `

`...) is now properly scaled with its font size +- some `FPDF` methods should not be used inside a `rotation` context, or things can get broken. + This is now forbidden: an exception is now raised in those cases. ## [2.4.3] - 2021-09-01 ### Added -- support for **emojis**! Me precisely unicode characters above `0xFFFF` in general, thanks to @moe-25 +- support for **emojis**! More precisely unicode characters above `0xFFFF` in general, thanks to @moe-25 - `Template` can now insert justified text - [`get_scale_factor`](https://pyfpdf.github.io/fpdf2/fpdf/util.html#fpdf.util.get_scale_factor) utility function to obtain `FPDF.k` without having to create a document - [`convert_unit`](https://pyfpdf.github.io/fpdf2/fpdf/util.html#fpdf.util.convert_unit) utility function to convert a number, `x,y` point, or list of `x,y` points from one unit to another unit - ### Changed - `fpdf.FPDF()` constructor now accepts ints or floats as a unit, and raises a `ValueError` if an invalid unit is provided. - ### Fixed - `Template` `background` property is now properly supported - [#203](https://github.com/PyFPDF/fpdf2/pull/203) ⚠️ Beware that its default value changed from `0` to `0xffffff`, as a value of **zero would render the background as black**. - `Template.parse_csv`: preserving numeric values when using CSV based templates - [#205](https://github.com/PyFPDF/fpdf2/pull/205) - the code snippet to generate Code 39 barcodes in the documentation was missing the start & end `*` characters. This has been fixed, and a warning is now triggered by the [`FPDF.code39`](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.code39) method when those characters are missing. - ### Fixed - Detect missing `uni=True` when loading cached fonts (page numbering was missing digits) @@ -44,13 +43,12 @@ This has been fixed, and a warning is now triggered by the [`FPDF.code39`](https ### Added - disable font caching when `fpdf.FPDF` constructor invoked with `font_cache_dir=None`, thanks to @moe-25 ! - [`FPDF.circle`](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.circle): new method added, thanks to @viraj-shah18 ! -- `HTMLMixin` / `HTML2FPDF`: support setting HTML font colors by name and short hex codes +- `write_html`: support setting HTML font colors by name and short hex codes - [`FPDF.will_page_break`](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.will_page_break) utility method to let users know in advance when adding an elemnt will trigger a page break. This can be useful to repeat table headers on each page for exemple, _cf._ [documentation on Tables](https://pyfpdf.github.io/fpdf2/Tables.html#repeat-table-header-on-each-page). - [`FPDF.set_link`](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.set_link) now support a new optional `x` parameter to set the horizontal position after following the link - ### Fixed - fixed a bug when `fpdf.Template` was used to render QRCodes, due to a forced conversion to string (#175) @@ -73,7 +71,7 @@ _cf._ [documentation on Tables](https://pyfpdf.github.io/fpdf2/Tables.html#repea - `FPDF.cell`: new optional boolean `center` parameter that positions the cell horizontally - `FPDF.set_link`: new optional `zoom` parameter that sets the zoom level after following the link. Currently ignored by Sumatra PDF Reader, but observed by Adobe Acrobat reader. -- `HTMLMixin` / `HTML2FPDF`: now support `align="justify"` +- `write_html`: now support `align="justify"` - new method `FPDF.image_filter` to control the image filters used for images - `FPDF.add_page`: new optional `duration` & `transition` parameters used for [presentations (documentation page)](https://pyfpdf.github.io/fpdf2/Presentations.html) @@ -100,7 +98,7 @@ _cf._ [documentation on Tables](https://pyfpdf.github.io/fpdf2/Tables.html#repea - new method `FPDF.text_annotation` to insert... Text Annotations - `FPDF.image` now also accepts an `io.BytesIO` as input ### Fixed -- `HTMLMixin` / `HTML2FPDF`: properly handling `` inside `` & allowing to center them horizontally +- `write_html`: properly handling `` inside `` & allowing to center them horizontally ## [2.3.2] - 2021-03-27 ### Added @@ -146,7 +144,7 @@ prevented strings passed first to the text-rendering methods to be displayed. ## [2.2.0] - 2021-01-11 ### Added - new unit tests, a code formatter (`black`) and a linter (`pylint`) to improve code quality -- new boolean parameter `table_line_separators` for `HTMLMixin.write_html` & underlying `HTML2FPDF` constructor +- new boolean parameter `table_line_separators` for `write_html` & underlying `HTML2FPDF` constructor ### Changed - the documentation URL is now simply https://pyfpdf.github.io/fpdf2/ ### Removed diff --git a/fpdf/html.py b/fpdf/html.py index 20bf3542f..2af98e903 100644 --- a/fpdf/html.py +++ b/fpdf/html.py @@ -12,6 +12,7 @@ LOGGER = logging.getLogger(__name__) BULLET_WIN1252 = "\x95" # BULLET character in Windows-1252 encoding +DEFAULT_HEADING_SIZES = dict(h1=2, h2=1.5, h3=1.17, h4=1, h5=0.83, h6=0.67) COLOR_DICT = { "black": "#000000", @@ -198,6 +199,7 @@ def __init__( li_tag_indent=5, table_line_separators=False, ul_bullet_char=BULLET_WIN1252, + heading_sizes=None, **_, ): """ @@ -239,7 +241,9 @@ def __init__( self.theader_out = self.tfooter_out = False self.table_row_height = 0 self.heading_level = None - self.hsize = dict(h1=2, h2=1.5, h3=1.17, h4=1, h5=0.83, h6=0.67) + self.heading_sizes = dict(**DEFAULT_HEADING_SIZES) + if heading_sizes: + self.heading_sizes.update(heading_sizes) self._only_imgs_in_td = False def width2unit(self, length): @@ -405,12 +409,12 @@ def handle_starttag(self, tag, attrs): self.pdf.ln(self.h) if attrs: self.align = attrs.get("align") - if tag in self.hsize: + if tag in self.heading_sizes: self.heading_level = int(tag[1:]) - k = self.hsize[tag] - self.pdf.ln(5 * k) + hsize = self.heading_sizes[tag] + self.pdf.ln(5 * hsize) self.pdf.set_text_color(150, 0, 0) - self.set_font(size=12 * k) + self.set_font(size=12 * hsize) if attrs: self.align = attrs.get("align") if tag == "hr": @@ -552,7 +556,7 @@ def handle_starttag(self, tag, attrs): def handle_endtag(self, tag): # Closing tag LOGGER.debug("ENDTAG %s", tag) - if tag in self.hsize: + if tag in self.heading_sizes: self.heading_level = None self.pdf.ln(self.h) self.set_font() diff --git a/test/html/html_custom_heading_sizes.pdf b/test/html/html_custom_heading_sizes.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e4d2b3332dd435a5fa0bbbf76f1c05cac5955c17 GIT binary patch literal 2006 zcma)7UuYaf7)J#E3>d9G2@OusU@P=yXaC)v#7J`2Yf}?1T(6`y4YIkLT(<1(hTU0{ zQy;`q8o}g2@Ih?_LDAMHp%1MO7ObMa^r6+qh(aEPT0^UE^_$(fyS>{yWI1kUzS;SH zzxjQCW@M&RK1g#K(y)U4h0|zsl+@bG43inV$q@Vp%;T8CY88$M@v-5xjQajQ+m6e$>7AWb44w8*g7fclrI_ex6yovcCA} zt+QvD`jB(3{lc@(^UbZzOKqcl?<9F^YyHLt$G`gKxpy!BXbo@7Zmpd8;sU-9Y~0;` z=kTx2!c#}j+_+`F-nh1xqWA6%pITWX)0>|?xcAk`E4P=Uhd;Sl8~*G5%3ArKXa4>3 z>!CffA78ru(E~$o{P5lFM~)v}`h9wn{_*l-V>8=#=0AUFduZR|7e5t;muHbO2-z~C zhKRi4G(tQlB(KgRQuHGsF(T8f;n)-YDxOnd$tV~()xwreb5>y<^~5y>y_O*gh)mca z6C+G&PK$*HtA5M&5IGht@Hn6%tN?PzgAh1g6O&oT8}~w|V@M&0=q9`&0|LK(jBz|i zAXUIOTLpy47G%eSd>7?=63!A67#)QH#e!#5cqJz)3#VC~V_1M`?<8glr>jEQ2||wZ z@n$k*_r^(HRwC{?9t%@$#J3%~T{k5LI}?pDIq}*_tmG7IDP~SLu@||nAR8lDDI__T zSuf~8*4G^FGQ7XG2gW(PeEiy21Z`b<-0NHz{q1~Vaz_5UW z$R)m=vfF+HYd~YbW@?bh1GfAQ{w!cN_+7ySar*aC#maD@D$QU}P0!kN zBkRvlQk;2UJ1z_08ivm8yvc-qr0V-{Kg3roB9q>tk7;~&h$a+rJK(X_)KpD0GIFR= GM*jneM>OF8 literal 0 HcmV?d00001 diff --git a/test/html/html_headings_line_height.pdf b/test/html/html_headings_line_height.pdf index 24ca3aab55df5485d0d00448d7274c12f526ca5c..66d990926a84bc852f5446f3a68868a8df0d3526 100644 GIT binary patch delta 339 zcmbO$HbZPe1Eb-@Ml+_qsEIo_%1XSsAnSKQ)6MLu2q)i^Y4>;DZan^;-K5U`O1h`q zod4_7@s+`Zuh_A4bj2g zug`aR7)w{Y+Rs>E;li}}J>v~_F#`()0}xQiQ{VzK3=GUHjV9maw5~V7kTJJ3!w|DD zGe;LQG%z>C5HqsCFwfA$5>uVIsUbwK8Nx|M1_qYs1{xVzU}!cnHZhodmdk^~fJ;@? I)!&T^02mcrqW}N^ delta 344 zcmbOsHdkyy1Ebl*Ml+^$K@)dwl(o3A(0a~-;GUdQnnE%fy8DZ_Gxy);&-hcf;`}6; z$?c394vlGt#1F_B3)kP9pgeJ#QE+|kHEBPFRI3V}Et)HN>od4_7@s+`Zuh_A4bj2g zug`aR7)!sq;@#YGTkk=>4Y#k}=2wh2*u@Nu6%0T?Ay0t|%rG!8voxQ4gVVa+5<|wq zzz|c++z4IF(7?g~L(Iqm!#qP%puK3e8=9M%LBz}uPBJnuG(lHrWMqk<*~r+$bn*!< O4-QiH4 {long_title*3}

H5 {long_title*3}
H6 {long_title*4}
-

P {long_title*5}

- """ +

P {long_title*5}

""" ) assert_pdf_equal(pdf, HERE / "html_headings_line_height.pdf", tmp_path) + + +def test_html_custom_heading_sizes(tmp_path): # issue-223 + pdf = MyFPDF() + pdf.add_page() + pdf.write_html( + """

This is a H1

+

This is a H2

+

This is a H3

+

This is a H4

+
This is a H5
+
This is a H6
""", + heading_sizes=dict(h1=0.5, h2=1, h3=1.5, h4=2, h5=2.5, h6=3), + ) + assert_pdf_equal(pdf, HERE / "html_custom_heading_sizes.pdf", tmp_path)