From 7154186be22747b87d6ac8e366ec83b1e9bacb2f Mon Sep 17 00:00:00 2001 From: Lucas Cimon <925560+Lucas-C@users.noreply.github.com> Date: Wed, 7 Jun 2023 13:00:43 +0200 Subject: [PATCH] cell() & multi_cell() horizontal centering (#809) --- CHANGELOG.md | 3 ++ fpdf/fpdf.py | 45 +++++++++--------- test/text/cell_centering_and_align_x.pdf | Bin 0 -> 1029 bytes test/text/multi_cell_align_x.pdf | Bin 0 -> 1970 bytes test/text/multi_cell_centering.pdf | Bin 0 -> 1994 bytes .../text/multi_cell_centering_and_align_x.pdf | Bin 0 -> 1975 bytes test/text/test_cell.py | 13 ++++- test/text/test_multi_cell.py | 29 +++++++++++ 8 files changed, 65 insertions(+), 25 deletions(-) create mode 100644 test/text/cell_centering_and_align_x.pdf create mode 100644 test/text/multi_cell_align_x.pdf create mode 100644 test/text/multi_cell_centering.pdf create mode 100644 test/text/multi_cell_centering_and_align_x.pdf diff --git a/CHANGELOG.md b/CHANGELOG.md index 05b0d792c..f4f56f1a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,9 +20,12 @@ This can also be enabled programmatically with `warnings.simplefilter('default', ### Added - [`FPDF.mirror()`](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.mirror) - New method: [documentation page](https://pyfpdf.github.io/fpdf2/Transformations.html) - Contributed by @sebastiantia - [`FPDF.table()`](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.table): new optional parameters `gutter_height`, `gutter_width` and `wrapmode` +- [`FPDF.multi_cell()`](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.multi_cell): has a new optional `center` parameter to position the cell horizontally at the center of the page - Added Tutorial in Khmer language - thanks to @kuth-chi ### Fixed - [`FPDF.image()`](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.image): allowing images path starting with `data` to be passed as input +### Deprecated +- the `center` optional parameter of [`FPDF.cell()`](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.cell) is **no more** deprecated, as it allows for horizontal positioning, which is different from text alignment control with `align="C"` ## [2.7.4] - 2023-04-28 ### Added diff --git a/fpdf/fpdf.py b/fpdf/fpdf.py index e9491dfbf..76af3d0ef 100644 --- a/fpdf/fpdf.py +++ b/fpdf/fpdf.py @@ -2593,7 +2593,7 @@ def cell( align=Align.L, fill=False, link="", - center="DEPRECATED", + center=False, markdown=False, new_x=XPos.RIGHT, new_y=YPos.TOP, @@ -2622,15 +2622,14 @@ def cell( new_x (fpdf.enums.XPos, str): New current position in x after the call. Default: RIGHT new_y (fpdf.enums.YPos, str): New current position in y after the call. Default: TOP ln (int): **DEPRECATED since 2.5.1**: Use `new_x` and `new_y` instead. - align (fpdf.enums.Align, str): Allows to center or align the text inside the cell. + align (fpdf.enums.Align, str): Set text alignment inside the cell. Possible values are: `L` or empty string: left align (default value) ; - `C`: center; `X`: center around current x; `R`: right align + `C`: center; `X`: center around current x position; `R`: right align fill (bool): Indicates if the cell background must be painted (`True`) or transparent (`False`). Default value: False. link (str): optional link to add on the cell, internal (identifier returned by `FPDF.add_link`) or external URL. - center (bool): **DEPRECATED** since 2.5.1: - Use align="C" or align="X" instead. + center (bool): center the cell horizontally on the page. markdown (bool): enable minimal markdown-like markup to render part of text as bold / italics / underlined. Default to False. @@ -2656,16 +2655,6 @@ def cell( raise ValueError( "cell() only produces one text line, justified alignment is not possible" ) - if center == "DEPRECATED": - center = False - else: - warnings.warn( - 'The parameter "center" is deprecated. Use align="C" or align="X" instead.', - DeprecationWarning, - stacklevel=3, - ) - if align == Align.L: - align = Align.C if ln != "DEPRECATED": # For backwards compatibility, if "ln" is used we overwrite "new_[xy]". if ln == 0: @@ -2709,6 +2698,7 @@ def cell( align=align, fill=fill, link=link, + center=center, ) def _render_styled_text_line( @@ -2722,6 +2712,7 @@ def _render_styled_text_line( align: Align = Align.L, fill: bool = False, link: str = "", + center: bool = False, ): """ Prints a cell (rectangular area) with optional borders, background color and @@ -2756,8 +2747,7 @@ def _render_styled_text_line( or transparent (`False`). Default value: False. link (str): optional link to add on the cell, internal (identifier returned by `FPDF.add_link`) or external URL. - markdown (bool): enable minimal markdown-like markup to render part - of text as bold / italics / underlined. Default to False. + center (bool): center the cell horizontally on the page. Returns: a boolean indicating if page break was triggered """ @@ -2782,6 +2772,12 @@ def _render_styled_text_line( "A 'text_line' parameter with fragments must be provided if 'w' is None" ) w = styled_txt_width + self.c_margin + self.c_margin + if center: + self.x = ( + self.w / 2 if align == Align.X else self.l_margin + (self.epw - w) / 2 + ) + if align == Align.X: + self.x -= w / 2 max_font_size = 0 # how much height we need to accomodate. # currently all font sizes within a line are vertically aligned on the baseline. for frag in text_line.fragments: @@ -2789,10 +2785,6 @@ def _render_styled_text_line( max_font_size = frag.font_size if h is None: h = max_font_size - if align == Align.X: - self.x -= w / 2 - # if center_cell: - # self.x = self.l_margin + (self.epw - w) / 2 page_break_triggered = self._perform_page_break_if_need_be(h) sl = [] k = self.k @@ -3276,6 +3268,7 @@ def multi_cell( wrapmode: WrapMode = WrapMode.WORD, dry_run=False, output=MethodReturnValue.PAGE_BREAK, + center=False, ): """ This method allows printing text with line breaks. They can be automatic @@ -3294,10 +3287,10 @@ def multi_cell( or a string containing some or all of the following characters (in any order): `L`: left ; `T`: top ; `R`: right ; `B`: bottom. Default value: 0. - align (fpdf.enums.Align, str): Allows to center or align the text. + align (fpdf.enums.Align, str): Set text alignment inside the cell. Possible values are: `J`: justify (default value); `L` or empty string: left align; - `C`: center; `X`: center around current x; `R`: right align + `C`: center; `X`: center around current x position; `R`: right align fill (bool): Indicates if the cell background must be painted (`True`) or transparent (`False`). Default value: False. split_only (bool): **DEPRECATED since 2.7.4**: @@ -3318,6 +3311,7 @@ def multi_cell( Can be useful when combined with `output`. output (fpdf.enums.MethodReturnValue): defines what this method returns. If several enum values are joined, the result will be a tuple. + center (bool): center the cell horizontally on the page. Using `new_x=XPos.RIGHT, new_y=XPos.TOP, maximum height=pdf.font_size` is useful to build tables with multiline text in cells. @@ -3352,6 +3346,7 @@ def multi_cell( dry_run=False, split_only=False, output=MethodReturnValue.LINES if split_only else output, + center=center, ) wrapmode = WrapMode.coerce(wrapmode) if isinstance(w, str) or isinstance(h, str): @@ -3398,6 +3393,10 @@ def multi_cell( # If width is 0, set width to available width between margins if w == 0: w = self.w - self.r_margin - self.x + if center: + self.x = ( + self.w / 2 if align == Align.X else self.l_margin + (self.epw - w) / 2 + ) maximum_allowed_width = w - 2 * self.c_margin # Calculate text length diff --git a/test/text/cell_centering_and_align_x.pdf b/test/text/cell_centering_and_align_x.pdf new file mode 100644 index 0000000000000000000000000000000000000000..edf65f7eb564c5c0eca08714d86fafeca2882e01 GIT binary patch literal 1029 zcmaizO=uHA6vsu>!_b4bqWCdLLo1rjPLfS_B^Huw7o#l++lts&VUkSa%4VkQPG~BA zTvY_Yi#^yPC{%?aiiignAL_9E!PnS7X~Jvf)y{m;Drd-Iz&k)mZs z@o*eqs6k%72-0bQa=gI=!~mUgs{t&<<&rZE&^WESB+Hwyq)A0L_2HNSjRYPxOu0Ha zKT9Dhk{S)b$OxdUs_bGR+rlhmnTl{Z>v%$ByW#|74bPJXDs+s@^M-(^z-W$pjXG<~ z0aWEV-6T08h{tQ8G^)g2^-NGE0+?vW6p1f?yABQwc*C#I04BqhDtX9Hg?9aH*Avv2 zwR%KQi&l7*0yIWhO+{v-8>d`i?@e z@57B>SB}5fx3K5ax5uliUssNNTp^2(oKN19jn5C8&sPR^-OOJ=x^)^v9bP9A>?o5*aaj$bGcL=p!0ieEO*G1)b)D)`WgoQfR(XUNDSrPL zDf@I9*%+$Kj%0$0KCetr0ZT~sn9NO^vVweFg3fm4TxZTthip?J+nmIz`o)su;gEF< zn@JPL;uxEn98=gF)U?6Fq2Q)ZXMhF`pw`<1CUrfo!x`AqOxRk2tf!gsG-`x2Y;N5b zOKsH>DcR(kV)c}4*iBkeH+q`ziR)3n_1q@hOH`JO9L@z)4@Ne6p3QKmP^fPUmeN_Q uYg!_cOT;Wat78jm2`gjAaY~{vtax(d|2tZg13`Sz!j!@$h(tyUHuwv(fGJ}D literal 0 HcmV?d00001 diff --git a/test/text/multi_cell_align_x.pdf b/test/text/multi_cell_align_x.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d2fddffb816a1f5daa95609d9f08a71f213b0e59 GIT binary patch literal 1970 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 literal 0 HcmV?d00001 diff --git a/test/text/multi_cell_centering_and_align_x.pdf b/test/text/multi_cell_centering_and_align_x.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0222e3daa030218840d2ae9af821778fc7dbe4d8 GIT binary patch literal 1975 zcmai#dpJ~i7{}XvluXi7ExL>xDw3Ev!`w7zZpt+~2FWCcIW&wpoH-gsmu;F&2_>bH z;%TLks1zw(w$zq%FU6Bv+3iBuU8IuQb6mFBr$6@h&+nY~{k`A!{k`WrpLf2$z{3G_ zbRvNO6+j|lNE{A{!bcJn7yyze-ZH5Y2zA0;qE#e{FD#WoJR}JSrQ#Mko9;+s0W4?G zk;TTn;^cT3p!h@4u#)8FMuHVmLXM^}Cw8K6j%%W(lB)CWpN z5;1_XTmkbDd19Pm#H<8}@ zM#4&fp)W}!*V7|H$Mig8atucCTICQ13*bmZ3X>>4up$~;1AtTpod{E6C=A7slJeH? zt=sk1*?7sokm<^+HY|#5qe!AVyn=hrP>lUSu z-uK&-pWn0*ac_B^nyxv?=M)T+ElGdQtzsIog|!bOXyEoZ0a^s`h>nwC3d;la14Y+j*P$8R35K|7PpD#8F2R$ct~I zVR__^Tx(u!F8{{~*TEWIptZd>S#A`wVlRL4QJ1N0;@-;c0%O;Nh>LmT*B{pz9CI-d z%>3bo@v&)_g3JvH-Rj+5pLXv*zr_8`#`KmpzHgORRPM**6!Rd?e=g7K$ekm%WI}ieKyRpQ_7GAP>=Us$h7Y}?ZTBQ!wu9k`*cftB}Kd5|Gz)U6OompSlz+;ZL|ut8lrukSy2KTgE&XGTu==`Qm>1JBu&X4YwNLUM<8F>4!W)R|Pc zBkSFT+G&!mo!nE)>ditjW40H(gT3TJEn2~gFMa4>-_2aqc5{*eRphqk#FS@I#{tm5ZuQ;pLOoPks!kRg0{THwoH)85a$ z^Fh1)C8zw3QRD41&+j_r6?^uV#g?gV7F7AAtUXxa@K|)xCVV#fa3?uCxUQxli&?ho z%Z7Q!s#>i!zgW1DD|S`Mmt{7`-g({EGuf-Mt7J}ncJl_!R&jha-cO<0%C4)l)KSw6 zL77Qyt3{_7z%oqhFU5_|CNC|r&II}2XSsbi% z5Fv38PC}sqXOBz~4N!t*3a&ya8#9n70f`aVFnPz53b06oe`Em+I^Bs5L;+uIY+%?0D8Aa*_-%-# zX9L;ao})3pv4PGYUh;oqK|1)29>}2LV`c0dhC(tqj1JGo0GS%bOQwhr1Otc(hfgL? zMHB)MtstHSKq$wV&lWQI9(<6;aArC482I9`sdOF{#78ER#d7=q6T@9rVi1ZAw+Uo0 NoJsTN3zvJ4{sBmN10(