mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-14 22:42:46 +00:00
* feat(ps): add craft-ps with an EPS reader (read feature) The reading half of craft-ps (#115): a clean-room PostScript interpreter that runs an EPS and returns what it paints as a one-page vector PDF of its bounding box, colours kept, so apps can draw and export EPS as a placed PDF. Moved from DesignCraft (storytold/designcraft, where it was written for placed EPS), with its app-specific names made neutral. Written from the PostScript Language Reference (3rd ed.), the EPS specification, the Type 1 Font Format and Adobe TN 5012/5176/5177. Covers the language, paths, clipping, strokepath, device/Separation/ DeviceN/Indexed/ICCBased/Lab colour, images with lazily decoded filters, shadings 1-7 and patterns, and Type 1/3/42/CFF/CID text through CMaps, horizontal and vertical. Bounded: each operator charges work proportional to its operands against the step limit as it goes, every byte read or decoded counts, copies count against the memory cap; defaults 50 M steps / 1 GiB, and 20 M / 256 MiB on wasm32. No panics (deny lints, fuzzed in debug builds). 34 tests, the Type 42 one on a TrueType font built in the test. No new external crates: pdf-writer, miniz_oxide, weezl, skrifa and kurbo are already locked at these versions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * clippy(ps): document stateful decoder complexity * fix(ps): bound huge arc angle normalization * fix(ps): preserve EffectCraft EPS text and limits parity * fix(ps): address interpreter review findings * fix(ps): preserve composite glyph paint order * fix(ps): address interpreter review findings * fix(ps): bound filter buffers and preserve file aliases * fix(ps): make predictor allocation fallible --------- Co-authored-by: markus.doebele <markus.doebele@top21.sh> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
42 lines
1.5 KiB
Rust
42 lines
1.5 KiB
Rust
//! `cargo run -p craft-ps --features read --example eps2pdf -- in.eps out.pdf`: convert one EPS.
|
|
|
|
fn main() -> std::process::ExitCode {
|
|
let args: Vec<String> = std::env::args().skip(1).collect();
|
|
let (Some(input), Some(output)) = (args.first(), args.get(1)) else {
|
|
eprintln!("usage: eps2pdf in.eps out.pdf");
|
|
return std::process::ExitCode::FAILURE;
|
|
};
|
|
let bytes = match std::fs::read(input) {
|
|
Ok(b) => b,
|
|
Err(e) => {
|
|
eprintln!("{input}: {e}");
|
|
return std::process::ExitCode::FAILURE;
|
|
}
|
|
};
|
|
let t = std::time::Instant::now();
|
|
match craft_ps::read::eps_to_pdf(&bytes, craft_ps::read::Limits::default()) {
|
|
Ok(c) => {
|
|
eprintln!(
|
|
"{input}: {:.0} ms, bbox {:?}, {} warnings, {} steps, {} KiB",
|
|
t.elapsed().as_secs_f64() * 1000.0,
|
|
c.bbox,
|
|
c.warnings.len(),
|
|
c.steps,
|
|
c.memory / 1024
|
|
);
|
|
for w in c.warnings.iter().take(20) {
|
|
eprintln!(" {w}");
|
|
}
|
|
if let Err(e) = std::fs::write(output, &c.pdf) {
|
|
eprintln!("{output}: {e}");
|
|
return std::process::ExitCode::FAILURE;
|
|
}
|
|
std::process::ExitCode::SUCCESS
|
|
}
|
|
Err(e) => {
|
|
eprintln!("{input}: {e} ({:.0} ms)", t.elapsed().as_secs_f64() * 1000.0);
|
|
std::process::ExitCode::FAILURE
|
|
}
|
|
}
|
|
}
|