This commit is contained in:
JiriUhlir
2026-09-03 10:03:10 +02:00
parent 156289fe2d
commit a922117b3d
13 changed files with 583 additions and 59 deletions
+52
View File
@@ -13,6 +13,9 @@ logger = logging.getLogger(__name__)
HEADING_TAGS = ("h1", "h2", "h3", "h4", "h5", "h6")
ID_SAFE = re.compile(r"[^a-zA-Z0-9_-]+")
# Matches a width declaration in percent, but never max-width or min-width.
PERCENT_WIDTH = re.compile(r"(?<![\w-])width\s*:\s*\d+(?:\.\d+)?%", re.IGNORECASE)
TOC_PLACEHOLDER = "\u2007\u2007\u2007" # figure spaces, keeps the pass 1 layout stable
@@ -70,6 +73,55 @@ class SourceDocument:
base.set("href", base_url)
self.head.insert(0, base)
# -- images ---------------------------------------------------------
def relax_percentage_image_widths(self) -> int:
"""Replace percentage widths of images inside table cells by auto.
WeasyPrint resolves such a percentage against a cell width that is not
known yet while the table is being laid out. The image comes out zero
wide and disappears from the PDF without any error, so it is not even
reported as a missing asset. Rendering the image at its intrinsic size
capped by max-width gives the result the document intended.
Only relevant for WeasyPrint, Chromium lays these images out correctly.
Returns the number of images that were changed.
"""
changed = 0
for cell in self.body.iter("td", "th"):
for image in cell.iter("img"):
if self._relax_image_width(image):
changed += 1
if changed:
logger.info(
"Percentage width of images inside table cells replaced by auto",
extra={"images": changed},
)
return changed
@staticmethod
def _relax_image_width(image) -> bool:
touched = False
if (image.get("width") or "").strip().endswith("%"):
del image.attrib["width"]
touched = True
style = image.get("style") or ""
if PERCENT_WIDTH.search(style):
style = PERCENT_WIDTH.sub("width: auto", style)
touched = True
if not touched:
return False
if "max-width" not in style.lower():
stripped = style.strip().rstrip(";")
style = f"{stripped}; max-width: 100%" if stripped else "max-width: 100%"
image.set("style", style)
return True
# -- headings -------------------------------------------------------
def collect_headings(self, max_depth: int) -> list[Heading]:
"""Assign ids to headings that lack one and return them in document order."""