mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-28 03:58:45 -06:00
Save as PDF/A removes the encryption (PDF/A forbids it), but it was gated only by the modify permission, so a document opened with the user password whose owner withheld printing or copying could be saved unencrypted with every restriction gone. It now needs the same rights as Protect and Remove security, also inside a batch. Fixes #3921
This commit is contained in:
@@ -2274,6 +2274,15 @@ fn check_permission(edit: &Edit, p: &pdfcraft_cos::Permissions) -> Result<(), Ed
|
||||
Err(EditError::NotPermitted("changing security"))
|
||||
}
|
||||
}
|
||||
// Converting to PDF/A takes the encryption off (PDF/A forbids it), so on an encrypted
|
||||
// document it is a change of security (#3921).
|
||||
Edit::ConvertPdfA { .. } => {
|
||||
if unrestricted(p) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(EditError::NotPermitted("removing the encryption, which PDF/A forbids"))
|
||||
}
|
||||
}
|
||||
Edit::SetInfo { .. }
|
||||
| Edit::AddHeaderFooter { .. }
|
||||
| Edit::AddWatermark { .. }
|
||||
@@ -2314,7 +2323,6 @@ fn check_permission(edit: &Edit, p: &pdfcraft_cos::Permissions) -> Result<(), Ed
|
||||
| Edit::AddOcrText { .. }
|
||||
| Edit::SetDocumentScript { .. }
|
||||
| Edit::SetFieldScript { .. }
|
||||
| Edit::ConvertPdfA { .. }
|
||||
| Edit::RemoveStandardId { .. }
|
||||
| Edit::EditTextLine { .. }
|
||||
| Edit::EditTextBlock { .. }
|
||||
|
||||
@@ -1888,6 +1888,34 @@ fn permissions_password_restricts_others_but_not_this_session() {
|
||||
assert!(s2.get(id3).unwrap().allows_security_change());
|
||||
}
|
||||
|
||||
/// Converting to PDF/A takes the encryption off (PDF/A forbids it), so on an encrypted document
|
||||
/// it needs the same rights as removing security, not just the right to make changes (#3921).
|
||||
#[test]
|
||||
fn convert_to_pdfa_needs_the_owners_rights_on_an_encrypted_document() {
|
||||
let pdfa = || Edit::ConvertPdfA { level: pdfcraft_preflight::Level::A2b };
|
||||
let refused = Err(EditError::NotPermitted("removing the encryption, which PDF/A forbids"));
|
||||
// Changes allowed; printing (bit 3) and copying (bit 5) withheld.
|
||||
let bytes = protected("pw", "owner", !((1 << 2) | (1 << 4)));
|
||||
let mut s = Session::new();
|
||||
let id = s.open("x.pdf", None, bytes.clone(), Some("pw")).unwrap();
|
||||
assert!(s.get(id).unwrap().allows_modification());
|
||||
assert_eq!(s.apply(id, pdfa()), refused);
|
||||
// Nor inside a batch.
|
||||
let batch = Edit::Batch { label: "x".into(), edits: vec![Edit::RotatePages { pages: vec![0], degrees: 90 }, pdfa()] };
|
||||
assert_eq!(s.apply(id, batch), refused);
|
||||
// Nothing was applied: no undo step, and saving gives back the encrypted original.
|
||||
assert!(s.get(id).unwrap().can_undo().is_none());
|
||||
assert_eq!(s.save_bytes(id).unwrap().as_slice(), bytes.as_slice());
|
||||
// The owner's password allows it, and the result is no longer encrypted.
|
||||
let id2 = s.open("x.pdf", None, bytes, Some("owner")).unwrap();
|
||||
s.apply(id2, pdfa()).unwrap();
|
||||
let saved = s.save_bytes(id2).unwrap();
|
||||
assert!(pdfcraft_cos::Document::open(saved).unwrap().trailer().get(b"Encrypt").is_none());
|
||||
// An unencrypted document needs nothing more.
|
||||
let (mut s, id) = session_with(1);
|
||||
s.apply(id, pdfa()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn combine_source_check_says_why_a_file_cannot_be_combined() {
|
||||
let (mut s, id) = session_with(1);
|
||||
|
||||
@@ -214,7 +214,7 @@ milestone = "M1"
|
||||
status = "shipped"
|
||||
commands = []
|
||||
tools = []
|
||||
evidence = ["crates/ui-egui/tests/it/editing.rs::restricted_documents_show_a_notice_and_block_page_changes", "crates/crypt/src/tests.rs::permission_bits_follow_table_22", "crates/crypt/src/tests.rs::perms_limits_what_an_edited_p_grants", "crates/engine/src/tests.rs::combining_protected_files_is_refused_clearly", "crates/engine/src/tests.rs::print_restricted_documents_disable_print_unless_opened_by_the_owner", "crates/ui-egui/tests/it/print.rs::print_restricted_documents_say_so_before_the_dialog"]
|
||||
evidence = ["crates/ui-egui/tests/it/editing.rs::restricted_documents_show_a_notice_and_block_page_changes", "crates/crypt/src/tests.rs::permission_bits_follow_table_22", "crates/crypt/src/tests.rs::perms_limits_what_an_edited_p_grants", "crates/engine/src/tests.rs::combining_protected_files_is_refused_clearly", "crates/engine/src/tests.rs::print_restricted_documents_disable_print_unless_opened_by_the_owner", "crates/ui-egui/tests/it/print.rs::print_restricted_documents_say_so_before_the_dialog", "crates/engine/src/tests.rs::convert_to_pdfa_needs_the_owners_rights_on_an_encrypted_document"]
|
||||
notes = ""
|
||||
|
||||
[[feature]]
|
||||
@@ -7817,8 +7817,8 @@ milestone = "M11"
|
||||
status = "partial"
|
||||
commands = ["standards.pdfa"]
|
||||
tools = ["pdfa_convert"]
|
||||
evidence = ["crates/preflight/src/tests.rs::convert_fixes_all_but_fonts", "crates/preflight/src/tests.rs::convert_does_not_give_cmyk_documents_an_srgb_output_intent", "crates/automation/tests/it/automation.rs::pdfa_through_tools", "crates/ui-egui/tests/it/compare.rs::pdfa_dialog_verifies_and_converts"]
|
||||
notes = "Fixes XMP, output intent (generated sRGB ICC profile, only for documents without DeviceCMYK; CMYK documents need a user-chosen CMYK output intent, not yet offered), forbidden actions, annotation flags, interpolation, encryption, AFRelationship. Not yet: embedding fonts, converting device CMYK, flattening transparency for PDF/A-1."
|
||||
evidence = ["crates/preflight/src/tests.rs::convert_fixes_all_but_fonts", "crates/preflight/src/tests.rs::convert_does_not_give_cmyk_documents_an_srgb_output_intent", "crates/automation/tests/it/automation.rs::pdfa_through_tools", "crates/ui-egui/tests/it/compare.rs::pdfa_dialog_verifies_and_converts", "crates/engine/src/tests.rs::convert_to_pdfa_needs_the_owners_rights_on_an_encrypted_document"]
|
||||
notes = "Fixes XMP, output intent (generated sRGB ICC profile, only for documents without DeviceCMYK; CMYK documents need a user-chosen CMYK output intent, not yet offered), forbidden actions, annotation flags, interpolation, encryption, AFRelationship. Not yet: embedding fonts, converting device CMYK, flattening transparency for PDF/A-1. On an encrypted document it needs the owner password, as Protect and Remove security do, since it removes the encryption (#3921)."
|
||||
|
||||
[[feature]]
|
||||
id = "optimize.pdfa-accessible-levels"
|
||||
|
||||
Reference in New Issue
Block a user