mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-28 03:58:45 -06:00
fix(print): list the source pages once per imposition
page_form listed every page of the document to take the one it printed, so imposing P pages walked the page tree and copied its leaf dictionaries P times over: P^2 page copies. impose passes the list it already holds. A page index outside it is an error, not a panic.
This commit is contained in:
+20
-3
@@ -770,13 +770,16 @@ fn appearance_matrix(doc: &Document, form: &Dict, rect: [f64; 4]) -> Option<Matr
|
||||
}
|
||||
|
||||
/// A Form XObject holding page `index` as it prints (user space; BBox = crop box).
|
||||
/// `pages` are the document's pages, listed once by the caller: listing them for each page
|
||||
/// printed would make printing quadratic in the page count.
|
||||
fn page_form(
|
||||
doc: &mut Document,
|
||||
pages: &[pdfcraft_model::Page],
|
||||
index: usize,
|
||||
content: Content,
|
||||
promoted: &mut std::collections::HashMap<ObjRef, ObjRef>,
|
||||
) -> Result<ObjRef, PrintError> {
|
||||
let page = pdfcraft_model::pages(doc).swap_remove(index);
|
||||
let page = pages.get(index).ok_or_else(|| PrintError::Invalid(format!("there is no page {}", index.saturating_add(1))))?;
|
||||
let crop = page.crop(doc);
|
||||
let mut res = page.dict.get(b"Resources").and_then(|r| doc.resolve(r).as_dict().cloned()).unwrap_or_default();
|
||||
let mut body = if content == Content::FormFieldsOnly { Vec::new() } else { decoded_contents(doc, &page.dict) };
|
||||
@@ -841,10 +844,24 @@ fn page_form(
|
||||
Ok(doc.add(Object::Stream(Stream::flate(d, &body))))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
thread_local! {
|
||||
/// Pages listed by [`source_pages`] on this thread.
|
||||
static PAGES_LISTED: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
|
||||
}
|
||||
|
||||
/// The source document's pages, for printing.
|
||||
fn source_pages(doc: &Document) -> Vec<pdfcraft_model::Page> {
|
||||
let pages = pdfcraft_model::pages(doc);
|
||||
#[cfg(test)]
|
||||
PAGES_LISTED.with(|c| c.set(c.get() + pages.len()));
|
||||
pages
|
||||
}
|
||||
|
||||
/// Build the print-ready PDF for `settings`. Encryption is not carried over (the file goes to a
|
||||
/// printer or is the user's own copy); callers must check the print permission first.
|
||||
pub fn impose(src: &Document, settings: &Settings) -> Result<Vec<u8>, PrintError> {
|
||||
let pages = pdfcraft_model::pages(src);
|
||||
let pages = source_pages(src);
|
||||
let sizes: Vec<(f64, f64)> = pages.iter().map(|p| p.display_size(src)).collect();
|
||||
let sheets = layout(&sizes, settings)?;
|
||||
let mut doc = src.clone();
|
||||
@@ -861,7 +878,7 @@ pub fn impose(src: &Document, settings: &Settings) -> Result<Vec<u8>, PrintError
|
||||
let form = match forms.get(&pl.page) {
|
||||
Some(f) => *f,
|
||||
None => {
|
||||
let f = page_form(&mut doc, pl.page, settings.content, &mut promoted)?;
|
||||
let f = page_form(&mut doc, &pages, pl.page, settings.content, &mut promoted)?;
|
||||
forms.insert(pl.page, f);
|
||||
f
|
||||
}
|
||||
|
||||
@@ -192,6 +192,17 @@ fn custom_pages_per_sheet_never_overfill_the_grid() {
|
||||
assert_eq!(pdfcraft_model::pages(&Document::open(Arc::new(out)).unwrap()).len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn imposing_lists_the_pages_once() {
|
||||
let n = 150;
|
||||
let doc = fixture(n);
|
||||
PAGES_LISTED.with(|c| c.set(0));
|
||||
let out = impose(&doc, &settings((0..n).collect(), Layout::multiple(2))).unwrap();
|
||||
let listed = PAGES_LISTED.with(|c| c.get());
|
||||
assert!(listed <= 2 * n, "{listed} pages listed to print {n}");
|
||||
assert_eq!(pdfcraft_model::pages(&Document::open(Arc::new(out)).unwrap()).len(), n / 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn booklets_pair_pages_for_folding() {
|
||||
let sizes = vec![(200.0, 300.0); 6];
|
||||
|
||||
@@ -147,7 +147,8 @@ fn promotion_preserves_streams_and_explicit_subtypes() {
|
||||
for subtype in [None, Some(Object::name("Form")), Some(Object::name("Image")), Some(Object::Null), Some(Object::Int(7))] {
|
||||
let (mut doc, ap, _) = widget_fixture(subtype.clone(), Some([2.0, 0.0, 0.0, 3.0, 5.0, 7.0]));
|
||||
let original = doc.get(ap);
|
||||
let form = page_form(&mut doc, 0, Content::Document, &mut std::collections::HashMap::new()).unwrap();
|
||||
let pages = pdfcraft_model::pages(&doc);
|
||||
let form = page_form(&mut doc, &pages, 0, Content::Document, &mut std::collections::HashMap::new()).unwrap();
|
||||
assert_eq!(doc.get(ap), original, "original in the print clone");
|
||||
let promoted = xobject(&doc, doc.get(form).as_dict().unwrap(), b"PCPrA0");
|
||||
if subtype.is_some() {
|
||||
|
||||
Reference in New Issue
Block a user