mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-01 14:08:23 -06:00
fix(engine): print as low-resolution pictures when only that is allowed
Printing checked the print permission but not the separate high-quality one (Table 22, bit 12), so a document limited to low-resolution printing came out as its original vector pages, text included. Such a document now always prints as pictures of at most 150 dpi, whatever the settings ask for.
This commit is contained in:
@@ -232,6 +232,10 @@ pub struct DocId(pub u64);
|
||||
/// Undo/redo depth. Snapshots share unchanged data, so this is memory-cheap.
|
||||
const MAX_UNDO: usize = 100;
|
||||
|
||||
/// The highest resolution a document that allows only low-resolution printing (Table 22, bit 12
|
||||
/// clear) is printed at.
|
||||
const LOW_RESOLUTION_PRINT_DPI: f64 = 150.0;
|
||||
|
||||
/// The passwords that go with one state of a document (protection can change them).
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
struct Keys {
|
||||
@@ -4522,9 +4526,15 @@ impl Session {
|
||||
if !doc.allows_printing() {
|
||||
return Err(EditError::NotPermitted("printing"));
|
||||
}
|
||||
// Table 22, bit 12 clear: only a low-level representation may be printed, so the pages
|
||||
// always go as pictures, at no more than the low resolution.
|
||||
let low_res = doc.permissions().is_some_and(|p| !p.print_high_quality()).then(|| print::Raster {
|
||||
dpi: settings.raster.map_or(LOW_RESOLUTION_PRINT_DPI, |r| r.dpi.min(LOW_RESOLUTION_PRINT_DPI)),
|
||||
grayscale: settings.raster.is_some_and(|r| r.grayscale),
|
||||
});
|
||||
// Print as image: the pages used become pictures first, then the picture document is
|
||||
// imposed in their place (its page k is the k-th distinct source page used).
|
||||
if let Some(raster) = &settings.raster {
|
||||
if let Some(raster) = low_res.as_ref().or(settings.raster.as_ref()) {
|
||||
let mut distinct: Vec<usize> = Vec::new();
|
||||
for &p in &settings.pages {
|
||||
if !distinct.contains(&p) {
|
||||
|
||||
@@ -1340,6 +1340,38 @@ fn print_restricted_documents_disable_print_unless_opened_by_the_owner() {
|
||||
assert!(enabled(&s, low, "print.dialog"), "low-resolution printing is printing");
|
||||
}
|
||||
|
||||
/// Table 22, bit 12 clear: printing is limited to a low-level representation. Whatever the
|
||||
/// settings ask for, the print-ready PDF carries each page as a picture of at most 150 dpi, with
|
||||
/// no text or vector artwork left; the owner password lifts the limit.
|
||||
#[test]
|
||||
fn low_resolution_printing_prints_pictures_of_at_most_150_dpi() {
|
||||
let bytes = protected("", "owner", -1 ^ (1 << 11));
|
||||
let mut s = Session::new();
|
||||
let low = s.open("low.pdf", None, bytes.clone(), None).unwrap();
|
||||
let width = f64::from(s.get(low).unwrap().info.pages[0].width);
|
||||
let actual = print::Settings { pages: vec![0], layout: print::Layout::Size(print::SizeMode::Actual), ..Default::default() };
|
||||
for raster in [None, Some(print::Raster { dpi: 600.0, grayscale: false })] {
|
||||
let printed = s.print_pdf(low, &print::Settings { raster, ..actual.clone() }).unwrap();
|
||||
let mut s2 = Session::new();
|
||||
let id2 = s2.open("print.pdf", None, Arc::new(printed.clone()), None).unwrap();
|
||||
assert!(page_texts(&s2, id2).iter().all(|t| t.trim().is_empty()), "{raster:?}: no text is left");
|
||||
let doc = pdfcraft_cos::Document::open(Arc::new(printed)).unwrap();
|
||||
let widths: Vec<i64> = doc
|
||||
.scan_objects()
|
||||
.filter_map(|(_, o)| match &*o {
|
||||
pdfcraft_cos::Object::Stream(st) if st.dict.name(b"Subtype") == Some(b"Image") => st.dict.int(b"Width"),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
assert!(!widths.is_empty() && widths.iter().all(|&w| w as f64 <= width * 150.0 / 72.0 + 1.0), "{raster:?}: {widths:?} px for {width} pt");
|
||||
}
|
||||
let owner = s.open("low.pdf", None, bytes, Some("owner")).unwrap();
|
||||
let printed = s.print_pdf(owner, &actual).unwrap();
|
||||
let mut s2 = Session::new();
|
||||
let id2 = s2.open("print.pdf", None, Arc::new(printed), None).unwrap();
|
||||
assert!(page_texts(&s2, id2).iter().any(|t| !t.trim().is_empty()), "the owner prints the pages themselves");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_permissions_password_unlocks_an_open_document() {
|
||||
// Only a permissions password (#995, #1077): opens without asking. Changes are allowed,
|
||||
|
||||
Reference in New Issue
Block a user