mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-25 10:39:01 -06:00
* CLI: text exits non-zero when a selected page cannot be read (#131) printcraft-cli text --page N reported the failure on stderr and still exited 0, so a script checking the status could not tell a failed extraction from a successful one. The failing pages are collected and the command now fails with them named, pinned by a test for the nonexistent-page case and one for a normal extraction. * Move the text exit-code test to the renamed pdfcraft-cli app Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Mohammed Munzir <Mohammed.Munzir@iga.gov.bh> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
60 lines
2.7 KiB
Rust
60 lines
2.7 KiB
Rust
//! `text` must fail loudly when a page it was asked for cannot be read (#131).
|
||
|
||
use std::process::Command;
|
||
|
||
const BIN: &str = env!("CARGO_BIN_EXE_pdfcraft-cli");
|
||
|
||
/// A PDF with `n` 200×300 pt pages reading "Page 1", "Page 2", …
|
||
fn fixture(n: usize) -> Vec<u8> {
|
||
let mut objs: Vec<Vec<u8>> = vec![b"<< /Type /Catalog /Pages 2 0 R >>".to_vec()];
|
||
let kids: Vec<String> = (0..n).map(|i| format!("{} 0 R", 4 + 2 * i)).collect();
|
||
objs.push(format!("<< /Type /Pages /Kids [{}] /Count {n} /MediaBox [0 0 200 300] >>", kids.join(" ")).into_bytes());
|
||
objs.push(b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>".to_vec());
|
||
for i in 0..n {
|
||
objs.push(format!("<< /Type /Page /Parent 2 0 R /Contents {} 0 R /Resources << /Font << /F1 3 0 R >> >> >>", 5 + 2 * i).into_bytes());
|
||
let body = format!("BT /F1 24 Tf 20 150 Td (Page {}) Tj ET", i + 1);
|
||
objs.push(format!("<< /Length {} >>\nstream\n{body}\nendstream", body.len()).into_bytes());
|
||
}
|
||
let mut out = b"%PDF-1.7\n".to_vec();
|
||
let mut offsets = Vec::new();
|
||
for (i, o) in objs.iter().enumerate() {
|
||
offsets.push(out.len());
|
||
out.extend_from_slice(format!("{} 0 obj\n", i + 1).as_bytes());
|
||
out.extend_from_slice(o);
|
||
out.extend_from_slice(b"\nendobj\n");
|
||
}
|
||
let xref = out.len();
|
||
out.extend_from_slice(format!("xref\n0 {}\n0000000000 65535 f \n", objs.len() + 1).as_bytes());
|
||
for o in offsets {
|
||
out.extend_from_slice(format!("{o:010} 00000 n \n").as_bytes());
|
||
}
|
||
out.extend_from_slice(format!("trailer\n<< /Size {} /Root 1 0 R >>\nstartxref\n{xref}\n%%EOF\n", objs.len() + 1).as_bytes());
|
||
out
|
||
}
|
||
|
||
fn tmp(name: &str) -> std::path::PathBuf {
|
||
let d = std::env::temp_dir().join(format!("pdfcraft-cli-text-{}", std::process::id()));
|
||
std::fs::create_dir_all(&d).unwrap();
|
||
d.join(name)
|
||
}
|
||
|
||
#[test]
|
||
fn text_of_a_selected_page_that_cannot_be_read_fails() {
|
||
let path = tmp("three.pdf");
|
||
std::fs::write(&path, fixture(3)).unwrap();
|
||
let out = Command::new(BIN).args(["text", path.to_str().unwrap(), "--page", "9"]).output().unwrap();
|
||
assert!(!out.status.success(), "a page that cannot be read must not exit 0");
|
||
let stderr = String::from_utf8_lossy(&out.stderr);
|
||
assert!(stderr.contains("page 9"), "the failing page must be named: {stderr}");
|
||
}
|
||
|
||
#[test]
|
||
fn text_of_a_readable_document_succeeds() {
|
||
let path = tmp("ok.pdf");
|
||
std::fs::write(&path, fixture(3)).unwrap();
|
||
let out = Command::new(BIN).args(["text", path.to_str().unwrap()]).output().unwrap();
|
||
assert!(out.status.success());
|
||
let stdout = String::from_utf8_lossy(&out.stdout);
|
||
assert!(stdout.contains("Page 1") && stdout.contains("Page 3"), "{stdout}");
|
||
}
|