diff --git a/pyWebLayout/layout/ereader_layout.py b/pyWebLayout/layout/ereader_layout.py index e8312e3..477ea17 100644 --- a/pyWebLayout/layout/ereader_layout.py +++ b/pyWebLayout/layout/ereader_layout.py @@ -337,8 +337,17 @@ class BidirectionalLayouter: comparison = self._position_compare(actual_end, end_position) # Perfect match or close enough (within same block) + # BUT: ensure we actually moved backward (estimated_start < end_position) if comparison == 0: - return page, estimated_start + # Check if we actually found a valid previous page + if self._position_compare(estimated_start, end_position) < 0: + return page, estimated_start + # If estimated_start >= end_position, we haven't moved backward + # Continue iterating to find a better position + elif iteration == 0: + # On first iteration, if we can't find a previous position, + # we're likely at or near the beginning + break # Track best result so far distance = abs(actual_end.block_index - end_position.block_index) @@ -357,7 +366,39 @@ class BidirectionalLayouter: estimated_start.word_index = 0 # If we exhausted iterations, return best result found - return best_page if best_page else page, best_start + # BUT: ensure we didn't return the same position (no backward progress) + final_page = best_page if best_page else page + final_start = best_start + + # Safety check: if final_start >= end_position, we failed to move backward + # This can happen at the beginning of the document or when estimation failed + if self._position_compare(final_start, end_position) >= 0: + # Can't go further back - check if we're at the absolute beginning + if end_position.block_index == 0 and end_position.word_index == 0: + # Already at beginning, return as-is + return final_page, final_start + + # Fallback strategy: try a more aggressive backward jump + # Start from several blocks before the current position + blocks_to_jump = max(1, min(5, end_position.block_index)) + fallback_pos = RenderingPosition( + chapter_index=end_position.chapter_index, + block_index=max(0, end_position.block_index - blocks_to_jump), + word_index=0 + ) + + # Render forward from the fallback position + fallback_page, fallback_end = self.render_page_forward(fallback_pos, font_scale) + + # Verify the fallback actually moved us backward + if self._position_compare(fallback_pos, end_position) < 0: + return fallback_page, fallback_pos + + # If even the fallback didn't work, we're likely at the beginning + # Return a page starting from the beginning + return self.render_page_forward(RenderingPosition(), font_scale) + + return final_page, final_start def _scale_block_fonts(self, block: Block, font_scale: float) -> Block: """Apply font scaling to all fonts in a block""" diff --git a/pyWebLayout/layout/page_buffer.py b/pyWebLayout/layout/page_buffer.py index 9cb9ac7..4718868 100644 --- a/pyWebLayout/layout/page_buffer.py +++ b/pyWebLayout/layout/page_buffer.py @@ -420,12 +420,18 @@ class BufferedPageRenderer: cached_page = self.buffer.get_page(end_position) if cached_page: # Get previous position from reverse position map - prev_pos = self.buffer.reverse_position_map.get(end_position, end_position) + prev_pos = self.buffer.reverse_position_map.get(end_position) - # Start background rendering for previous pages - self.buffer.start_background_rendering(end_position, 'backward') + # Only use cache if we have the reverse position mapping + # Otherwise, we need to compute it + if prev_pos is not None: + # Start background rendering for previous pages + self.buffer.start_background_rendering(end_position, 'backward') - return cached_page, prev_pos + return cached_page, prev_pos + + # Cache hit for the page, but we don't have the reverse position + # Fall through to compute it below # Render the page directly page, start_pos = self.layouter.render_page_backward(end_position, font_scale)