diff --git a/pyWebLayout/layout/ereader_layout.py b/pyWebLayout/layout/ereader_layout.py index 0fb46b0..1888a01 100644 --- a/pyWebLayout/layout/ereader_layout.py +++ b/pyWebLayout/layout/ereader_layout.py @@ -857,27 +857,3 @@ class BidirectionalLayouter: if pos1.word_index != pos2.word_index: return 1 if pos1.word_index > pos2.word_index else -1 return 0 - - -# Add can_fit_line method to Page class if it doesn't exist -def _add_page_methods(): - """Add missing methods to Page class""" - if not hasattr(Page, 'can_fit_line'): - def can_fit_line(self, line_height: int) -> bool: - """Check if a line of given height can fit on the page""" - available_height = self.content_size[1] - self._current_y_offset - return available_height >= line_height - - Page.can_fit_line = can_fit_line - - if not hasattr(Page, 'available_width'): - @property - def available_width(self) -> int: - """Get available width for content""" - return self.content_size[0] - - Page.available_width = available_width - - -# Apply the page methods -_add_page_methods() diff --git a/tests/layout/test_ereader_layout.py b/tests/layout/test_ereader_layout.py index 09513ab..5436514 100644 --- a/tests/layout/test_ereader_layout.py +++ b/tests/layout/test_ereader_layout.py @@ -899,5 +899,43 @@ class TestBidirectionalLayouter: assert next_pos == position # No progress possible +class TestNoPageMonkeyPatching: + """ + R5: importing this module used to run _add_page_methods(), which attached + can_fit_line/available_width to Page if they were absent. They are not + absent, so it never fired - but its can_fit_line took (line_height) and + ignored descenders, while Page's takes (baseline_spacing, ascent, descent). + Had Page's ever been renamed, the import would have silently reinstated the + pre-S2 clipping bug from another package. + """ + + def test_module_does_not_patch_page(self): + import pyWebLayout.layout.ereader_layout as ereader_layout + + assert not hasattr(ereader_layout, '_add_page_methods') + + def test_page_owns_its_geometry_methods(self): + from pyWebLayout.concrete.page import Page + + assert 'can_fit_line' in vars(Page) + assert 'available_width' in vars(Page) + + def test_can_fit_line_still_accounts_for_descenders(self, sample_page_style): + """ + The patched version took a single line_height and had no way to express + descent, so a descender hanging past the content box counted as fitting. + """ + from pyWebLayout.concrete.page import Page + + page = Page(size=(200, 100), style=sample_page_style) + content_y, content_h = page.content_rect[1], page.content_rect[3] + available = content_y + content_h - page._current_y_offset + + assert page.can_fit_line(0, ascent=available, descent=0) + assert not page.can_fit_line(0, ascent=available, descent=1), \ + "a descender past the content box must not be reported as fitting" + assert page.can_fit_line(0, ascent=available - 1, descent=1) + + if __name__ == "__main__": pytest.main([__file__, "-v"])