refactor(pdf): use shared writer for export and print (PDF.W1)

This commit is contained in:
skylar taylor-barrick committed 2026-10-13 20:41:14 -06:00
1 parent ba5447bbd9
commit a699a26221
24 files changed
+235 -54

No files matched your search

+5 -1
View File
@@ -9,6 +9,7 @@ mod brands;
mod bundle;
mod ico;
mod layers;
mod pdf_deps;
mod pngsuite;
mod sha256;
mod stats;
@@ -24,9 +25,10 @@ usage: cargo xtask <command>
commands:
assets check that every icon/image/font/asset is attributed in ASSETS.md
brands check that user-visible text (labels, params docs, MCP, docs, packaging) names no other vendor's products
pdf-deps verify immutable shared PDF writer and patched Krilla sources
layers enforce the crate dependency layering (plan/architecture.md §3)
wasm cargo check --target wasm32-unknown-unknown for the wasm-safe crates
ci fmt --check, clippy -D warnings, test, assets, brands, layers, wasm (stops at first failure)
ci fmt --check, clippy -D warnings, test, pdf-deps, assets, brands, layers, wasm (stops at first failure)
corpus [--download] [--affinity]
show where test corpora live; --download fetches PngSuite into corpus/pngsuite,
--affinity the pinned public Affinity documents into corpus/affinity
@@ -45,6 +47,7 @@ fn main() -> ExitCode {
Some("assets") => assets::run(&root()),
Some("brands") => brands::run(&root()),
Some("layers") => cmd_layers(),
Some("pdf-deps") => pdf_deps::run(&root()),
Some("wasm") => cmd_wasm(),
Some("ci") => cmd_ci(),
Some("corpus") if rest.contains(&"--affinity") => affinity_corpus::fetch(),
@@ -200,6 +203,7 @@ fn cmd_ci() -> Result<(), String> {
run(c, "cargo test --workspace")
}),
),
("pdf-deps", Box::new(|| pdf_deps::run(&root()))),
("assets", Box::new(|| assets::run(&root()))),
("brands", Box::new(|| brands::run(&root()))),
("layers", Box::new(cmd_layers)),
+98
View File
@@ -0,0 +1,98 @@
//! Verify that PDF writing uses one immutable shared crate and its patched writer backend.
use std::path::Path;
use std::process::Command;
use serde_json::Value;
const REV: &str = "c3487320b2d1de657b860b3c8abb44e1f2b32bd8";
const PACKAGES: &[(&str, &str)] = &[("craft-pdf", "0.1.0"), ("krilla", "0.8.2")];
pub fn run(root: &Path) -> Result<(), String> {
let output = Command::new(std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into()))
.current_dir(root)
.args(["metadata", "--locked", "--format-version", "1"])
.output()
.map_err(|e| format!("cargo metadata: {e}"))?;
if !output.status.success() {
return Err(format!("cargo metadata: {}", String::from_utf8_lossy(&output.stderr)));
}
let metadata: Value = serde_json::from_slice(&output.stdout).map_err(|e| format!("cargo metadata: {e}"))?;
validate(&metadata)?;
println!("PDF writer: shared crate and patched backend resolve once to {REV}");
Ok(())
}
fn validate(metadata: &Value) -> Result<(), String> {
let packages = metadata.get("packages").and_then(Value::as_array).ok_or("cargo metadata has no packages")?;
let source = format!("git+https://github.com/storytold/craft-libs?rev={REV}#{REV}");
for &(name, version) in PACKAGES {
let mut found = packages.iter().filter(|p| p.get("name").and_then(Value::as_str) == Some(name));
let package = found.next().ok_or_else(|| format!("{name}: missing resolved package"))?;
if found.next().is_some() {
return Err(format!("{name}: expected one resolved package"));
}
if package.get("version").and_then(Value::as_str) != Some(version) || package.get("source").and_then(Value::as_str) != Some(source.as_str()) {
return Err(format!("{name}: expected {version} from {source}; got {package}"));
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
fn metadata() -> Value {
let packages: Vec<_> = PACKAGES
.iter()
.map(|(name, version)| {
json!({
"name":name, "version":version,
"source":format!("git+https://github.com/storytold/craft-libs?rev={REV}#{REV}")
})
})
.collect();
json!({"packages": packages})
}
#[test]
fn accepts_the_pinned_writer_without_changing_reader_sources() {
let mut m = metadata();
m["packages"]
.as_array_mut()
.unwrap()
.push(json!({"name":"hayro-syntax", "version":"0.7.2", "source":"registry+https://github.com/rust-lang/crates.io-index"}));
assert!(validate(&m).is_ok());
}
#[test]
fn rejects_missing_or_duplicate_writer_packages() {
for index in 0..PACKAGES.len() {
let mut m = metadata();
m["packages"].as_array_mut().unwrap().remove(index);
assert!(validate(&m).is_err());
let mut m = metadata();
let duplicate = m["packages"][index].clone();
m["packages"].as_array_mut().unwrap().push(duplicate);
assert!(validate(&m).is_err());
}
}
#[test]
fn rejects_registry_local_version_and_revision_drift() {
for index in 0..PACKAGES.len() {
for (field, value) in [
("source", Value::Null),
("source", json!("registry+https://github.com/rust-lang/crates.io-index")),
("source", json!("git+https://github.com/storytold/craft-libs?branch=main#other")),
("version", json!("0.99.0")),
] {
let mut m = metadata();
m["packages"][index][field] = value;
assert!(validate(&m).is_err());
}
}
}
}