Skip to content

Commit 304d5ca

Browse files
authored
Added Cell level control for Table Borders (#1285)
1 parent 63bb170 commit 304d5ca

12 files changed

+353
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ This can also be enabled programmatically with `warnings.simplefilter('default',
1818

1919
## [2.8.2] - Not released yet
2020
### Added
21+
* new optional parameter `border` for table cells [issue #1192](https://github.com/py-pdf/fpdf2/issues/1192) users can define specific borders (left, right, top, bottom) for individual cells
2122
* [`FPDF.write_html()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.write_html): now parses `<title>` tags to set the [document title](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.set_title). By default, it is added as PDF metadata, but not rendered in the document body. However, this can be enabled by passing `render_title_tag=True` to `FPDF.write_html()`.
2223
* support for LZWDecode compression [issue #1271](https://github.com/py-pdf/fpdf2/issues/1271)
2324
### Fixed

docs/Tables.md

+63
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Result:
3838
* control over borders: color, width & where they are drawn
3939
* handle splitting a table over page breaks, with headings repeated
4040
* control over cell background color
41+
* control over cell borders
4142
* control table width & position
4243
* control over text alignment in cells, globally or per row
4344
* allow to embed images in cells
@@ -277,6 +278,68 @@ Result:
277278
All the possible layout values are described
278279
there: [`TableBordersLayout`](https://py-pdf.github.io/fpdf2/fpdf/enums.html#fpdf.enums.TableBordersLayout).
279280

281+
## Set cell borders
282+
283+
_New in [:octicons-tag-24: 2.8.2](https://github.com/py-pdf/fpdf2/blob/master/CHANGELOG.md)_
284+
285+
```python
286+
from fpdf import FPDF
287+
288+
pdf = FPDF()
289+
pdf.add_page()
290+
pdf.set_font("Times", size=16)
291+
with pdf.table() as table:
292+
for data_row in TABLE_DATA:
293+
row = table.row()
294+
for datum in data_row:
295+
row.cell(datum, border="LEFT")
296+
pdf.output('table.pdf')
297+
```
298+
299+
Result:
300+
301+
![](table_with_cell_border_left.jpg)
302+
303+
```python
304+
from fpdf import FPDF
305+
306+
pdf = FPDF()
307+
pdf.add_page()
308+
pdf.set_font("Times", size=16)
309+
with pdf.table() as table:
310+
for data_row in TABLE_DATA:
311+
row = table.row()
312+
for datum in data_row:
313+
row.cell(datum, border="TOP")
314+
pdf.output('table.pdf')
315+
```
316+
317+
Result:
318+
319+
![](table_with_cell_border_top.jpg)
320+
321+
```python
322+
from fpdf import FPDF
323+
from fpdf.enums import CellBordersLayout
324+
325+
pdf = FPDF()
326+
pdf.add_page()
327+
pdf.set_font("Times", size=16)
328+
with pdf.table() as table:
329+
for data_row in TABLE_DATA:
330+
row = table.row()
331+
for datum in data_row:
332+
row.cell(datum, border=CellBordersLayout.TOP | CellBordersLayout.LEFT)
333+
pdf.output('table.pdf')
334+
```
335+
336+
Result:
337+
338+
![](table_with_cell_border_left_top.jpg)
339+
340+
All the possible borders values are described there: [`CellBordersLayout`](https://py-pdf.github.io/fpdf2/fpdf/enums.html#fpdf.enums.CellBordersLayout).
341+
342+
280343
## Insert images
281344

282345
```python

docs/table_with_cell_border_left.jpg

44.2 KB
Loading
55.5 KB
Loading

docs/table_with_cell_border_top.jpg

56.7 KB
Loading

fpdf/enums.py

+68
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,74 @@ class TableBordersLayout(CoerciveEnum):
310310
"Draw only the top horizontal border, below the headings"
311311

312312

313+
class CellBordersLayout(CoerciveIntFlag):
314+
"""Defines how to render cell borders in table
315+
316+
The integer value of `border` determines which borders are applied. Below are some common examples:
317+
318+
- border=1 (LEFT): Only the left border is enabled.
319+
- border=3 (LEFT | RIGHT): Both the left and right borders are enabled.
320+
- border=5 (LEFT | TOP): The left and top borders are enabled.
321+
- border=12 (TOP | BOTTOM): The top and bottom borders are enabled.
322+
- border=15 (ALL): All borders (left, right, top, bottom) are enabled.
323+
- border=16 (INHERIT): Inherit the border settings from the parent element.
324+
325+
Using `border=3` will combine LEFT and RIGHT borders, as it represents the
326+
bitwise OR of `LEFT (1)` and `RIGHT (2)`.
327+
"""
328+
329+
NONE = 0
330+
"Draw no border on any side of cell"
331+
332+
LEFT = 1
333+
"Draw border on the left side of the cell"
334+
335+
RIGHT = 2
336+
"Draw border on the right side of the cell"
337+
338+
TOP = 4
339+
"Draw border on the top side of the cell"
340+
341+
BOTTOM = 8
342+
"Draw border on the bottom side of the cell"
343+
344+
ALL = LEFT | RIGHT | TOP | BOTTOM
345+
"Draw border on all side of the cell"
346+
347+
INHERIT = 16
348+
"Inherits the border layout from the table borders layout"
349+
350+
@classmethod
351+
def coerce(cls, value):
352+
if isinstance(value, int) and value > 16:
353+
raise ValueError("INHERIT cannot be combined with other values")
354+
return super().coerce(value)
355+
356+
def __and__(self, value):
357+
value = super().__and__(value)
358+
if value > 16:
359+
raise ValueError("INHERIT cannot be combined with other values")
360+
return value
361+
362+
def __or__(self, value):
363+
value = super().__or__(value)
364+
if value > 16:
365+
raise ValueError("INHERIT cannot be combined with other values")
366+
return value
367+
368+
def __str__(self):
369+
border_str = []
370+
if self & CellBordersLayout.LEFT:
371+
border_str.append("L")
372+
if self & CellBordersLayout.RIGHT:
373+
border_str.append("R")
374+
if self & CellBordersLayout.TOP:
375+
border_str.append("T")
376+
if self & CellBordersLayout.BOTTOM:
377+
border_str.append("B")
378+
return "".join(border_str) if border_str else "NONE"
379+
380+
313381
class TableCellFillMode(CoerciveEnum):
314382
"Defines which table cells to fill"
315383

fpdf/table.py

+11
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
WrapMode,
1212
VAlign,
1313
TableSpan,
14+
CellBordersLayout,
1415
)
1516
from .errors import FPDFException
1617
from .fonts import CORE_FONTS, FontFace
@@ -251,13 +252,18 @@ def render(self):
251252
self._fpdf.l_margin = prev_l_margin
252253
self._fpdf.x = self._fpdf.l_margin
253254

255+
# pylint: disable=too-many-return-statements
254256
def get_cell_border(self, i, j, cell):
255257
"""
256258
Defines which cell borders should be drawn.
257259
Returns a string containing some or all of the letters L/R/T/B,
258260
to be passed to `fpdf.FPDF.multi_cell()`.
259261
Can be overriden to customize this logic
260262
"""
263+
264+
if cell.border != CellBordersLayout.INHERIT:
265+
return str(cell.border)
266+
261267
if self._borders_layout == TableBordersLayout.ALL:
262268
return 1
263269
if self._borders_layout == TableBordersLayout.NONE:
@@ -770,6 +776,7 @@ def cell(
770776
rowspan=1,
771777
padding=None,
772778
link=None,
779+
border=CellBordersLayout.INHERIT,
773780
):
774781
"""
775782
Adds a cell to the row.
@@ -788,6 +795,7 @@ def cell(
788795
rowspan (int): optional number of rows this cell should span.
789796
padding (tuple): optional padding (left, top, right, bottom) for the cell.
790797
link (str, int): optional link, either an URL or an integer returned by `FPDF.add_link`, defining an internal link to a page
798+
border (fpdf.enums.CellBordersLayout): optional cell borders, defaults to `CellBordersLayout.INHERIT`
791799
792800
"""
793801
if text and img:
@@ -819,6 +827,7 @@ def cell(
819827
rowspan,
820828
padding,
821829
link,
830+
CellBordersLayout.coerce(border),
822831
)
823832
self.cells.append(cell)
824833
return cell
@@ -838,6 +847,7 @@ class Cell:
838847
"rowspan",
839848
"padding",
840849
"link",
850+
"border",
841851
)
842852
text: str
843853
align: Optional[Union[str, Align]]
@@ -849,6 +859,7 @@ class Cell:
849859
rowspan: int
850860
padding: Optional[Union[int, tuple, type(None)]]
851861
link: Optional[Union[str, int]]
862+
border: Optional[CellBordersLayout]
852863

853864
def write(self, text, align=None):
854865
raise NotImplementedError("Not implemented yet")

0 commit comments

Comments
 (0)