Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve outer border behaviour with nonzero gutter #1087

Merged
merged 9 commits into from
Jan 26, 2024
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ This can also be enabled programmatically with `warnings.simplefilter('default',
* non-bold `TitleStyle` is now rendered as non-bold even when the current font is bold
* calling `.table()` inside the `render_toc_function`
* using `.set_text_shaping(True)` & `.offset_rendering()`
* Fixed gutter handing when a pagebreak occurs within a table with header rows - thanks to @mjasperse
### Changed
* 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)

## [2.7.7] - 2023-12-10
### Added
Expand Down
41 changes: 33 additions & 8 deletions fpdf/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ def __init__(
"outer_border_width is only allowed when borders_layout is ALL or NO_HORIZONTAL_LINES"
)
self._outer_border_width = 0
if self._outer_border_width:
self._outer_border_margin = (
(gutter_width + outer_border_width / 2),
(gutter_height + outer_border_width / 2),
)
else:
self._outer_border_margin = (0, 0)

# check first_row_as_headings for non-default case num_heading_rows != 1
if self._num_heading_rows != 1:
Expand Down Expand Up @@ -186,27 +193,30 @@ def render(self):
self._fpdf.l_margin = self._fpdf.x

# Pre-Compute the relative x-positions of the individual columns:
cell_x_positions = [0]
xx = self._outer_border_margin[0]
cell_x_positions = [xx]
if self.rows:
xx = 0
for i in range(self.rows[0].cols_count):
xx += self._get_col_width(0, i)
xx += self._gutter_width
cell_x_positions.append(xx)

# Starting the actual rows & cells rendering:
self._fpdf.y += self._outer_border_margin[1]
for i in range(len(self.rows)):
row_layout_info = self._get_row_layout_info(i)
if row_layout_info.triggers_page_jump:
# pylint: disable=protected-access
self._fpdf._perform_page_break()
self._fpdf.y += self._outer_border_margin[1]
# repeat headings on top:
for row_idx in range(self._num_heading_rows):
self._render_table_row(
row_idx,
self._get_row_layout_info(row_idx),
cell_x_positions=cell_x_positions,
)
self._fpdf.y += self._gutter_height
elif i and self._gutter_height:
self._fpdf.y += self._gutter_height
self._render_table_row(
Expand Down Expand Up @@ -384,14 +394,26 @@ def _render_table_cell(
_remember_linewidth = self._fpdf.line_width
self._fpdf.set_line_width(self._outer_border_width)

if i == 0:
self._fpdf.line(x1, y1, x2, y1)
if i == len(self.rows) - 1:
self._fpdf.line(x1, y2, x2, y2)
# draw the outer box separated by the gutter dimensions
# the top and bottom borders are one continuous line
# whereas the left and right borders are segments beause of possible pagebreaks
x1 = self._fpdf.l_margin
x2 = x1 + self._width
y1 = y1 - self._outer_border_margin[1]
y2 = y2 + self._outer_border_margin[1]

if j == 0:
# lhs border
self._fpdf.line(x1, y1, x1, y2)
if j == len(row.cells) - 1:
# rhs border
self._fpdf.line(x2, y1, x2, y2)
# continuous top line border
if i == 0:
self._fpdf.line(x1, y1, x2, y1)
# continuous bottom line border
if i == len(self.rows) - 1:
self._fpdf.line(x1, y2, x2, y2)

self._fpdf.set_line_width(_remember_linewidth)

Expand Down Expand Up @@ -481,8 +503,11 @@ def _get_col_width(self, i, j, colspan=1):
between columns if the cell spans multiple columns."""

cols_count = self.rows[i].cols_count
width = self._width - (cols_count - 1) * self._gutter_width

width = (
self._width
- (cols_count - 1) * self._gutter_width
- 2 * self._outer_border_margin[0]
)
gutter_within_cell = max((colspan - 1) * self._gutter_width, 0)

if not self._col_widths:
Expand Down
Binary file modified test/table/table_with_colspan.pdf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified test/table/table_with_outside_border_width.pdf
Binary file not shown.
4 changes: 2 additions & 2 deletions test/table/test_table_padding.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
HERE = Path(__file__).resolve().parent


def run_comparison(pdf, name, tmp_path):
def run_comparison(pdf, name, tmp_path, generate=False):
filename = HERE / f"{name}.pdf"
assert_pdf_equal(pdf, filename, tmp_path, generate=False)
assert_pdf_equal(pdf, filename, tmp_path, generate=generate)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a reason to leave the generate parameter here but it's not a showstopper.



IMG_DIR = HERE.parent / "image"
Expand Down
Loading