mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-14 19:27:04 -04:00
CLI: text refuses a document that doesn't allow copying (#1650)
* CLI: text refuses a document that doesn't allow copying (part of #1645) * CLI: text's refusal points to --password
This commit is contained in:
1 parent
a0bb1f08a4
commit
2f09340af1
2 files changed
+45
-1
No files matched your search
@@ -338,11 +338,20 @@ fn text(args: &[String]) -> Result<(), CliError> {
|
||||
// no pages, and "extract every page" of nothing would print nothing and exit 0 (#132). Ask
|
||||
// `inspect` why instead, so a protected file fails the same way `info` does.
|
||||
if r.page_count() == 0 {
|
||||
let info = inspect(bytes, password).map_err(|e| format!("text: {e}"))?;
|
||||
let info = inspect(bytes.clone(), password).map_err(|e| format!("text: {e}"))?;
|
||||
if !info.pages.is_empty() {
|
||||
return Err("text: the document could not be parsed".into());
|
||||
}
|
||||
}
|
||||
// Printing the text is copying it, so ask what `text_extract` asks (#1645): without the
|
||||
// permissions password, a document that doesn't allow copying gives no text.
|
||||
let mut session = pdfcraft_engine::Session::new();
|
||||
if let Some(doc) = session.open(path, None, bytes, password).ok().and_then(|id| session.get(id)) {
|
||||
// The engine's hint is about editing in the app; on the command line the way in is
|
||||
// `--password`.
|
||||
doc.check_copying()
|
||||
.map_err(|_| "text: the document's permissions don't allow copying its text; pass the owner password with --password to extract it")?;
|
||||
}
|
||||
let pages: Vec<usize> = match selected_page {
|
||||
Some(page) => vec![page],
|
||||
None => (0..r.page_count()).collect(),
|
||||
|
||||
@@ -96,6 +96,41 @@ fn text_of_a_protected_document_with_the_password_succeeds() {
|
||||
assert!(stdout.contains("Page 1") && stdout.contains("Page 3"), "{stdout}");
|
||||
}
|
||||
|
||||
/// `three.pdf` encrypted so that the open password `openme` doesn't allow copying, and the
|
||||
/// permissions password `boss` lifts that.
|
||||
fn copy_protected_fixture() -> std::path::PathBuf {
|
||||
let src = tmp("to-protect-no-copy.pdf");
|
||||
std::fs::write(&src, fixture(3)).unwrap();
|
||||
let out_path = tmp("protected-no-copy.pdf");
|
||||
let script = tmp("protect-no-copy.json");
|
||||
let steps = serde_json::json!([
|
||||
{ "tool": "doc_open", "args": { "path": src } },
|
||||
{ "tool": "doc_protect", "args": { "doc": 1, "open_password": "openme", "permissions_password": "boss", "copy": false } },
|
||||
{ "tool": "doc_save", "args": { "doc": 1, "path": out_path } },
|
||||
]);
|
||||
std::fs::write(&script, steps.to_string()).unwrap();
|
||||
let out = Command::new(BIN).args(["run", "--script", script.to_str().unwrap()]).output().unwrap();
|
||||
assert!(out.status.success(), "protecting the fixture failed: {}", String::from_utf8_lossy(&out.stderr));
|
||||
out_path
|
||||
}
|
||||
|
||||
/// `text` copies text out, so it asks what `text_extract` asks (#1645): the open password alone
|
||||
/// gives no text when copying isn't allowed, the permissions password does.
|
||||
#[test]
|
||||
fn text_respects_a_document_that_does_not_allow_copying() {
|
||||
let path = copy_protected_fixture();
|
||||
let out = Command::new(BIN).args(["text", path.to_str().unwrap(), "--password", "openme"]).output().unwrap();
|
||||
assert!(!out.status.success(), "copying isn't allowed: {}", String::from_utf8_lossy(&out.stdout));
|
||||
assert!(out.stdout.is_empty(), "{}", String::from_utf8_lossy(&out.stdout));
|
||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(stderr.contains("don't allow copying"), "the reason must be named: {stderr}");
|
||||
assert!(stderr.contains("--password"), "and the way in: {stderr}");
|
||||
let out = Command::new(BIN).args(["text", path.to_str().unwrap(), "--password", "boss"]).output().unwrap();
|
||||
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
|
||||
let stdout = String::from_utf8_lossy(&out.stdout);
|
||||
assert!(stdout.contains("Page 1") && stdout.contains("Page 3"), "{stdout}");
|
||||
}
|
||||
|
||||
mod page_argument_tests {
|
||||
use super::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
Reference in new issue
Block a user