Skip to content

Commit

Permalink
cell() & multi_cell() horizontal centering (#809)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lucas-C committed Jun 7, 2023
1 parent 44dd0a4 commit 7154186
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 25 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
45 changes: 22 additions & 23 deletions fpdf/fpdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand All @@ -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:
Expand Down Expand Up @@ -2709,6 +2698,7 @@ def cell(
align=align,
fill=fill,
link=link,
center=center,
)

def _render_styled_text_line(
Expand All @@ -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
Expand Down Expand Up @@ -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
"""
Expand All @@ -2782,17 +2772,19 @@ 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:
if frag.font_size > max_font_size:
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
Expand Down Expand Up @@ -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
Expand All @@ -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**:
Expand All @@ -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.
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down
Binary file added test/text/cell_centering_and_align_x.pdf
Binary file not shown.
Binary file added test/text/multi_cell_align_x.pdf
Binary file not shown.
Binary file added test/text/multi_cell_centering.pdf
Binary file not shown.
Binary file added test/text/multi_cell_centering_and_align_x.pdf
Binary file not shown.
13 changes: 11 additions & 2 deletions test/text/test_cell.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,20 @@ def test_cell_centering(tmp_path):
pdf = FPDF()
pdf.add_page()
pdf.set_font("Times", size=60)
with pytest.warns(DeprecationWarning):
pdf.cell(txt="Lorem ipsum", border=1, center=True)
pdf.cell(txt="Lorem ipsum", border=1, center=True)
assert_pdf_equal(pdf, HERE / "cell_centering.pdf", tmp_path)


def test_cell_centering_and_align_x(tmp_path):
pdf = FPDF()
pdf.add_page()
pdf.set_font("Times", size=16)
pdf.cell(txt="Lorem ipsum", border=1, center=True, align="X")
pdf.set_draw_color(r=0, g=255, b=0)
pdf.line(pdf.w / 2, 0, pdf.w / 2, pdf.h)
assert_pdf_equal(pdf, HERE / "cell_centering_and_align_x.pdf", tmp_path)


def test_cell_markdown(tmp_path):
pdf = FPDF()
pdf.add_page()
Expand Down
29 changes: 29 additions & 0 deletions test/text/test_multi_cell.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,3 +422,32 @@ def test_multi_cell_char_wrap(tmp_path): # issue #649
pdf.ln()
pdf.multi_cell(w=50, txt=txt, new_x="LEFT", fill=True, align="L", wrapmode="CHAR")
assert_pdf_equal(pdf, HERE / "multi_cell_char_wrap.pdf", tmp_path)


def test_multi_cell_centering(tmp_path):
pdf = FPDF()
pdf.add_page()
pdf.set_font("Times", size=16)
pdf.multi_cell(w=120, txt=LOREM_IPSUM, border=1, center=True)
assert_pdf_equal(pdf, HERE / "multi_cell_centering.pdf", tmp_path)


def test_multi_cell_align_x(tmp_path):
pdf = FPDF()
pdf.add_page()
pdf.set_font("Times", size=16)
pdf.set_x(140)
pdf.multi_cell(w=120, txt=LOREM_IPSUM, border=1, align="X")
pdf.set_draw_color(r=0, g=255, b=0)
pdf.line(140, 0, 140, pdf.h)
assert_pdf_equal(pdf, HERE / "multi_cell_align_x.pdf", tmp_path)


def test_multi_cell_centering_and_align_x(tmp_path):
pdf = FPDF()
pdf.add_page()
pdf.set_font("Times", size=16)
pdf.multi_cell(w=120, txt=LOREM_IPSUM, border=1, center=True, align="X")
pdf.set_draw_color(r=0, g=255, b=0)
pdf.line(pdf.w / 2, 0, pdf.w / 2, pdf.h)
assert_pdf_equal(pdf, HERE / "multi_cell_centering_and_align_x.pdf", tmp_path)

0 comments on commit 7154186

Please sign in to comment.