Skip to content

Table cell annotations are misplaced (or crash) when the cell is vertically padded - #122

Open
jimbofreedman wants to merge 1 commit into
weblyzard:masterfrom
spotship:fix/table-annotation-vertical-padding
Open

Table cell annotations are misplaced (or crash) when the cell is vertically padded#122
jimbofreedman wants to merge 1 commit into
weblyzard:masterfrom
spotship:fix/table-annotation-vertical-padding

Conversation

@jimbofreedman

Copy link
Copy Markdown

Fixes #121.

The multi-line branch of TableCell.get_annotations double-counts a cell's top padding, so annotations inside a cell that is shorter than its row either land on the wrong line or index past the end of annotation_lines.

Cause

height.setter pads short cells with empty lines, and width.setter then records line_width from the padded blocks — so line_width carries one zero-length entry per padding line. get_annotations accumulates line_break_pos over that padded list, which makes the loop variable no a padded line index, and then adds self.vertical_padding on top of it:

line_break_pos = list(accumulate(self.line_width))
...
for no, line_break in enumerate(line_break_pos):
    if a.start <= (line_break + no):
        annotation_lines[no + self.vertical_padding].append(a)

Annotation start offsets reference the pre-padding cell content, so no has to be a content-line index for the + vertical_padding shift to be correct.

valign effect
top none — vertical_padding == 0
middle annotations silently shift onto the bottom padding lines, so text[start:end] returns whitespace or text from the next column
bottom IndexError: list index out of range as soon as annotations reach the second content line

Minimal reproducer on master (2.7.3):

from inscriptis import get_annotated_text
from inscriptis.model.config import ParserConfig

html = ('<table><tr>'
        '<td style="vertical-align:bottom">a1<br><b>a2</b></td>'
        '<td>b1<br>b2<br>b3<br>b4</td>'
        '</tr></table>')

get_annotated_text(html, ParserConfig(annotation_rules={'b': ['b']}))
# IndexError: list index out of range

See #121 for the valign=middle case and full output.

Fix

Record the pre-padding row count as _content_height in normalize_blocks, and accumulate line_break_pos over only the content slice of line_width. no is then a content-line index 0..rows-1, and annotation_lines[no + top_pad] addresses the output line that actually holds the content. The single-line branch and valign=top are untouched.

Tests

tests/test_table_annotation_vertical_padding.py adds four regression tests (bottom-align crash, middle-align offset shift, annotation on the last content line, padded two-column list). All four fail on master — two with IndexError, two with wrong slices — and pass with the fix.

Locally clean: uv run ruff check, uv run ruff format --check, uv run ty check, uv build, and uv run pytest (76 passed) on each of Python 3.10–3.14.

Relation to #92

As noted in #121, this is not the bug reported in #92 — that snippet's two columns are equal height, so no padding is applied and its output is unchanged by this PR. #92 remains open.

The multi-line path in `TableCell.get_annotations` built `line_break_pos`
from the post-padded `self.line_width` (which contains zero-length entries
for the top and/or bottom padding added by `height.setter`) and then indexed
`annotation_lines` by `no + self.vertical_padding`. Because `no` was already
a padded line index, adding `vertical_padding` on top double-counted the top
padding:

* `valign=bottom` cells shorter than their row could write past the end of
  `annotation_lines`, raising `IndexError` on the second or later content
  line's annotations.
* `valign=middle` cells silently shifted annotations onto the bottom-padding
  lines below the actual content, so `text[start:end]` returned whitespace or
  text from the next column.

Fix: scan only the content-width slice of `line_width` (using a new
`_content_height` recorded in `normalize_blocks`) so `no` is a content-line
index 0..rows-1, and the destination `annotation_lines[no + top_pad]` then
correctly references the output line holding the content.

Regression tests in tests/test_table_annotation_vertical_padding.py cover the
`IndexError` crash and the silent middle-/bottom-align offset shift.
@jimbofreedman
jimbofreedman marked this pull request as ready for review July 28, 2026 04:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Annotations in vertically padded table cells are misplaced, or raise IndexError

1 participant