diff --git a/crates/model/README.md b/crates/model/README.md index 06c30b039..5872b2566 100644 --- a/crates/model/README.md +++ b/crates/model/README.md @@ -9,7 +9,8 @@ L3/L4 crates need: geometry, and the matrix from "display space" (points, origin at the bottom-left of the page as shown, after `/UserUnit` and `/Rotate`) to user space, so content can be placed the way the reader sees the page. `/UserUnit` is read as Acrobat reads it: the page's own entry, a number - from 1 up, at most 75,000; anything else counts as 1. + from 1 up, at most 75,000; anything else counts as 1. A `/Rotate` that isn't a multiple of 90 + counts as 0, as the renderer draws it. `view_matrix_for(rotation, rect)` is the same matrix for any rectangle, e.g. to draw a comment's picture upright inside its user-space `/Rect` on a turned page. diff --git a/crates/model/src/lib.rs b/crates/model/src/lib.rs index 4e4f3f53b..211b44492 100644 --- a/crates/model/src/lib.rs +++ b/crates/model/src/lib.rs @@ -37,9 +37,11 @@ impl Page { } } - /// Clockwise rotation when displayed: 0, 90, 180 or 270. + /// Clockwise rotation when displayed: 0, 90, 180 or 270. A `/Rotate` that isn't a multiple + /// of 90 (ISO 32000-2 ยง7.7.3.3 requires one) counts as 0, as the renderer draws that page. pub fn rotation(&self, doc: &Document) -> i64 { - self.dict.get(b"Rotate").and_then(|o| doc.resolve(o).as_int()).unwrap_or(0).rem_euclid(360) / 90 * 90 + let r = self.dict.get(b"Rotate").and_then(|o| doc.resolve(o).as_int()).unwrap_or(0).rem_euclid(360); + if r % 90 == 0 { r } else { 0 } } /// The size of the page's user-space unit in points (`/UserUnit`; it isn't inherited), read @@ -184,6 +186,21 @@ mod tests { } } + /// A `/Rotate` that isn't a quarter turn counts as 0, as the renderer draws the page + /// upright, so display space matches what's on screen. + #[test] + fn rotate_off_a_quarter_turn_counts_as_upright() { + for rotate in [135, 30, -45, 359] { + let d = doc(rotate); + let p = &pages(&d)[0]; + assert_eq!(p.rotation(&d), 0, "{rotate}"); + assert_eq!(p.display_size(&d), (200.0, 300.0), "{rotate}"); + assert_eq!(p.view_matrix(&d), [1.0, 0.0, 0.0, 1.0, 10.0, 20.0], "{rotate}"); + } + let d = doc(-90); + assert_eq!(pages(&d)[0].rotation(&d), 270); + } + /// Display space is in points: `/UserUnit 2` doubles it. As in Acrobat, the value isn't /// inherited, values below 1 or not numbers count as 1, and values over 75,000 as 75,000. #[test]