mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-06 18:52:36 +00:00
fix(annot): a 3D poster fitted to its rectangle with rounding is taken
A poster box fitted to the annotation rectangle in floating point can end a few ulps outside it (a 7x3 picture centred in 0 100 3.6 110.8 spans -2.2e-16 to 3.6000000000000005), and the exact containment check refused it. The check now allows a slack of 1e-9 of the rectangle's largest coordinate, clamps the box to the rectangle, and only then requires it to have width and height, so a box wholly within the slack outside the rectangle is still refused. Tests: a_3d_poster_fitted_to_its_rectangle_with_rounding_is_taken fails without the slack (Invalid); the new case in a_3d_poster_outside_its_rectangle_or_without_a_size_is_refused fails when the size check runs before the clamp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
This commit is contained in:
1 parent
16ffab9b03
commit
9da253ebee
2 files changed
+25
-2
No files matched your search
@@ -2296,6 +2296,7 @@ fn a_3d_poster_outside_its_rectangle_or_without_a_size_is_refused() {
|
||||
(picture([50.0, 100.0, 250.0, 300.0], (10.0, 10.0)), "starts left of the rectangle"),
|
||||
(picture([100.0, 100.0, 300.0, 301.0], (10.0, 10.0)), "ends above it"),
|
||||
(picture([100.0, 100.0, 300.0, 300.0], (0.0, 10.0)), "has no width"),
|
||||
(picture([100.0 - 2e-7, 150.0, 100.0 - 1e-7, 200.0], (10.0, 10.0)), "lies outside within the rounding slack"),
|
||||
] {
|
||||
let mut doc = fixture();
|
||||
let new = NewModel3d {
|
||||
@@ -2310,3 +2311,22 @@ fn a_3d_poster_outside_its_rectangle_or_without_a_size_is_refused() {
|
||||
assert_eq!(list(&doc, 0).len(), 1, "{why}: nothing added");
|
||||
}
|
||||
}
|
||||
|
||||
/// A poster box fitted to the rectangle in floating point ends an ulp outside it: a 7×3 picture
|
||||
/// centred in [0 100 3.6 110.8] spans -2.2e-16 to 3.6000000000000005. It is taken, inside the
|
||||
/// rectangle.
|
||||
#[test]
|
||||
fn a_3d_poster_fitted_to_its_rectangle_with_rounding_is_taken() {
|
||||
let mut doc = fixture();
|
||||
let at = [-2.220446049250313e-16, 104.62857142857143, 3.6000000000000005, 106.17142857142858];
|
||||
let poster = PosterPicture { xobject: ObjRef { num: 6, generation: 0 }, image: false, size: (7.0, 3.0), at };
|
||||
let new = NewModel3d {
|
||||
page: 0,
|
||||
rect: [0.0, 100.0, 3.6, 110.8],
|
||||
model: b"PRC rest",
|
||||
activation: Activation3d::default(),
|
||||
poster: Some(poster),
|
||||
contents: "",
|
||||
};
|
||||
assert_eq!(add_3d_model(&mut doc, &new, &meta("m")), Ok(1));
|
||||
}
|
||||
@@ -199,8 +199,11 @@ fn annotation_dict(new: &NewModel3d<'_>, rect: [f64; 4], page: ObjRef, stream: O
|
||||
/// The poster drawing `p` into its box, which must lie within `rect`.
|
||||
fn picture_poster(rect: [f64; 4], p: &PosterPicture) -> Result<Stream, AnnotError> {
|
||||
let at = normalize(p.at);
|
||||
let inside = finite(&p.at) && at[0] >= rect[0] && at[1] >= rect[1] && at[2] <= rect[2] && at[3] <= rect[3] && at[2] > at[0] && at[3] > at[1];
|
||||
if !inside {
|
||||
// A box fitted to `rect` in floating point can end a few ulps outside it: allow that, then clamp.
|
||||
let slack = rect.iter().fold(1.0_f64, |m, v| m.max(v.abs())) * 1e-9;
|
||||
let near = finite(&p.at) && at[0] >= rect[0] - slack && at[1] >= rect[1] - slack && at[2] <= rect[2] + slack && at[3] <= rect[3] + slack;
|
||||
let at = [at[0].max(rect[0]), at[1].max(rect[1]), at[2].min(rect[2]), at[3].min(rect[3])];
|
||||
if !(near && at[2] > at[0] && at[3] > at[1]) {
|
||||
return Err(AnnotError::Invalid("the poster must fit inside the 3D model's rectangle".into()));
|
||||
}
|
||||
let place = appearance::picture_place(at, p.image, Some(p.size)).ok_or_else(|| AnnotError::Invalid("the poster picture has no size".into()))?;
|
||||
|
||||
Reference in new issue
Block a user