refactor(engine): drop redundant image and font checks

- page_image_at: PageImage::contains already requires the point inside
  visible_rect (rect clipped by the placement clip), so the extra bounds
  test before it decided nothing.
- image_dimensions: the 256 MB input-length guard repeated the caller's;
  the decoder's max_alloc limit still caps what decoding allocates. Its
  test no longer allocates 256 MB to hit that guard.
- font_content_key hashed every font file twice; one 64-bit hash plus the
  length is enough to tell duplicate files apart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
FredApps
2026-11-01 12:33:49 +01:00
co-authored by Claude Opus 5.5
parent 4c70761803
commit 3817a9f3ec
3 changed files with 6 additions and 23 deletions
+1 -9
View File
@@ -18,16 +18,8 @@ impl Document {
/// [`Document::page_images`]: a raster image, or Form artwork that holds one.
pub fn page_image_at(&self, page: usize, at: [f64; 2]) -> Option<usize> {
let editor = self.editor.as_ref()?;
let inside = |r: &[f64; 4]| (r[0]..=r[2]).contains(&at[0]) && (r[1]..=r[3]).contains(&at[1]);
self.page_images(page).iter().rposition(|i| {
let bounds = if let Some(clip) = i.clip_rect {
let r = [i.rect[0].max(clip[0]), i.rect[1].max(clip[1]), i.rect[2].min(clip[2]), i.rect[3].min(clip[3])];
if r.iter().all(|v| v.is_finite()) && r[2] > r[0] && r[3] > r[1] { r } else { i.visible_rect.unwrap_or(i.rect) }
} else {
i.visible_rect.unwrap_or(i.rect)
};
inside(&bounds)
&& i.contains(at)
i.contains(at)
&& match (i.is_form, i.object) {
(false, Some(_)) => true,
(true, Some(form)) => picture_in_form(&editor.cos, form).is_some(),
+1 -7
View File
@@ -62,9 +62,6 @@ pub(crate) fn mark_source(doc: &mut pdfcraft_cos::Document, f: &MarkFile) -> Res
/// The dimensions of an image from its bytes (JPEG, PNG, TIFF), without decoding the full raster.
pub fn image_dimensions(bytes: &[u8]) -> Option<(u32, u32)> {
if bytes.len() > 64_000_000 * 4 {
return None;
}
let format = image::guess_format(bytes).ok()?;
let mut reader = image::ImageReader::with_format(std::io::Cursor::new(bytes), format);
let mut limits = image::Limits::default();
@@ -123,11 +120,8 @@ mod tests {
}
#[test]
fn invalid_and_oversized_inputs_return_none() {
fn invalid_inputs_return_none() {
assert_eq!(image_dimensions(&[]), None);
assert_eq!(image_dimensions(b"not an image"), None);
let huge = vec![0u8; 64_000_000 * 4 + 1];
assert_eq!(image_dimensions(&huge), None);
}
}
+4 -7
View File
@@ -89,14 +89,11 @@ pub fn discover_budgeted(roots: &[PathBuf], mut budget: u64) -> Vec<FontFile> {
files
}
fn font_content_key(bytes: &[u8]) -> (usize, u64, u64) {
fn font_content_key(bytes: &[u8]) -> (usize, u64) {
use std::hash::{DefaultHasher, Hasher};
let mut h1 = DefaultHasher::new();
h1.write(bytes);
let mut h2 = DefaultHasher::new();
h2.write_u8(0xff);
h2.write(bytes);
(bytes.len(), h1.finish(), h2.finish())
let mut h = DefaultHasher::new();
h.write(bytes);
(bytes.len(), h.finish())
}
fn font_extension(path: &Path) -> bool {
path.extension().and_then(std::ffi::OsStr::to_str).is_some_and(|e| e.eq_ignore_ascii_case("ttf") || e.eq_ignore_ascii_case("otf"))