Files
pdfcraft/apps/pdfcraft-cli/tests/render.rs
T
505c4a2fa3 Issue #248: pdfcraft-cli render writes the format its --out name says (.png, .jpg, .tif or .pam) (#255)
`render --out p1.png` wrote a netpbm PAM stream (P7) whatever the name,
so a `.png` could not be opened by any PNG reader. The CLI already links
the engine, whose export module encodes PNG, JPEG and TIFF, so the output
format now follows the extension: `.png`, `.jpg`/`.jpeg` (quality 90),
`.tif`/`.tiff`, and `.pam` for the raw stream as before. Any other name
is refused up front, before the page is rendered, with the accepted list.

`export::encode_image` wraps the three encoders behind `ImageFormat` so
the CLI and `Exporter::image` share one match.

Tests (apps/pdfcraft-cli/tests/render.rs): a `.png` gets the PNG
signature and an IHDR with the rendered size; `.JPG` and `.tif` get
JPEG/TIFF magic; `.pam` still gets the P7 header and the right number of
bytes; `.bmp` fails with the message and writes nothing.

Co-authored-by: wstock <1394687+wstock@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-08 07:25:43 -04:00

80 lines
3.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! `render --out` writes the file format its name says (#248): a `.png` is a PNG, not a netpbm
//! PAM stream with a `.png` extension.
use std::path::PathBuf;
use std::process::{Command, Output};
const BIN: &str = env!("CARGO_BIN_EXE_pdfcraft-cli");
fn tmp(test: &str, name: &str) -> PathBuf {
let d = std::env::temp_dir().join(format!("pdfcraft-cli-render-{test}-{}", std::process::id()));
std::fs::create_dir_all(&d).unwrap();
d.join(name)
}
/// A one-page PDF with some text, as `--out` would be given it.
fn fixture(test: &str) -> PathBuf {
let pdf = tmp(test, "in.pdf");
let bytes = pdfcraft_engine::Session::new().create_from_text("Render test", "Hello from PdfCraft").unwrap();
std::fs::write(&pdf, bytes.as_slice()).unwrap();
pdf
}
fn render(pdf: &std::path::Path, out: &std::path::Path) -> Output {
Command::new(BIN).args(["render", pdf.to_str().unwrap(), "--page", "1", "--dpi", "36", "--out", out.to_str().unwrap()]).output().unwrap()
}
fn be32(b: &[u8]) -> u32 {
u32::from_be_bytes([b[0], b[1], b[2], b[3]])
}
#[test]
fn a_png_name_gets_a_png() {
let pdf = fixture("png");
let out = tmp("png", "p1.png");
let r = render(&pdf, &out);
assert!(r.status.success(), "{}", String::from_utf8_lossy(&r.stderr));
let bytes = std::fs::read(&out).unwrap();
assert_eq!(&bytes[..8], b"\x89PNG\r\n\x1a\n", "not a PNG: {:?}", &bytes[..bytes.len().min(16)]);
// IHDR: the page is 612×792 pt, rendered at 36 dpi
assert_eq!(&bytes[12..16], b"IHDR");
assert_eq!((be32(&bytes[16..20]), be32(&bytes[20..24])), (306, 396));
// the reported size matches the file
let stderr = String::from_utf8_lossy(&r.stderr);
assert!(stderr.contains("306×396"), "{stderr}");
}
#[test]
fn jpeg_and_tiff_names_get_those_formats() {
let pdf = fixture("jpg");
let jpg = tmp("jpg", "p1.JPG");
assert!(render(&pdf, &jpg).status.success());
assert_eq!(&std::fs::read(&jpg).unwrap()[..2], b"\xff\xd8");
let tif = tmp("jpg", "p1.tif");
assert!(render(&pdf, &tif).status.success());
let bytes = std::fs::read(&tif).unwrap();
assert!(bytes.starts_with(b"II*\0") || bytes.starts_with(b"MM\0*"), "not a TIFF: {:?}", &bytes[..4]);
}
#[test]
fn a_pam_name_still_gets_the_netpbm_stream() {
let pdf = fixture("pam");
let out = tmp("pam", "p1.pam");
assert!(render(&pdf, &out).status.success());
let bytes = std::fs::read(&out).unwrap();
let header = b"P7\nWIDTH 306\nHEIGHT 396\nDEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n";
assert!(bytes.starts_with(header), "{:?}", String::from_utf8_lossy(&bytes[..bytes.len().min(80)]));
assert_eq!(bytes.len(), header.len() + 306 * 396 * 4);
}
#[test]
fn an_unknown_extension_fails_and_writes_nothing() {
let pdf = fixture("bmp");
let out = tmp("bmp", "p1.bmp");
let r = render(&pdf, &out);
assert!(!r.status.success());
let stderr = String::from_utf8_lossy(&r.stderr);
assert!(stderr.contains(".png, .jpg, .tif or .pam"), "{stderr}");
assert!(!out.exists());
}