fix(ereader): keep resume position when a block spans a page (S11)
render_page_forward discarded new_pos on the failure path, but a block that only partially fitted has still advanced the position: paragraph_layouter reports the word it stopped at, and _layout_paragraph_on_page packs it into new_pos. Dropping it told the caller no progress was made, so navigation dead-ended on any paragraph larger than a single page - the reader saw "end of document" mid-book. A 2877-word paragraph at 800x600 rendered 26 lines and reported the start position back; it now paginates across 12 pages. Also guard the navigation loop: EreaderManager.next_page treats no-progress as end-of-document, which is only correct at the actual end. Anywhere else it now logs the offending block index and skips that block, so a future layout bug costs one block rather than the rest of the book.
This commit is contained in:
@@ -344,7 +344,14 @@ class BidirectionalLayouter:
|
||||
scaled_block, page, current_pos, font_scale)
|
||||
|
||||
if not success:
|
||||
# Block doesn't fit, we're done with this page
|
||||
# The block did not fit in its entirety. It may still have been
|
||||
# laid out partially - a paragraph larger than one page places as
|
||||
# many lines as fit and reports the word it stopped at. Keeping
|
||||
# that resume point is what allows the next page to continue;
|
||||
# discarding it tells the caller no progress was made, which
|
||||
# dead-ends navigation on the block forever.
|
||||
if self._position_compare(new_pos, current_pos) > 0:
|
||||
current_pos = new_pos
|
||||
break
|
||||
|
||||
# Add inter-block spacing after successfully laying out a block
|
||||
|
||||
@@ -9,6 +9,7 @@ into a unified, easy-to-use API.
|
||||
from __future__ import annotations
|
||||
from typing import List, Dict, Optional, Tuple, Any, Callable
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from .ereader_layout import RenderingPosition, ChapterNavigator, ChapterInfo
|
||||
@@ -20,6 +21,8 @@ from pyWebLayout.style.page_style import PageStyle
|
||||
from pyWebLayout.style.fonts import BundledFont
|
||||
from pyWebLayout.layout.document_layouter import image_layouter
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BookmarkManager:
|
||||
"""
|
||||
@@ -417,6 +420,21 @@ class EreaderLayoutManager:
|
||||
self._notify_position_changed()
|
||||
return self.get_current_page()
|
||||
|
||||
# No progress. That is the correct answer only at the end of the
|
||||
# document; anywhere else a block has failed to lay out and would trap
|
||||
# the reader on this page. Skipping the block costs one block, not the
|
||||
# rest of the book.
|
||||
if self.current_position.block_index < len(self.blocks):
|
||||
logger.error(
|
||||
"Block %d made no layout progress; skipping it. This is a layout "
|
||||
"bug - the block placed nothing and reported no resume point.",
|
||||
self.current_position.block_index)
|
||||
self.current_position = RenderingPosition(
|
||||
chapter_index=self.current_position.chapter_index,
|
||||
block_index=self.current_position.block_index + 1)
|
||||
self._notify_position_changed()
|
||||
return self.get_current_page()
|
||||
|
||||
return None # At end of document
|
||||
|
||||
def previous_page(self) -> Optional[Page]:
|
||||
|
||||
Reference in New Issue
Block a user