Skip to content

Commit 7e1efc2

Browse files
authored
Allow custom cell_fill_mode in tables (#1117)
1 parent b7e9b11 commit 7e1efc2

7 files changed

+82
-6
lines changed

CHANGELOG.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ This can also be enabled programmatically with `warnings.simplefilter('default',
1919
## [2.7.9] - Not released yet
2020
### Added
2121
* support for overriding paragraph direction on bidirectional text
22-
* new optional `li_prefix_color` parameter for `FPDF.write_html()`
23-
* support for `start` & `type` attributes of `<ol>` tags, and `type` attribute of `<ul>` tags, when using `FPDF.write_html()`
22+
* new optional `li_prefix_color` parameter for [`FPDF.write_html()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.write_html)
23+
* support for `start` & `type` attributes of `<ol>` tags, and `type` attribute of `<ul>` tags, when using [`FPDF.write_html()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.write_html)
2424
* [`FPDF.write_html()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.write_html) now accepts a `tag_styles` parameter to control the font, color & size of HTML elements: `<a>`, `<blockquote>`, `<li>`...
2525
* [`FPDF.write_html()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.write_html) now accepts a `tag_indents` parameter to control, for example, the indent of `<blockquote>` elements
26+
* allow to define custom `cell_fill_mode` logic for tables: [_Set cells background_ - documentation section](https://py-pdf.github.io/fpdf2/Tables.html#set-cells-background). Also added 2 new values: `TableCellFillMode.EVEN_ROWS` & `TableCellFillMode.EVEN_COLUMNS`: [documentation](https://py-pdf.github.io/fpdf2/fpdf/enums.html#fpdf.enums.TableCellFillMode)
2627
### Changed
2728
* improved the performance of `FPDF.start_section()` - _cf._ [issue #1092](https://github.com/py-pdf/fpdf2/issues/1092)
2829
### Deprecated
@@ -52,7 +53,7 @@ This can also be enabled programmatically with `warnings.simplefilter('default',
5253
* outer table borders are now drawn continuously for nonzero `gutter_width`/`gutter_height`, with spacing applied inside the border similar to HTML tables - thanks to @mjasperse - cf. [#1071](https://github.com/py-pdf/fpdf2/issues/1071)
5354
* removed the requirement that all rows in a `Table` have the same number of columns - thanks to @mjasperse
5455
### Deprecated
55-
- font aliases (`Arial``Helvetica`, `CourierNew``Courer`, `TimesNewRoman``Times`). They will be removed in a later release.
56+
- font aliases (`Arial``Helvetica`, `CourierNew``Courier`, `TimesNewRoman``Times`). They will be removed in a later release.
5657

5758
## [2.7.7] - 2023-12-10
5859
### Added

docs/HTML.md

+2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ pdf.output("html.pdf")
8181

8282
## Styling HTML tags globally
8383

84+
_New in [:octicons-tag-24: 2.7.9](https://github.com/py-pdf/fpdf2/blob/master/CHANGELOG.md)_
85+
8486
The style of several HTML tags (`<a>`, `<blockquote>`, `<code>`, `<pre>`, `<h1>`, `<h2>`, `<h3>`...) can be set globally, for the whole HTML document, by passing `tag_styles` to `FPDF.write_html()`:
8587

8688
```python

docs/Tables.md

+13
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,19 @@ The cell color is set following those settings, ordered by priority:
208208
4. The table setting `cell_fill_color`, if `cell_fill_mode` indicates to fill a cell
209209
5. The document `.fill_color` set before rendering the table
210210

211+
Finally, it is possible to define your own cell-filling logic:
212+
213+
```python
214+
class EvenOddCellFillMode():
215+
@staticmethod
216+
def should_fill_cell(i, j):
217+
return i % 2 and j % 2
218+
219+
...
220+
with pdf.table(cell_fill_color=lightblue, cell_fill_mode=EvenOddCellFillMode()) as table:
221+
...
222+
```
223+
211224

212225
## Set borders layout
213226

fpdf/enums.py

+20-3
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def coerce(cls, value):
5454

5555
raise ValueError(f"{value} is not a valid {cls.__name__}")
5656

57-
raise TypeError(f"{value} cannot convert to a {cls.__name__}")
57+
raise TypeError(f"{value} cannot be converted to a {cls.__name__}")
5858

5959

6060
class CoerciveIntEnum(IntEnum):
@@ -317,15 +317,32 @@ class TableCellFillMode(CoerciveEnum):
317317
COLUMNS = intern("COLUMNS")
318318
"Fill only table cells in odd columns"
319319

320+
EVEN_ROWS = intern("EVEN_ROWS")
321+
"Fill only table cells in even rows"
322+
323+
EVEN_COLUMNS = intern("EVEN_COLUMNS")
324+
"Fill only table cells in even columns"
325+
326+
@classmethod
327+
def coerce(cls, value):
328+
"Any class that has a .should_fill_cell() method is considered a valid 'TableCellFillMode' (duck-typing)"
329+
if callable(getattr(value, "should_fill_cell", None)):
330+
return value
331+
return super().coerce(value)
332+
320333
def should_fill_cell(self, i, j):
321334
if self is self.NONE:
322335
return False
323336
if self is self.ALL:
324337
return True
325338
if self is self.ROWS:
326-
return bool(i % 2)
339+
return i % 2 == 1
327340
if self is self.COLUMNS:
328-
return bool(j % 2)
341+
return j % 2 == 1
342+
if self is self.EVEN_ROWS:
343+
return i % 2 == 0
344+
if self is self.EVEN_COLUMNS:
345+
return j % 2 == 0
329346
raise NotImplementedError
330347

331348

test/table/table_cell_fill_mode.pdf

2.01 KB
Binary file not shown.
1.41 KB
Binary file not shown.

test/table/test_table.py

+43
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from fpdf import FPDF, FPDFException
77
from fpdf.drawing import DeviceRGB
88
from fpdf.fonts import FontFace
9+
from fpdf.table import TableCellFillMode
10+
911
from test.conftest import assert_pdf_equal, LOREM_IPSUM
1012

1113

@@ -267,6 +269,27 @@ def test_table_with_cell_fill(tmp_path):
267269
assert_pdf_equal(pdf, HERE / "table_with_cell_fill.pdf", tmp_path)
268270

269271

272+
class EvenOddCellFillMode:
273+
@staticmethod
274+
def should_fill_cell(i, j):
275+
return i % 2 and j % 2
276+
277+
278+
def test_table_with_cell_fill_custom_class(tmp_path):
279+
pdf = FPDF()
280+
pdf.add_page()
281+
pdf.set_font("Times", size=16)
282+
lightblue = (173, 216, 230)
283+
with pdf.table(
284+
cell_fill_color=lightblue, cell_fill_mode=EvenOddCellFillMode()
285+
) as table:
286+
for data_row in TABLE_DATA:
287+
row = table.row()
288+
for datum in data_row:
289+
row.cell(datum)
290+
assert_pdf_equal(pdf, HERE / "table_with_cell_fill_custom_class.pdf", tmp_path)
291+
292+
270293
def test_table_with_internal_layout(tmp_path):
271294
pdf = FPDF()
272295
pdf.add_page()
@@ -732,3 +755,23 @@ def test_table_with_varying_col_count(tmp_path):
732755
table.row(subset)
733756

734757
assert_pdf_equal(pdf, HERE / "table_with_varying_col_count.pdf", tmp_path)
758+
759+
760+
def test_table_cell_fill_mode(tmp_path):
761+
pdf = FPDF()
762+
pdf.add_page()
763+
pdf.set_font("Helvetica")
764+
light_sky_blue = (150, 200, 255)
765+
headings_style = FontFace(fill_color=128) # grey
766+
for mode in ("ROWS", "COLUMNS", "EVEN_ROWS", "EVEN_COLUMNS"):
767+
pdf.cell(text=f"cell_fill_mode=TableCellFillMode.{mode}:")
768+
pdf.ln(10)
769+
with pdf.table(
770+
TABLE_DATA,
771+
headings_style=headings_style,
772+
cell_fill_mode=getattr(TableCellFillMode, mode),
773+
cell_fill_color=light_sky_blue,
774+
):
775+
pass
776+
pdf.ln()
777+
assert_pdf_equal(pdf, HERE / "table_cell_fill_mode.pdf", tmp_path)

0 commit comments

Comments
 (0)