+ if width or height:
+ LOGGER.warning(
+ 'Ignoring unsupported "width" / "height" set on element'
+ )
+ if self.align:
+ LOGGER.warning("Ignoring unsupported alignment")
+ self.table_row.cell(img=attrs["src"], img_fill_width=True)
+ self.td_th["rendered"] = True
+ return
if self.pdf.y + height > self.pdf.page_break_trigger:
self.pdf.add_page(same=True)
- y = self.pdf.get_y()
- if self.table_col_index is not None:
- self._only_imgs_in_td = True
- # in a : its width must not exceed the cell width:
- td_width = self._td_width()
- if not width or width > td_width:
- if width: # Preserving image aspect ratio:
- height *= td_width / width
- width = td_width
- x = self._td_x()
- if self.align and self.align[0].upper() == "C":
- x += (td_width - width) / 2
- else:
- x = self.pdf.get_x()
- if self.align and self.align[0].upper() == "C":
- x = self.pdf.w / 2 - width / 2
+ x, y = self.pdf.get_x(), self.pdf.get_y()
+ if self.align and self.align[0].upper() == "C":
+ x = self.pdf.w / 2 - width / 2
LOGGER.debug(
'image "%s" x=%d y=%d width=%d height=%d',
attrs["src"],
@@ -636,20 +497,10 @@ def handle_starttag(self, tag, attrs):
width,
height,
)
- image_info = self.pdf.image(
+ info = self.pdf.image(
self.image_map(attrs["src"]), x, y, width, height, link=self.href
)
- width = image_info["rendered_width"]
- height = image_info["rendered_height"]
- self.pdf.set_x(x + width)
- if self.table_col_index is not None:
- # in a | : we grow the cell height according to the image height:
- if height > self.table_row_height:
- self.table_row_height = height
- else:
- self.pdf.set_y(y + height)
- if tag in ("b", "i", "u"):
- self.set_style(tag, True)
+ self.pdf.set_y(y + info.rendered_height)
if tag == "center":
self.align = "Center"
if tag == "toc":
@@ -708,7 +559,8 @@ def handle_endtag(self, tag):
if tag == "em":
tag = "i"
if tag in ("b", "i", "u"):
- self.set_style(tag, False)
+ if not self.td_th is not None:
+ self.set_style(tag, False)
self.follows_fmt_tag = True
if tag == "a":
self.href = ""
@@ -720,37 +572,22 @@ def handle_endtag(self, tag):
self.indent -= 1
self.bullet.pop()
if tag == "table":
- if not self.tfooter_out:
- self.output_table_footer()
+ with self.pdf.local_context(line_width=0.2):
+ self.table.render()
self.table = None
- self.th = False
- self.theader = None
- self.tfooter = None
self.pdf.ln(self.h)
- self.tr_index = None
- if tag == "thead":
- self.thead = None
- self.tr_index = None
- if tag == "tfoot":
- self.tfoot = None
- self.tr_index = None
- if tag == "tbody":
- self.tbody = None
- self.tr_index = None
if tag == "tr":
- if self.tfoot is None:
- self.pdf.ln(self.table_row_height)
- self.table_col_index = None
self.tr = None
+ self.table_row = None
if tag in ("td", "th"):
- if self.th:
- LOGGER.debug("revert style")
- self.set_style("b", False) # revert style
- elif self._only_imgs_in_td:
- self._insert_td()
- self.table_col_index += int(self.td.get("colspan", "1"))
- self.td = None
- self.th = False
+ if "rendered" not in self.td_th:
+ # handle_data() was not called => we call it to produce an empty cell:
+ bgcolor = color_as_decimal(
+ self.td_th.get("bgcolor", self.tr.get("bgcolor", None))
+ )
+ style = FontStyle(fill_color=bgcolor) if bgcolor else None
+ self.table_row.cell(text="", style=style)
+ self.td_th = None
if tag == "font":
# recover last font state
face, size, color = self.font_stack.pop()
diff --git a/fpdf/table.py b/fpdf/table.py
index 8676b7315..3ad25c150 100644
--- a/fpdf/table.py
+++ b/fpdf/table.py
@@ -1,6 +1,7 @@
from contextlib import contextmanager
+from dataclasses import dataclass
from numbers import Number
-from typing import List
+from typing import List, Union
from .enums import Align, TableBordersLayout
from .fonts import FontStyle
@@ -17,7 +18,7 @@ class Table:
def __init__(self, fpdf):
self._fpdf = fpdf
- self._rows = []
+ self.rows = []
self.align = "CENTER"
"""
Sets the table horizontal position relative to the page,
@@ -46,7 +47,7 @@ def __init__(self, fpdf):
def row(self):
"Adds a row to the table. Yields a `Row` object."
row = Row()
- self._rows.append(row)
+ self.rows.append(row)
yield row
def render(self):
@@ -67,22 +68,15 @@ def render(self):
self._fpdf.x = self._fpdf.l_margin
elif self._fpdf.x != self._fpdf.l_margin:
self._fpdf.l_margin = self._fpdf.x
- for i in range(len(self._rows)):
+ for i in range(len(self.rows)):
with self._fpdf.offset_rendering() as test:
- self._render_table_row_styled(i)
+ self._render_table_row(i)
if test.page_break_triggered:
# pylint: disable=protected-access
self._fpdf._perform_page_break()
if self.first_row_as_headings: # repeat headings on top:
- self._render_table_row_styled(0)
- if self.cell_fill_color:
- prev_fill_color = self._fpdf.fill_color
- self._fpdf.set_fill_color(self.cell_fill_color)
- else:
- prev_fill_color = None
- self._render_table_row_styled(i)
- if prev_fill_color:
- self._fpdf.set_fill_color(prev_fill_color)
+ self._render_table_row(0)
+ self._render_table_row(i)
self._fpdf.l_margin = prev_l_margin
self._fpdf.x = self._fpdf.l_margin
@@ -97,8 +91,8 @@ def get_cell_border(self, i, j):
return 1
if self.borders_layout == TableBordersLayout.NONE.value:
return 0
- columns_count = max(len(row.cells) for row in self._rows)
- rows_count = len(self._rows)
+ columns_count = max(row.cols_count for row in self.rows)
+ rows_count = len(self.rows)
border = list("LRTB")
if self.borders_layout == TableBordersLayout.INTERNAL.value:
if i == 0 and "T" in border:
@@ -110,39 +104,43 @@ def get_cell_border(self, i, j):
if j == columns_count - 1 and "R" in border:
border.remove("R")
if self.borders_layout == TableBordersLayout.MINIMAL.value:
+ if (i != 1 or rows_count == 1) and "T" in border:
+ border.remove("T")
if i != 0 and "B" in border:
border.remove("B")
- if rows_count > 1 and i != 1 and "T" in border:
- border.remove("T")
if j == 0 and "L" in border:
border.remove("L")
if j == columns_count - 1 and "R" in border:
border.remove("R")
if self.borders_layout == TableBordersLayout.NO_HORIZONTAL_LINES.value:
+ if i not in (0, 1) and "T" in border:
+ border.remove("T")
if i not in (0, rows_count - 1) and "B" in border:
border.remove("B")
- if rows_count > 1 and i not in (0, 1) and "T" in border:
+ if self.borders_layout == TableBordersLayout.HORIZONTAL_LINES.value:
+ if rows_count == 1:
+ return 0
+ border = list("TB")
+ if i == 0 and "T" in border:
border.remove("T")
+ if i == rows_count - 1 and "B" in border:
+ border.remove("B")
if self.borders_layout == TableBordersLayout.SINGLE_TOP_LINE.value:
+ if rows_count == 1:
+ return 0
border = list("TB")
+ if i != 1 and "T" in border:
+ border.remove("T")
if i != 0 and "B" in border:
border.remove("B")
- if rows_count > 1 and i != 1 and "T" in border:
- border.remove("T")
return "".join(border)
- def _render_table_row_styled(self, i):
- if i == 0 and self.first_row_as_headings:
- with self._fpdf.use_font_style(self.headings_style):
- self._render_table_row(i, fill=bool(self.headings_style.fill_color))
- else:
- self._render_table_row(i)
-
def _render_table_row(self, i, fill=False, **kwargs):
- row = self._rows[i]
+ row = self.rows[i]
lines_heights_per_cell = self._get_lines_heights_per_cell(i)
row_height = max(sum(lines_heights) for lines_heights in lines_heights_per_cell)
- for j in range(len(row.cells)):
+ j = 0
+ while j < len(row.cells):
cell_line_height = row_height / len(lines_heights_per_cell[j])
self._render_table_cell(
i,
@@ -152,6 +150,7 @@ def _render_table_row(self, i, fill=False, **kwargs):
fill=fill,
**kwargs,
)
+ j += row.cells[j].colspan
self._fpdf.ln(row_height)
# pylint: disable=inconsistent-return-statements
@@ -168,9 +167,9 @@ def _render_table_cell(
"""
If `lines_heights_only` is True, returns a list of lines (subcells) heights.
"""
- row = self._rows[i]
- col_width = self._get_col_width(i, j)
+ row = self.rows[i]
cell = row.cells[j]
+ col_width = self._get_col_width(i, j, cell.colspan)
lines_heights = []
if cell.img:
if lines_heights_only:
@@ -190,47 +189,62 @@ def _render_table_cell(
keep_aspect_ratio=True,
)
self._fpdf.set_xy(x, y)
- if not fill:
+ text_align = cell.align or self.text_align
+ if not isinstance(text_align, (Align, str)):
+ text_align = text_align[j]
+ style = cell.style
+ if not style and i == 0 and self.first_row_as_headings:
+ style = self.headings_style
+ if lines_heights_only and style:
+ style = style.replace(emphasis=None)
+ if style and style.fill_color:
+ fill = True
+ elif not fill:
fill = self.cell_fill_color and self.cell_fill_logic(i, j)
- text_align = (
- self.text_align
- if isinstance(self.text_align, (Align, str))
- else self.text_align[j]
- )
- lines = self._fpdf.multi_cell(
- w=col_width,
- h=row_height,
- txt=cell.text,
- max_line_height=cell_line_height,
- border=self.get_cell_border(i, j),
- align=text_align,
- new_x="RIGHT",
- new_y="TOP",
- fill=fill,
- split_only=lines_heights_only,
- **kwargs,
- )
- if lines_heights_only and cell.text:
- lines_heights += len(lines) * [self.line_height]
+ if fill and self.cell_fill_color and not (style and style.fill_color):
+ style = (
+ style.replace(fill_color=self.cell_fill_color)
+ if style
+ else FontStyle(fill_color=self.cell_fill_color)
+ )
+ with self._fpdf.use_font_style(style):
+ lines = self._fpdf.multi_cell(
+ w=col_width,
+ h=row_height,
+ txt=cell.text,
+ max_line_height=cell_line_height,
+ border=self.get_cell_border(i, j),
+ align=text_align,
+ new_x="RIGHT",
+ new_y="TOP",
+ fill=fill,
+ split_only=lines_heights_only,
+ **kwargs,
+ )
+ if lines_heights_only and not cell.img:
+ lines_heights += (len(lines) or 1) * [self.line_height]
if lines_heights_only:
return lines_heights
- def _get_col_width(self, i, j):
+ def _get_col_width(self, i, j, colspan=1):
if not self.col_widths:
- cols_count = len(self._rows[i].cells)
- return self.width / cols_count
+ cols_count = self.rows[i].cols_count
+ return colspan * (self.width / cols_count)
if isinstance(self.col_widths, Number):
- return self.col_widths
+ return colspan * self.col_widths
if j >= len(self.col_widths):
raise ValueError(
f"Invalid .col_widths specified: missing width for table() column {j + 1} on row {i + 1}"
)
# pylint: disable=unsubscriptable-object
- col_ratio = self.col_widths[j] / sum(self.col_widths)
- return col_ratio * self.width
+ col_width = 0
+ for k in range(j, j + colspan):
+ col_ratio = self.col_widths[k] / sum(self.col_widths)
+ col_width += col_ratio * self.width
+ return col_width
def _get_lines_heights_per_cell(self, i) -> List[List[int]]:
- row = self._rows[i]
+ row = self.rows[i]
lines_heights = []
for j in range(len(row.cells)):
lines_heights.append(
@@ -251,13 +265,21 @@ class Row:
def __init__(self):
self.cells = []
- def cell(self, text="", img=None, img_fill_width=False):
+ @property
+ def cols_count(self):
+ return sum(cell.colspan for cell in self.cells)
+
+ def cell(
+ self, text="", align=None, style=None, img=None, img_fill_width=False, colspan=1
+ ):
"""
Adds a cell to the row.
Args:
text (str): string content, can contain several lines.
In that case, the row height will grow proportionally.
+ align (str, fpdf.enums.Align): optional text alignment
+ style (fpdf.fonts.FontStyle): optional text style
img: optional. Either a string representing a file path to an image,
an URL to an image, an io.BytesIO, or a instance of `PIL.Image.Image`.
img_fill_width (bool): optional, defaults to False. Indicates to render the image
@@ -268,13 +290,15 @@ def cell(self, text="", img=None, img_fill_width=False):
"fpdf2 currently does not support inserting text with an image in the same table cell."
"Pull Requests are welcome to implement this 😊"
)
- self.cells.append(Cell(text, img, img_fill_width))
+ self.cells.append(Cell(text, align, style, img, img_fill_width, colspan))
+@dataclass
class Cell:
"Internal representation of a table cell"
-
- def __init__(self, text, img, img_fill_width):
- self.text = text
- self.img = img
- self.img_fill_width = img_fill_width
+ text: str
+ align: Union[str, Align]
+ style: FontStyle
+ img: str
+ img_fill_width: bool
+ colspan: int
diff --git a/test/html/test_customize_ul.pdf b/test/html/html_customize_ul.pdf
similarity index 100%
rename from test/html/test_customize_ul.pdf
rename to test/html/html_customize_ul.pdf
diff --git a/test/html/html_features.pdf b/test/html/html_features.pdf
index b31838a392fcef84b891cf2aa392787f953dd112..b0d3c5fba7555bed99e8bf3f8c6385c807d7c097 100644
GIT binary patch
delta 1572
zcmaE=_eXDoJ4?N#A(x#US8+*EYGN)|#hj&Kr^{v=h}?Ou{ch9U6;<5R&aU=7=Xq()
zFLsgHH@pt0v@AK>{-s_!>C1r=X11xUJ!$V!-zh98sBD=S@Zt4~3by!joB~}2Z8vS_
zoHg6dB4D)RD2I~d3LfR7iQ5|d*-BXbeGB8wr{>*GV$A)x+jV|o{kI39ZHLY`2%gqt
z&GGM;#57BlCDT>lzQ_8mO=}*mTl2jC{QA=9FS81MD=yrjF!#uWv#g6=HS^y6$j)?9
zPl#vZ!9|w@uD{IlT{YoIyUAgtz3Xm=UX;0Z)bi9##k(6;uv`|q>KHjQ*d=v>Do5L)
zqUxI>!b=``JaL@*E`ay+vRewR^)8mYr*hhhBFs!4DJPa#@7injyl;y}+_T=wEZ@Ct
z*S4f?RA1P5{>6gr{mM$G-i8)Vb-kDsysv`!&h9d4KjFQXc?=nqj?YbDRXM$T^1odz
z^XC?_1Rt5YS5x5TmAw{s|6AM+ZQCTup}Bho?`eaR0_lY_vt!?W&h>gPo!okA;*E^@
zUvGS0b>6t4CHZ;(v@|*MtKu8>em7ny_~Hel?Yj5JQu5?em}k2-$v!AeIq~+2pm65r
zNzCDECa-KLY72R`?r*c#k`;Sb-f8~#x~F}UXn}O_to*5oQxwlUHeye+SzD
zcjf!DJ}p_NG2P?RjQzH$;Rp3n9vfQMIkYF2<+RmX_q!LDePMV%eN(Ychwt=w%6Z()1EJ|!8-H3-!uV^7nO^zbH3LUJU7?oApeW)
zZp$}08;PON1M|GlDnvO%xsn{PvFS{RfZ@==R-@OuMI!fF6&Hq+uty+;IvWsDN+N1~1
z;+oY$xGq`WS(@Cb=XFci$oWA-&O+u*Y|b+Gbik!ws^UeeZGCMeTeR2#TAFO
z+QovJ7CyN7uk_-}kh;m~qP+DMSmZ5@G2CEmU~GbJ
zwz0V}hJ7YR78vd_F)=Yh*K1;GXa*9iNA|plnF)p^Cg$dtnk_8B9zfP>YG8yBn5KrN
z7>+SDG6h=<(OWY4nwU|nk%^I+iJ7ybv!kJffs2`?sk6DGsiTprftjnRqlKe^oee=1
bv5*v5tp4ES8+*EYGN)|#hkriw}WmQh}1mSK9~G^(JQ^z6SKLE4K8Qa
z9XQg)@kXL&;Zx^d`=<(@o-|o_(o3J&%WqVd74O;Yb?D)%4x!Ws&o7?j*%xDwFeiX(
zv$e(iwfUL}IYr(Ehl~s)RoxD)Y4GR!BsK5Wr*)aH_q;yEwChv2d%fM^H;ExLx$K!$
z4;ArU*`Mg9o$+4LOWN&8R8?SDDD(DE9aN@~tilM^rBopndR_j~5Cg*7)zx;Ncj@2$HrX?~u}ssp8|d&9$`{dV+i>NyY+
zk>4kq)MPeELpt;)-&KpU6}I)WA!
z#s}TQ%a}hJZS0O{c5&kOLYIDRR4Yd
zp4&`1lMmL<&52fU)W22JS#f>~@4E<~0qb-7O51<#H)*!#(Tr!R*N%NCz?gXKlj2+N
z2dB+-?2c7_=m4=-Uwedm8^t{~^>CQqK1@hXnv!
zU=LUl@ItC0=hqa@y5LOSD%Nw+CdVZ+S94wx)4lp}OZNU-%XUp-Rp(p%hpSJ}%jR#4
z88fp)^DCALHjR(LQR~Zj7BKA+7LZtQdN0`Sa|4P+PaGd
zPMyy)gXKOlpX%JdbMtqO9G-d$Qws$H5Kzcd-~uxY3@wc<(Z!66EDbQkjLnTO#7qs%
zF~rQUh?yIjfz;I_TWn;3MWLmkDY}8i28MLBf#Kh7V
z-9QsFBMeJS%uSFrL-bmhgMEVR5mTU%=;oOk8e=%d)QHL0c=97L+gJ-%LlZM+Qwvj9
z0~aGVOG9S^HxpA!GczM|V+&VTM@Kswf+}Jmi9WHYq@pM_jmy-?(v(Y8)z#mP3jk)`
BW=;SA
diff --git a/test/html/test_img_not_overlapping.pdf b/test/html/html_img_not_overlapping.pdf
similarity index 100%
rename from test/html/test_img_not_overlapping.pdf
rename to test/html/html_img_not_overlapping.pdf
diff --git a/test/html/html_table_line_separators.pdf b/test/html/html_table_line_separators.pdf
index 6dd04631dca24e67e2a627e547ef256d8884255a..37d32850638ebcbda2dd3d3a7780f333ba6a4f6d 100644
GIT binary patch
delta 428
zcmX@l`HpjgJ!8F*kr|hr9anKlQEFl?SH+yE6AtDbG7xZi|Fg^XP-^!2z1h#ZSlaKc
z`o-_1q0q$Q^yqtG=$e$p9TOg(GX8Ai{2@~E3B&iSsSlEq4@#P_UE;g8a@Pv;pnQ>w
zhAj^!+HOf`-LUR-ZBzb@1L2;SpxtAD=jfvh60Mq*$Hxe#Xcqi`I=yXcyt-)TC-*s&zbrM&)#I6~9+|F$6AEdPO=(e5Te5dNUMv780}@=|F8byW__ktjCLkw3I-sckf*=}W*8WlnV4dTnVT46
zh*_8zOb%zUj&X6burRkYGj?<~a5i%?HFI_}H!`zyHg$EhurxF=cD1u1s3H~;wuwb0
T6-B9OT$TogT&k+B{%%|VUUQ9D
diff --git a/test/html/html_table_line_separators_issue_137.pdf b/test/html/html_table_line_separators_issue_137.pdf
index d278ad893969eecb0c994dc5c198cfd9f8fafd4a..c6d470aa4d462ed52676cc2dd3b2d79893a482d2 100644
GIT binary patch
delta 428
zcmX@k`I2*kJ!8F*ks+6z9anKlQEFl?SH+yE6L<3+GT?FjUf1d#;qz!uxQfILfuD_D
zx=t&26d3pW%Ws*mWKrsuuM=z6`))Wbp~ZYY=WXKFrw2GCnd@76!arZ<31?;nERb
zp%Sw|yVv-IN=QM&4t-Xs+t+mkG?*v$RB$IdT&A0)nXWV1nHwn>fPg}t0vDKJU|?o$h#_WfW{DwYVP-bjfW#&W50hSV%-97L`;KrKWLN8kuvcs=E5S
GaRC4k!pvjZfuGnW?^hJIf%tN
z#?Zjn#l_gf(9zh^%-qP)(!kZy#l_Ot*~Hk{+||k4)y{^XidaaPCKi=c6s4wdSy&o#
Lsj9mAyKw;k(%XkV
diff --git a/test/html/html_simple_table.pdf b/test/html/html_table_simple.pdf
similarity index 69%
rename from test/html/html_simple_table.pdf
rename to test/html/html_table_simple.pdf
index f5ef356c9f464699203ae39e5ba935934cf5002d..b5aa7478e9b434f51cadf1f82ff832c2d4fbb132 100644
GIT binary patch
delta 389
zcmX@gd6jd6J!8F*fgzWj9anKlQEFl?SH+yE6Hap-G7xb6{bByes6=TYP2DTbdiB<5^B
zAtrUfDPmgTx`zyZ{p^4I>rD?4JH#ngTKaC9z`OYkocCMQ&gUM8kaCi$?vU+TvMBlM
zi&fw3L*?!=bSGrGX=(GFUK3osJEnf>tYVGDfxrL72sxMX%()uRz1aMv-O<05OL-PA
zE4i=Ix^I54p403DE95lPP1yKDlOG~$Z+|0)GiqX#0NWlOE6!H|fzzhQeGgCth
zF>@133^5B6v&nia)-e{Y2BtJ)!&T^0H$|`IRF3v
delta 373
zcmcc0d6aX5J!8G0g(;Vv9anKlQEFl?SH+yY{nmVk3KRNeD}NUs%N?E#+^N*A}`^&hv$-zIj_>=%Oj0+
zw-zXgob^dM8a(IC#k+df-z)8ZZ#t`_A>6LKWa9Nl8Z#ypIlld{``M&fO?^odCoGKZ
z;(j&lQIprQ{Il;XH8zVey<)U8Hd8PF0fjsTE-=Huz|7bbL(JUR7(>j$*kH09i*<~v
zvxT9Vxw)~Sft#g^vyr*0tEI8Cn}MUbk)xZ5iKT^|4M7#LkPuBQDyb++P2;k#G~`lM
Jb@g}S0svU7iJ1TZ
diff --git a/test/html/bgcolor_in_table.pdf b/test/html/html_table_with_bgcolor.pdf
similarity index 58%
rename from test/html/bgcolor_in_table.pdf
rename to test/html/html_table_with_bgcolor.pdf
index 2c061fbea44931dce9a16b009a95abb2eb3feb5f..c6050e45da4f29428072bb10b543150fa82b19af 100644
GIT binary patch
delta 705
zcmcc4^NVML17p2~C6}EYS8+*EYGN)|#hj&6qO)&12(;e+s(mgx$isYByj->C!lfcU
zEVXgBl{1AauTM*1|Nr}3lwa;nr6LWs&eUtg=YE!Lv8%IK*CDjWVt(NsWhy~7n;5glDenH~5Y2AspFYvy3edn0TvBmuQeJ)aSq)%!y
z$(~&>N%xt{FTNoCg;90Ms`c}CCUJ$Vdh4@U-1#)myn6Sp$KCrSg5y~hOSDfBy1r}M
zvwfHPvNrEKQgZF5Y32PYmb0!Og5S?y7Q!95c=eRJuw8o&M{Qec^lQRg(`d2$C2<1H
z4`Pp$F17y9w)dvlo>ePfd&Cqr{Wq+vnQ&6{#G(_&wr{k{jC_ClU2O5yZlA-}E9-59
z42|}LOlmZcKIWJfD=Zdtza*nGV3q7vG0{_P6}SB&cZB<&f4G{jZTeArX3@%TZZnn$
zr#~`j*^5)-r*fs{r4*NJmShfQ3NkTJFaQCCJOwT=!@$7O*b-gL
z(7@OnL(CA1n30Le$sGN^FMG~j
z<{3Qk*Bce_0`E6GlS6)f+b!qIoO|U-q7&22nEBOp-{aJO7>*m1K^t~1G_EIB*beV%`pzvCs3w#({Q9{Y6elsP?}w{c$3f{Sw8O4Yo%;Rf^e
zN4#eL_gKs1*!Oj}yPrQ&w5fMDS{GZ#I5X&AlE>evWmk5cT_MYUl=+-OrQGusKQCWR
zTN(YOcO(C?Yu9gNyzkxozr$ks^Cfe)c_-QQDgDdO;pe-tJNNkNX}{(jnSHg6DU7Ft
zaf1$%i^2Dkugc#4?zXb*nD1`C>b{Vnft>VN3!h^KPt0@^SH2D9_iwrN<^+4dl`z@D
z8|PXsb)3{VeNtn8&ZdxuLUU66Er2k1*SNP=zrMHU)&B`^YXtjV?u%dh
zF!p$Im$h2??K0NaRUvJ+*MC&M6BDvddROgxvGX758F$-j{@QHD9LW@9V5(pM0t$Hw
zTwsQQfu(^7x|pGXff0t7Ar>(s1Ix(|S#4sS3`{Le%nc39T+GazEL<%Noh>ZP&7CZr
m4Gb+!O$;3EYzV4|g(SMfqLPZD)HE(bBXa{TRaIAiH!c8WAQmJ5
diff --git a/test/html/html_table_with_border.pdf b/test/html/html_table_with_border.pdf
index 89a8d12f46933e92a5bb9b98839c0fafc47b35b6..6c3f372f349773f38953261c4823772760514f2e 100644
GIT binary patch
delta 506
zcmbQsb&_j?J!8GGp&6H*9anKlQEFl?SH+yElXm;D81lIO{>e4{!VH_aKHV&=vPu6K
z$|^G)leP-&_jl_tE;RC9u%-C1%zbI*@@6Ob2iZI3J$Mz?9J)fpr+UZCHvhu3BQrZQ
zY+Z6MT358sH)1iL&R)^*DrWM*!Y5T?C;Y-pl&2k=rjWQ)NzJ$Z%L*AE|AN9BZ~dnm
z>^<8ft(}{5iG8K$IVqDQ$(tv;%G~ZP3so_B%q%qZwaq&_zuk}ji@iF0h0DKXv99=}
zxoa}gpR`7~Iu(l)i2Rg^pC4bxy@IP&bM61n%UAS1tCF1-+C#r+v65!Dby)}hrnqN+gPk)Ei4V43|&pl&0Jj^9WC9=%}szT
q3ri;hGiO656H7BY8-glgA+efRR8motn#N^lU}VIls_N?R#svUK@W4m_
delta 462
zcmX@fHJ59HJ!8F*xiOcW9anKlQEFl?SH+yYfxG>V7zn)iEE?@}Fsb@&=^Z6U$L{|O
zMK&ewf^(0&Pu9M7#H?IX^Z3l;=Z+lsyx>CR4@o(tJNB6htzM2c2a;pvow>ifK6R$x
z$;R(ub6I`GZXT3v^_ihw(Wv5i|Cr0ho>NXW&9fhuSloB|bE@iWJ)5%t^I=Bc7rZ^6
zPsCX7@w@*#Q;gqRZFO1yhBt;L;b-Q*mwzoOerj6UlT9x<#ZELD+o%QajNxDn?@95J
z`pOYk>3OyG;|L2sSkE_mVJ4va@)n%Y32&A2YHKH>m2R=F#V0b
zSh~5B=@p}$k%@u<2q@$!aDf>H2IfY_7-AMih8SX&h8B}IvRKC$I61poI2&0wxfnW|
xnHe}68W=dbni`uMm>3utnYozS*$`9_3yG@4qLPZD)HE(jb4xB&RabvEE&!(@uulL0
diff --git a/test/html/html_table_with_empty_cell_contents.pdf b/test/html/html_table_with_empty_cell_contents.pdf
index efe61273cd75a7e5709690b3d803c034f4bea690..ff8f4a293cdac5630fbfa3f74c67f77fdd64a3d1 100644
GIT binary patch
delta 459
zcmZqRn#HxjfwA7)fXmK~tGJ{nH8Gc~V$RY@r~Q}>d0fAL7L7Y}OvUewO0-JEhSewJ
z4@_yd@(7vwqn6DybM8KoD@#k0=g0gOvf*X3JaE2X{=;_{n~XDfo~YV9dAR%Hii;YH
zk~t14SI;=WmFTrK?x67*+hW1z<`XYgoLFD5S!(+1=o9nnnf*Ta)hFEADO}<1(Ju3-
zK}qV1QC;q_um1zyzTk{tnQZM)$vH#H|8@WN@7~L*#GdNhvRLo^Dj=G@?C17F5_8`z
zzjZ(Tlws7RzkZTh^NkmF#dV7A4Ub$|`XO%WDm|-9M-9!qwM!@K`MrEq^420XT6g&p
z@5Xf=*ZD&EE-v!gC9+CCcDlIiR$GN`%jxSr|Fhn|*LJhgj>jDaHy3y4w8g7yefz)J
zk?AF)y_u1rf&mC9W7%*1(|7NY4D#^yFZp^=#Q67=7_HB*C)S+O
zvu!^6Nyv-8Y~{Cv=~16M|37RwcEKX>e6O(AM|Slvr`UY&6@C^+mI-SwvAXEAN|^6c
zwz|sYX~LzJdQa;%D&5ij#%;}ZP}TADgL8913+n!1{s7#f+o8km_o8`;?qR1ph_j>Mvp
TilWpsE=yAbE>%@me>W}w*tDQ6
diff --git a/test/html/test_img_inside_html_table_centered_with_align.pdf b/test/html/html_table_with_img.pdf
similarity index 95%
rename from test/html/test_img_inside_html_table_centered_with_align.pdf
rename to test/html/html_table_with_img.pdf
index 22db3f56c913b25fc9c7796c06d6353b29174ae0..a1043948508c22068ea813c78e53ca271eb0d90f 100644
GIT binary patch
delta 581
zcmcauyrN`-2_vK7WK%|$dP8F_J3Fr8lA_eaT&{{aQ_nbZ9WoGcy;vFcG3>*y&y)VB
z1+R3n)=pt>GM?CXXCJGm$ea_haTbS!dQy+r%2>4pyNH~pJD^CReSOwCJ9E%$+wwW87(#^GS5*0QvXzynSo+~
zj2e@@G`lAEoAXZQR8!l`rqu;xSny1qV=lnPrSE2>U^aP$xr@BHf`LL1m%gWwf}sJ3
z3G^-yrRJsNCuMON8cpW1aA7o_>~E19&rS;eQH|X(XU^&yygu^^8Nk6I@2xoeSj*
zr9T)JUn%y#zhL78v4Utr_0t!84hkl#^;yJhxWN?`{>FVvYoFiS=1=BThqSgWDO~c!
z{->T|-rj>6g2#;x?#*@;FRG4uar#iy!*hAdp1L3G{LR37*rH_ecgCj4x0%e?EEEhB
zf?_8-YN<`0pyoHZo0*5z93(XPp_;|!E6fQ>lg}|}Z;n^{$;4U3nfhuNVWQ1;>
zp^32x#NNr1EXC`Mxb%ba^Gg(rpw7)p%U3WogmPRIqHPQeoz0CcoJ|azjV&yl%*`zw
tT}@qFO)U*voy?uhO`PortB8dphs2_iilWpsE<+O&12ZmFRabvEE&!_+vV8ym
diff --git a/test/html/test_img_inside_html_table_without_explicit_dimensions.pdf b/test/html/html_table_with_img_without_explicit_dimensions.pdf
similarity index 95%
rename from test/html/test_img_inside_html_table_without_explicit_dimensions.pdf
rename to test/html/html_table_with_img_without_explicit_dimensions.pdf
index 78983577fac863fc2217f3c63477ea3827f9a727..a1043948508c22068ea813c78e53ca271eb0d90f 100644
GIT binary patch
delta 583
zcmX?7yrN`-2_vK7WK%|$dP8F_J3Fr8lA_eaT&{{aQ_nbZ9WoGcy;vFcG3>*y&y)VB
z1+R3n)=pt>GM?CXXCJGm$ea_haTbS!dQy+r%2>4pyNH~pJD@>ReSPQCJ9E%$>*3_SuGR{6oMwtRkPSUfjL2m
z(PHyQRb^(NTp**yWLwQHCPRbCA5{%E|J3Z_RWbys%}>hWva#XP2ODi;qwi*@UpjQN=9HEzs2&8kky`ps6!7FgG(s7c((3w*-svAuKjDF)^I1VJTa0
z%%vZcpI@S21ocE-TE2oIjN_sZZDZ+VZ0P1>U}kFS>}F(Q=3-`HZ077@Wa(t+XzpU_
gYGOxNMJyz7Bo>ua6s4wd0qr(7=2BI4^>^a}0Mc-$tpET3
delta 556
zcmZ2ca-?{J2_vJyWK%|$dJ|(VJ3Fr8lA_eaT&{{awSCrHOpXGqpDmAan^xYw``O=>
z^=!+|#w3LfiOSmYfXIMrb&&s6LPJ-i`wMIg)D9^cpc>#sB^x$$PG>&WcO
z>nwZuL3WzfTBl=~9XzaIFO-9{rnEd3J?yt}-$R3$o6np4G_7AFE0TC(v+R?cW>J4>
z>8v!aj<}5X9J7_;-o?H6A7aDU``3bH@^{9j$+wxz*enzb6oO(WJ8G#-o}lJ8xtp1X
z)f^-=`JtM{<}1tzN|VnqX>X2K`^m&;F*%S?V{)Tr*W?IIp2@eEf)Wu
zqs24%in+k#cjgWvW+3fc`kqD#mLL`|4D9SC?>85n9ARO}Xf%0}MQ**Rp@IPjDC8+{
zff)t{W~K({VwUEHMrdM&2BwBU^HKB~8kkv{qKlar8KRqKXku&(v3K$$OYwRmF8!eV
z{1OGA6=3J)rR6Ia0_DLR7lmjWOCvKQ0}C@3Cqpx1H&Y8EXA4scOH)^K6Bk!=M;8k>
eJHjerAt@lSsHCDOHH{1C0!w2qRaIAiH!c8nwWsd@
diff --git a/test/html/test_img_inside_html_table_centered_with_caption.pdf b/test/html/html_table_with_imgs_captions_and_colspan.pdf
similarity index 97%
rename from test/html/test_img_inside_html_table_centered_with_caption.pdf
rename to test/html/html_table_with_imgs_captions_and_colspan.pdf
index 813841803a692f5f1aa29c4d3d7cd3d6b1bad615..1b53a4e87669472435a8dd2bceef2f919f302a74 100644
GIT binary patch
delta 540
zcmbQVmT}cu#tjvW^%lllc6MCFB}J);xm*=^ZU+RUBA^OH}%^2^(+
zE#bnFM};K&J=H88NolS1P`5~Ep1sQ__|iv-?@ac_noVNPNuCKCE9#eN_)ai%v~i4k
zlo&jfO};+k_hy6AbgAG+F`{?PTR~o#k}jf{tOAbb@OM6@J)VRyeaql
z$vu*r6FOvDt5ZEMl*2D?ZdF9WnlPuRjDsj$EhK>fNF3!%z=0;}DmPUpKu8tOF
qj*iYwCPpS^=H_-b1XaXBVm+~_q@pM_jmyZ;!qAXQRn^tsjSB#^sKM3%
delta 491
zcmZ3rmT}Tr#tjvW^`@3wc6MCFB}J);xm*=N)yXe&7Oownq6Gxuj_XeRs3~){{52a
z-t5@ckK3GweXR!!Pr|3J0#
z!tv=Z4xIePZ^Y4NsUh}wO46gJd$Q}qV}0+Q6;eLYdDCEJM`U~&OKX07rjdH|%DoRk6G_%=!i}#60+vce!czs|IE+Rbs|*v|35VS#pVS}oWW^kW(o!%
zppd7)1!fo+n46hmh*_8!qlp<>TAG-ki5VH18KCPmGBh_boy;F*8)x8ZWMJlG>}+IW
z;AU#-X6fQ)VrgJtX6|O_>Sku-Y+`3aP(>^x5)+F`DvDCmxPWF`m~*MBy863u0RUoi
Bwaowk
diff --git a/test/html/test_html_whitespace_handling.pdf b/test/html/html_whitespace_handling.pdf
similarity index 100%
rename from test/html/test_html_whitespace_handling.pdf
rename to test/html/html_whitespace_handling.pdf
diff --git a/test/html/test_html.py b/test/html/test_html.py
index fb4fac153..062897613 100644
--- a/test/html/test_html.py
+++ b/test/html/test_html.py
@@ -26,7 +26,7 @@ def test_html_images(tmp_path):
f""
)
# Unable to text position of the image as write html moves to a new line after
- # adding the image but it can be seen in the produce test.pdf file.
+ # adding the image but it can be seen in the resulting html_images.pdf file.
assert round(pdf.get_x()) == 10
assert pdf.get_y() == pytest.approx(mm_after_image, abs=0.01)
@@ -83,8 +83,8 @@ def test_html_features(tmp_path):
" "
" "
" "
- ' id | '
- ' name | '
+ " id | "
+ " name | "
" "
" "
" |
"
@@ -110,8 +110,8 @@ def test_html_features(tmp_path):
" "
"