From 9d09e991f4cee8e6f3c08597d23f6fdf06369174 Mon Sep 17 00:00:00 2001 From: Developer Date: Fri, 15 May 2026 21:26:34 +0800 Subject: [PATCH] fix(pagination): rewrite calculate_pages to track lines instead of chars, newlines consume vertical height not horizontal --- src/reader.rs | 93 +++++++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/src/reader.rs b/src/reader.rs index 22f3137..fc7e960 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -17,10 +17,9 @@ pub fn recalculate_pages(book: &mut Book, font_size: f32, line_height: f32, pane } else { 1 }; - let chars_per_page = chars_per_line * lines_per_page; for section in &mut book.sections { let styled = style.apply_to_text(§ion.content); - section.pages = calculate_pages(&styled, chars_per_page); + section.pages = calculate_pages(&styled, chars_per_line, lines_per_page); } } @@ -391,60 +390,66 @@ fn render_bookmarks( } } -pub fn calculate_pages(text: &str, chars_per_page: usize) -> Vec { +pub fn calculate_pages(text: &str, chars_per_line: usize, lines_per_page: usize) -> Vec { let mut pages = Vec::new(); pages.push(0); - if text.is_empty() || chars_per_page == 0 { + if text.is_empty() || chars_per_line == 0 || lines_per_page == 0 { return pages; } let chars: Vec = text.chars().collect(); - let total_chars = chars.len(); - if total_chars <= chars_per_page { - pages.push(total_chars); + let total = chars.len(); + if total == 0 { return pages; } - let mut pos: usize = 0; - while pos < total_chars { - let next = pos + chars_per_page; - if next >= total_chars { - pages.push(total_chars); - break; + let mut page_start: usize = 0; + let mut line_count: usize = 0; + let mut line_pos: usize = 0; + let mut i: usize = 0; + + while i < total { + let ch = chars[i]; + + if ch == '\n' { + line_count += 1; + line_pos = 0; + i += 1; + } else if line_pos >= chars_per_line { + line_count += 1; + line_pos = 0; + } else { + line_pos += 1; + i += 1; } - // 在页面后半段查找段落边界(\n\n)或行边界(\n),不回超理想页长 - let search_start = pos + chars_per_page / 2; - let search_end = next.min(total_chars); - let mut split = next; - - // Prefer double newline (paragraph), then single newline - let mut found = false; - for i in (search_start..search_end).rev() { - if chars[i] == '\n' && i > 0 && chars[i - 1] == '\n' { - split = i - 1; - found = true; - break; - } - } - if !found { - for i in (search_start..search_end).rev() { - if chars[i] == '\n' { - split = i + 1; + if line_count >= lines_per_page && i > page_start { + let window_back = chars_per_line * lines_per_page / 3; + let search_start = page_start.max(i.saturating_sub(window_back)); + let mut split = i; + for j in (search_start..i).rev() { + if j > 0 && chars[j] == '\n' && chars[j - 1] == '\n' { + split = j + 1; break; } } + if split <= page_start { + split = i; + } + if split < total { + pages.push(split); + page_start = split; + line_count = 0; + line_pos = 0; + i = split; + } } - - if split <= pos { - split = next; - } - - pos = split.min(total_chars); - pages.push(pos); } + if *pages.last().unwrap() < total { + pages.push(total); + } pages } @@ -454,38 +459,38 @@ mod tests { #[test] fn test_pagination_empty() { - let pages = calculate_pages("", 100); + let pages = calculate_pages("", 10, 5); assert_eq!(pages, vec![0]); } #[test] fn test_pagination_shorter_than_page() { - let pages = calculate_pages("Hello World", 100); + let pages = calculate_pages("Hello World", 10, 10); assert_eq!(pages, vec![0, 11]); } #[test] fn test_pagination_exact_fit() { - let pages = calculate_pages("ABCD", 4); + let pages = calculate_pages("ABCD", 2, 2); assert_eq!(pages, vec![0, 4]); } #[test] fn test_pagination_multiple_pages() { let text = "A".repeat(100); - let pages = calculate_pages(&text, 30); + let pages = calculate_pages(&text, 10, 3); assert_eq!(pages, vec![0, 30, 60, 90, 100]); } #[test] fn test_pagination_single_char() { - let pages = calculate_pages("A", 1); + let pages = calculate_pages("A", 10, 5); assert_eq!(pages, vec![0, 1]); } #[test] fn test_pagination_zero_chars_per_page() { - let pages = calculate_pages("test", 0); + let pages = calculate_pages("test", 0, 5); assert_eq!(pages, vec![0]); } } \ No newline at end of file