mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 20:27:05 -06:00
Merge pull request #4612 from VoiceLessQ/fix/4366-name-content-in-an-array-object
PDF import: a page's content listed in an array object is named as content (refs #4366)
This commit is contained in:
@@ -3850,8 +3850,8 @@ fn pdf_a_huge_form_drawn_over_and_over_is_refused_in_bounded_time() {
|
||||
#[test]
|
||||
fn a_stream_past_the_cap_is_named() {
|
||||
let image = "/Type /XObject /Subtype /Image /Width 10 /Height 10 /ColorSpace /DeviceRGB /BitsPerComponent 8";
|
||||
let page = |entries: &str| {
|
||||
format!("<< /Type /Page /Parent 2 0 R /MediaBox [0 0 100 100] /Contents 4 0 R /Resources << /XObject << /Im 5 0 R >> >> {entries} >>")
|
||||
let page = |contents: &str, entries: &str| {
|
||||
format!("<< /Type /Page /Parent 2 0 R /MediaBox [0 0 100 100] /Contents {contents} /Resources << /XObject << /Im 5 0 R >> >> {entries} >>")
|
||||
.into_bytes()
|
||||
};
|
||||
let plain = |content: &str| format!("<< /Length {} >>\nstream\n{content}\nendstream", content.len() + 1).into_bytes();
|
||||
@@ -3864,17 +3864,27 @@ fn a_stream_past_the_cap_is_named() {
|
||||
let files = [
|
||||
(
|
||||
"an image",
|
||||
pdf(page(""), vec![plain("q 10 0 0 10 0 0 cm /Im Do Q"), packed_as_is(image, &spaces)]),
|
||||
pdf(page("4 0 R", ""), vec![plain("q 10 0 0 10 0 0 cm /Im Do Q"), packed_as_is(image, &spaces)]),
|
||||
"an image of 10 × 10 px in it (object 5) would decode to more than 256 MB",
|
||||
),
|
||||
(
|
||||
"a page's content",
|
||||
pdf(page(""), vec![packed_as_is("", &spaces), packed(image, "\0\0\0")]),
|
||||
pdf(page("4 0 R", ""), vec![packed_as_is("", &spaces), packed(image, "\0\0\0")]),
|
||||
"a page's content in it (object 4) would decode to more than 256 MB",
|
||||
),
|
||||
(
|
||||
"a page's content in an array",
|
||||
pdf(page("[4 0 R]", ""), vec![packed_as_is("", &spaces), packed(image, "\0\0\0")]),
|
||||
"a page's content in it (object 4) would decode to more than 256 MB",
|
||||
),
|
||||
(
|
||||
"a page's content in an array of its own",
|
||||
pdf(page("6 0 R", ""), vec![packed_as_is("", &spaces), packed(image, "\0\0\0"), b"[4 0 R]".to_vec()]),
|
||||
"a page's content in it (object 4) would decode to more than 256 MB",
|
||||
),
|
||||
(
|
||||
"the editing data",
|
||||
pdf(page(editing), vec![plain("0 0 5 5 re f"), packed(image, "\0\0\0"), packed_as_is("", &spaces)]),
|
||||
pdf(page("4 0 R", editing), vec![plain("0 0 5 5 re f"), packed(image, "\0\0\0"), packed_as_is("", &spaces)]),
|
||||
"its editing data (object 6) would decode to more than 256 MB",
|
||||
),
|
||||
];
|
||||
@@ -3888,6 +3898,39 @@ fn a_stream_past_the_cap_is_named() {
|
||||
});
|
||||
}
|
||||
|
||||
// Pages that share one `Contents` array, the array an object of its own: its stream is named as
|
||||
// a page's content, and a refused stream elsewhere is still found past the pages (#4612 review).
|
||||
#[test]
|
||||
fn pages_sharing_a_contents_array_name_its_stream() {
|
||||
let image = "/Type /XObject /Subtype /Image /Width 10 /Height 10 /ColorSpace /DeviceRGB /BitsPerComponent 8";
|
||||
let plain = |content: &str| format!("<< /Length {} >>\nstream\n{content}\nendstream", content.len() + 1).into_bytes();
|
||||
let spaces = spaces_past_the_cap();
|
||||
// 1 catalog, 2 pages, 3 the shared array, 4 its stream, 5 to 7 the pages, 8 an image they draw.
|
||||
let pdf = |content: Vec<u8>, image: Vec<u8>| {
|
||||
let page = b"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 100 100] /Contents 3 0 R /Resources << /XObject << /Im 8 0 R >> >> >>";
|
||||
let head = [
|
||||
b"<< /Type /Catalog /Pages 2 0 R >>".to_vec(),
|
||||
b"<< /Type /Pages /Kids [5 0 R 6 0 R 7 0 R] /Count 3 >>".to_vec(),
|
||||
b"[4 0 R]".to_vec(),
|
||||
content,
|
||||
];
|
||||
pdf_of(&head.into_iter().chain([page.to_vec(), page.to_vec(), page.to_vec(), image]).collect::<Vec<_>>())
|
||||
};
|
||||
let files = [
|
||||
(pdf(packed_as_is("", &spaces), packed(image, "\0\0\0")), "a page's content in it (object 4) would decode to more than 256 MB"),
|
||||
(
|
||||
pdf(plain("q 10 0 0 10 0 0 cm /Im Do Q"), packed_as_is(image, &spaces)),
|
||||
"an image of 10 × 10 px in it (object 8) would decode to more than 256 MB",
|
||||
),
|
||||
];
|
||||
on_a_small_stack(move || {
|
||||
for (bytes, why) in &files {
|
||||
let e = vectorcraft_engine::cmd::fileio::load("shared.pdf", bytes).err().map(|e| e.to_string());
|
||||
assert!(e.as_deref().is_some_and(|e| e.contains(why)), "{why}: {e:?}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pdf_art_that_draws_itself() {
|
||||
let own = "<< /XObject << /F 5 0 R >> /Pattern << /P 5 0 R >> /Font << /F1 5 0 R >> /ExtGState << /G << /SMask << /S /Alpha /G 5 0 R >> >> >> >>";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! The pages of a PDF to import: opening with a password, the pages picked, the box each page
|
||||
//! is cropped to ([`CropTo`]) and [`info`].
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use craft_pdf::syntax::object::{Array, Dict, MaybeRef, Name, Object, ObjectIdentifier, Stream, dict_or_stream};
|
||||
use craft_pdf::syntax::page::Page;
|
||||
@@ -193,9 +193,16 @@ fn stream_kind(pdf: &Pdf, id: ObjectIdentifier, dict: &Dict<'_>) -> String {
|
||||
}
|
||||
let is = |m: Option<MaybeRef<Object<'_>>>| matches!(m, Some(MaybeRef::Ref(r)) if ObjectIdentifier::from(r) == id);
|
||||
let pages = pdf.pages();
|
||||
// `Contents` is a stream or an array of them, the array in the page or an object of its own.
|
||||
// Each object is read once however many pages share it, so a file can't make this quadratic.
|
||||
let mut read = HashSet::new();
|
||||
let content = pages.iter().any(|page| match page.raw().get_raw::<Object<'_>>(b"Contents") {
|
||||
Some(MaybeRef::NotRef(Object::Array(a))) => items(&a).any(|m| is(Some(m))),
|
||||
other => is(other),
|
||||
Some(MaybeRef::Ref(r)) => {
|
||||
let at = ObjectIdentifier::from(r);
|
||||
read.insert(at) && page.raw().get::<Array<'_>>(b"Contents").map_or(at == id, |a| items(&a).any(|m| is(Some(m))))
|
||||
}
|
||||
_ => false,
|
||||
});
|
||||
if content {
|
||||
return "a page's content in it".to_string();
|
||||
|
||||
Reference in New Issue
Block a user