Skip to content

Commit

Permalink
Added Cell level control for Table Borders (#1285)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kaustbh authored Nov 12, 2024
1 parent 63bb170 commit 304d5ca
Show file tree
Hide file tree
Showing 12 changed files with 353 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ This can also be enabled programmatically with `warnings.simplefilter('default',

## [2.8.2] - Not released yet
### Added
* 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
* [`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()`.
* support for LZWDecode compression [issue #1271](https://github.com/py-pdf/fpdf2/issues/1271)
### Fixed
Expand Down
63 changes: 63 additions & 0 deletions docs/Tables.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Result:
* control over borders: color, width & where they are drawn
* handle splitting a table over page breaks, with headings repeated
* control over cell background color
* control over cell borders
* control table width & position
* control over text alignment in cells, globally or per row
* allow to embed images in cells
Expand Down Expand Up @@ -277,6 +278,68 @@ Result:
All the possible layout values are described
there: [`TableBordersLayout`](https://py-pdf.github.io/fpdf2/fpdf/enums.html#fpdf.enums.TableBordersLayout).

## Set cell borders

_New in [:octicons-tag-24: 2.8.2](https://github.com/py-pdf/fpdf2/blob/master/CHANGELOG.md)_

```python
from fpdf import FPDF

pdf = FPDF()
pdf.add_page()
pdf.set_font("Times", size=16)
with pdf.table() as table:
for data_row in TABLE_DATA:
row = table.row()
for datum in data_row:
row.cell(datum, border="LEFT")
pdf.output('table.pdf')
```

Result:

![](table_with_cell_border_left.jpg)

```python
from fpdf import FPDF

pdf = FPDF()
pdf.add_page()
pdf.set_font("Times", size=16)
with pdf.table() as table:
for data_row in TABLE_DATA:
row = table.row()
for datum in data_row:
row.cell(datum, border="TOP")
pdf.output('table.pdf')
```

Result:

![](table_with_cell_border_top.jpg)

```python
from fpdf import FPDF
from fpdf.enums import CellBordersLayout

pdf = FPDF()
pdf.add_page()
pdf.set_font("Times", size=16)
with pdf.table() as table:
for data_row in TABLE_DATA:
row = table.row()
for datum in data_row:
row.cell(datum, border=CellBordersLayout.TOP | CellBordersLayout.LEFT)
pdf.output('table.pdf')
```

Result:

![](table_with_cell_border_left_top.jpg)

All the possible borders values are described there: [`CellBordersLayout`](https://py-pdf.github.io/fpdf2/fpdf/enums.html#fpdf.enums.CellBordersLayout).


## Insert images

```python
Expand Down
Binary file added docs/table_with_cell_border_left.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/table_with_cell_border_left_top.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/table_with_cell_border_top.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 68 additions & 0 deletions fpdf/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,74 @@ class TableBordersLayout(CoerciveEnum):
"Draw only the top horizontal border, below the headings"


class CellBordersLayout(CoerciveIntFlag):
"""Defines how to render cell borders in table
The integer value of `border` determines which borders are applied. Below are some common examples:
- border=1 (LEFT): Only the left border is enabled.
- border=3 (LEFT | RIGHT): Both the left and right borders are enabled.
- border=5 (LEFT | TOP): The left and top borders are enabled.
- border=12 (TOP | BOTTOM): The top and bottom borders are enabled.
- border=15 (ALL): All borders (left, right, top, bottom) are enabled.
- border=16 (INHERIT): Inherit the border settings from the parent element.
Using `border=3` will combine LEFT and RIGHT borders, as it represents the
bitwise OR of `LEFT (1)` and `RIGHT (2)`.
"""

NONE = 0
"Draw no border on any side of cell"

LEFT = 1
"Draw border on the left side of the cell"

RIGHT = 2
"Draw border on the right side of the cell"

TOP = 4
"Draw border on the top side of the cell"

BOTTOM = 8
"Draw border on the bottom side of the cell"

ALL = LEFT | RIGHT | TOP | BOTTOM
"Draw border on all side of the cell"

INHERIT = 16
"Inherits the border layout from the table borders layout"

@classmethod
def coerce(cls, value):
if isinstance(value, int) and value > 16:
raise ValueError("INHERIT cannot be combined with other values")
return super().coerce(value)

def __and__(self, value):
value = super().__and__(value)
if value > 16:
raise ValueError("INHERIT cannot be combined with other values")
return value

def __or__(self, value):
value = super().__or__(value)
if value > 16:
raise ValueError("INHERIT cannot be combined with other values")
return value

def __str__(self):
border_str = []
if self & CellBordersLayout.LEFT:
border_str.append("L")
if self & CellBordersLayout.RIGHT:
border_str.append("R")
if self & CellBordersLayout.TOP:
border_str.append("T")
if self & CellBordersLayout.BOTTOM:
border_str.append("B")
return "".join(border_str) if border_str else "NONE"


class TableCellFillMode(CoerciveEnum):
"Defines which table cells to fill"

Expand Down
11 changes: 11 additions & 0 deletions fpdf/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
WrapMode,
VAlign,
TableSpan,
CellBordersLayout,
)
from .errors import FPDFException
from .fonts import CORE_FONTS, FontFace
Expand Down Expand Up @@ -251,13 +252,18 @@ def render(self):
self._fpdf.l_margin = prev_l_margin
self._fpdf.x = self._fpdf.l_margin

# pylint: disable=too-many-return-statements
def get_cell_border(self, i, j, cell):
"""
Defines which cell borders should be drawn.
Returns a string containing some or all of the letters L/R/T/B,
to be passed to `fpdf.FPDF.multi_cell()`.
Can be overriden to customize this logic
"""

if cell.border != CellBordersLayout.INHERIT:
return str(cell.border)

if self._borders_layout == TableBordersLayout.ALL:
return 1
if self._borders_layout == TableBordersLayout.NONE:
Expand Down Expand Up @@ -770,6 +776,7 @@ def cell(
rowspan=1,
padding=None,
link=None,
border=CellBordersLayout.INHERIT,
):
"""
Adds a cell to the row.
Expand All @@ -788,6 +795,7 @@ def cell(
rowspan (int): optional number of rows this cell should span.
padding (tuple): optional padding (left, top, right, bottom) for the cell.
link (str, int): optional link, either an URL or an integer returned by `FPDF.add_link`, defining an internal link to a page
border (fpdf.enums.CellBordersLayout): optional cell borders, defaults to `CellBordersLayout.INHERIT`
"""
if text and img:
Expand Down Expand Up @@ -819,6 +827,7 @@ def cell(
rowspan,
padding,
link,
CellBordersLayout.coerce(border),
)
self.cells.append(cell)
return cell
Expand All @@ -838,6 +847,7 @@ class Cell:
"rowspan",
"padding",
"link",
"border",
)
text: str
align: Optional[Union[str, Align]]
Expand All @@ -849,6 +859,7 @@ class Cell:
rowspan: int
padding: Optional[Union[int, tuple, type(None)]]
link: Optional[Union[str, int]]
border: Optional[CellBordersLayout]

def write(self, text, align=None):
raise NotImplementedError("Not implemented yet")
Expand Down
Loading

0 comments on commit 304d5ca

Please sign in to comment.