Fit to width fits the shown page in single-page view (#808) (#859)

This commit is contained in:
MarekSpodBiedry
2026-10-10 15:31:20 -04:00
committed by GitHub
parent 40a57f0d19
commit c7b9db8faa
+37 -6
View File
@@ -1357,7 +1357,7 @@ impl DocView {
let avail_w = (self.viewport_w - 2.0 * SIDE).max(100.0);
let per_row = if self.layout == PageLayout::TwoUp { 2.0 } else { 1.0 };
match self.fit {
Fit::Width => self.zoom = (avail_w - GAP * (per_row - 1.0)) / (max_w * PT * per_row),
Fit::Width => self.zoom = (avail_w - GAP * (per_row - 1.0)) / (w * PT * per_row),
Fit::Page => {
let zw = (avail_w - GAP * (per_row - 1.0)) / (w * PT * per_row);
let zh = (self.viewport_h - 2.0 * MARGIN) / (h * PT);
@@ -1369,6 +1369,16 @@ impl DocView {
self.zoom = self.zoom.clamp(0.08, 64.0);
}
/// Width of the scroll content. Single-page view sizes it to the page it shows, so a
/// wider page elsewhere in the document can't push the shown one off-centre.
fn content_width(&self, info: &DocInfo, avail_w: f32) -> f32 {
let single = (self.layout == PageLayout::Single).then(|| self.current.min(info.pages.len().saturating_sub(1)));
let shown = info.pages.iter().enumerate().filter(|&(i, _)| single.is_none_or(|c| c == i));
let max_w = shown.map(|(_, p)| self.display_size(p).0).fold(0.0, f32::max);
let per_row = if self.layout == PageLayout::TwoUp { 2.0 } else { 1.0 };
(max_w * self.zoom * PT * per_row + 2.0 * SIDE).max(avail_w)
}
/// Page rects in content coordinates (origin at the scroll content's top-left).
fn layout(&self, info: &DocInfo, content_w: f32) -> Vec<Rect> {
let s = self.zoom * PT;
@@ -1755,11 +1765,7 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) {
Vec2::ZERO
};
let max_w = info.pages.iter().map(|p| view.display_size(p).0).fold(0.0, f32::max)
* view.zoom
* PT
* if view.layout == PageLayout::TwoUp { 2.0 } else { 1.0 };
let content_w = (max_w + 2.0 * SIDE).max(avail.width());
let content_w = view.content_width(info, avail.width());
let rects = view.layout(info, content_w);
let middle_gesture = view.auto_scroll.blocks_input();
// Single page: when the whole page fits, the wheel would do nothing, so it turns pages
@@ -4079,6 +4085,31 @@ trailer << /Root 1 0 R >>
}
}
#[test]
fn fit_width_fits_the_shown_page_in_single_page_view_only() {
let page =
|width: f32, height: f32| pdfcraft_render::PageInfo { width, height, label: String::new(), crop: [0.0, 0.0, width, height], rotation: 0 };
let info = DocInfo { pages: vec![page(300.0, 400.0), page(600.0, 400.0)], ..Default::default() };
let mut v = DocView::new(DocId(1), &info, ViewDefaults::default());
(v.fit, v.viewport_w, v.viewport_h) = (Fit::Width, 1000.0, 800.0);
v.layout = PageLayout::Single;
v.fit_zoom(&info);
let narrow = v.zoom;
// The shown page fills the width between the side gutters, not centred for the wide page.
let shown = v.layout(&info, v.content_width(&info, 1000.0))[0];
assert!((shown.left() - SIDE).abs() < 1e-2 && (shown.width() - 860.0).abs() < 1e-2, "{shown:?}");
v.current = 1;
v.fit_zoom(&info);
assert!((narrow / v.zoom - 2.0).abs() < 1e-3, "the narrow page gets twice the zoom of the wide one");
// Scrolling views keep one zoom for the whole document.
v.layout = PageLayout::Continuous;
v.fit_zoom(&info);
let wide = v.zoom;
v.current = 0;
v.fit_zoom(&info);
assert_eq!(v.zoom, wide, "continuous fit width holds still across page sizes");
}
#[test]
fn layouts_match_their_commands_and_names() {
for l in PageLayout::ORDER {