Skip to content

Commit 2bdc5a9

Browse files
authored
Improve outer border behaviour with nonzero gutter (#1087)
* table: Changed drawing of outer border when gutter is nonzero [#1071] * test: Regenerated tests for new handling of outer border * table: Apply the gutter spacing between outer border and table contents [#1071] * Updated changelog for table outer border modification * table: Changed outer border calculation to use border edge instead of centre * table: Draw top and bottom outer border as one line * test: Regenerated tests for new interpretation of gutter spacing * table: Fixed header with gutter after pagebreak Header rows need gutter between them, as well as before the start of content; see test_table_with_colspan() * test: Reproduced comparison PDFs for outer border change Outer border top and bottom lines are now single continuous lines instead of many line segments
1 parent 9b18a9f commit 2bdc5a9

7 files changed

+38
-10
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ This can also be enabled programmatically with `warnings.simplefilter('default',
2424
* non-bold `TitleStyle` is now rendered as non-bold even when the current font is bold
2525
* calling `.table()` inside the `render_toc_function`
2626
* using `.set_text_shaping(True)` & `.offset_rendering()`
27+
* Fixed gutter handing when a pagebreak occurs within a table with header rows - thanks to @mjasperse
28+
### Changed
29+
* 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)
2730

2831
## [2.7.7] - 2023-12-10
2932
### Added

fpdf/table.py

+33-8
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,13 @@ def __init__(
109109
"outer_border_width is only allowed when borders_layout is ALL or NO_HORIZONTAL_LINES"
110110
)
111111
self._outer_border_width = 0
112+
if self._outer_border_width:
113+
self._outer_border_margin = (
114+
(gutter_width + outer_border_width / 2),
115+
(gutter_height + outer_border_width / 2),
116+
)
117+
else:
118+
self._outer_border_margin = (0, 0)
112119

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

188195
# Pre-Compute the relative x-positions of the individual columns:
189-
cell_x_positions = [0]
196+
xx = self._outer_border_margin[0]
197+
cell_x_positions = [xx]
190198
if self.rows:
191-
xx = 0
192199
for i in range(self.rows[0].cols_count):
193200
xx += self._get_col_width(0, i)
194201
xx += self._gutter_width
195202
cell_x_positions.append(xx)
196203

197204
# Starting the actual rows & cells rendering:
205+
self._fpdf.y += self._outer_border_margin[1]
198206
for i in range(len(self.rows)):
199207
row_layout_info = self._get_row_layout_info(i)
200208
if row_layout_info.triggers_page_jump:
201209
# pylint: disable=protected-access
202210
self._fpdf._perform_page_break()
211+
self._fpdf.y += self._outer_border_margin[1]
203212
# repeat headings on top:
204213
for row_idx in range(self._num_heading_rows):
205214
self._render_table_row(
206215
row_idx,
207216
self._get_row_layout_info(row_idx),
208217
cell_x_positions=cell_x_positions,
209218
)
219+
self._fpdf.y += self._gutter_height
210220
elif i and self._gutter_height:
211221
self._fpdf.y += self._gutter_height
212222
self._render_table_row(
@@ -384,14 +394,26 @@ def _render_table_cell(
384394
_remember_linewidth = self._fpdf.line_width
385395
self._fpdf.set_line_width(self._outer_border_width)
386396

387-
if i == 0:
388-
self._fpdf.line(x1, y1, x2, y1)
389-
if i == len(self.rows) - 1:
390-
self._fpdf.line(x1, y2, x2, y2)
397+
# draw the outer box separated by the gutter dimensions
398+
# the top and bottom borders are one continuous line
399+
# whereas the left and right borders are segments beause of possible pagebreaks
400+
x1 = self._fpdf.l_margin
401+
x2 = x1 + self._width
402+
y1 = y1 - self._outer_border_margin[1]
403+
y2 = y2 + self._outer_border_margin[1]
404+
391405
if j == 0:
406+
# lhs border
392407
self._fpdf.line(x1, y1, x1, y2)
393408
if j == len(row.cells) - 1:
409+
# rhs border
394410
self._fpdf.line(x2, y1, x2, y2)
411+
# continuous top line border
412+
if i == 0:
413+
self._fpdf.line(x1, y1, x2, y1)
414+
# continuous bottom line border
415+
if i == len(self.rows) - 1:
416+
self._fpdf.line(x1, y2, x2, y2)
395417

396418
self._fpdf.set_line_width(_remember_linewidth)
397419

@@ -483,8 +505,11 @@ def _get_col_width(self, i, j, colspan=1):
483505
between columns if the cell spans multiple columns."""
484506

485507
cols_count = self.rows[i].cols_count
486-
width = self._width - (cols_count - 1) * self._gutter_width
487-
508+
width = (
509+
self._width
510+
- (cols_count - 1) * self._gutter_width
511+
- 2 * self._outer_border_margin[0]
512+
)
488513
gutter_within_cell = max((colspan - 1) * self._gutter_width, 0)
489514

490515
if not self._col_widths:

test/table/table_with_colspan.pdf

10 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.
-49 Bytes
Binary file not shown.

test/table/test_table_padding.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
HERE = Path(__file__).resolve().parent
1313

1414

15-
def run_comparison(pdf, name, tmp_path):
15+
def run_comparison(pdf, name, tmp_path, generate=False):
1616
filename = HERE / f"{name}.pdf"
17-
assert_pdf_equal(pdf, filename, tmp_path, generate=False)
17+
assert_pdf_equal(pdf, filename, tmp_path, generate=generate)
1818

1919

2020
IMG_DIR = HERE.parent / "image"

0 commit comments

Comments
 (0)