mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-14 19:27:04 -04:00
Rename PrintCraft to PdfCraft (#174)
The app, crates, binaries, bundle id, packaging, icons, screenshots and docs are now PdfCraft / pdfcraft (repo storytold/pdfcraft). On first launch the desktop app moves the PrintCraft settings and crash-recovery folders to the new name, and both apps read settings saved under the old storage key, so upgrades keep recent files, preferences and unsaved work. The signing test fixtures keep their PrintCraft subject names (they are binary). Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
1c78ff479c
commit
d3db99818f
351 files changed
+3218
-3216
No files matched your search
+2
-2
@@ -1,7 +1,7 @@
|
||||
//! `cargo xtask assets`: enforce the asset policy (AGENTS.md §1).
|
||||
//!
|
||||
//! `ATTRIBUTION.toml` lists every asset: committed or vendored files (`[[asset]]`), files that
|
||||
//! Cargo dependencies compile into PrintCraft (`[[bundled]]`), and files xtask downloads at build
|
||||
//! Cargo dependencies compile into PdfCraft (`[[bundled]]`), and files xtask downloads at build
|
||||
//! time (`[[fetched]]`). This gate fails when:
|
||||
//! - an asset-like file in the repository has no entry, or its SHA-256 differs;
|
||||
//! - a licence is not on the allowlist, or a declared licence file is missing;
|
||||
@@ -350,7 +350,7 @@ pub fn render_markdown(m: &Manifest) -> String {
|
||||
let mut s = String::new();
|
||||
s.push_str("# Attribution\n\n");
|
||||
s.push_str("<!-- Generated from ATTRIBUTION.toml by `cargo xtask assets --write`. Do not edit by hand. -->\n\n");
|
||||
s.push_str("Every asset PrintCraft includes, bundles or uses to build its published material, with its author, source and licence. ");
|
||||
s.push_str("Every asset PdfCraft includes, bundles or uses to build its published material, with its author, source and licence. ");
|
||||
s.push_str(
|
||||
"The policy is in [AGENTS.md](AGENTS.md) §1. The machine-readable list, with SHA-256 hashes, is [ATTRIBUTION.toml](ATTRIBUTION.toml). ",
|
||||
);
|
||||
|
||||
@@ -81,7 +81,7 @@ impl Kind {
|
||||
}
|
||||
}
|
||||
|
||||
const AUTHOR: &str = "PrintCraft Reviewer";
|
||||
const AUTHOR: &str = "PdfCraft Reviewer";
|
||||
const AUTHOR_2: &str = "Layout Editor";
|
||||
|
||||
/// Builds annotation dictionaries (and their appearance XObjects) for one document.
|
||||
@@ -119,7 +119,7 @@ impl Annotator {
|
||||
"Subj" => text(subtype),
|
||||
"M" => Object::string_literal(when.pdf.clone()),
|
||||
"CreationDate" => Object::string_literal(when.pdf),
|
||||
"NM" => Object::string_literal(format!("printcraft-showcase-{:03}-{}", self.seq, subtype.to_lowercase())),
|
||||
"NM" => Object::string_literal(format!("pdfcraft-showcase-{:03}-{}", self.seq, subtype.to_lowercase())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ impl Annotator {
|
||||
reply.set("AP", dictionary! { "N" => icon });
|
||||
let reply_id = doc.add_object(reply);
|
||||
|
||||
let mut state = self.base("Text", r, colors::TEAL, AUTHOR, "Accepted set by PrintCraft Reviewer", page);
|
||||
let mut state = self.base("Text", r, colors::TEAL, AUTHOR, "Accepted set by PdfCraft Reviewer", page);
|
||||
state.set("F", 30); // hidden: review-state annotations are shown only in the comments list
|
||||
state.set("Name", "Comment");
|
||||
state.set("IRT", self_id);
|
||||
@@ -463,7 +463,7 @@ impl Annotator {
|
||||
fn stamp(&mut self, doc: &mut Document, page: ObjectId, r: Rect) -> Dictionary {
|
||||
let green = Rgb::hex(0x1d7f4e);
|
||||
let mut dict = self.base("Stamp", r, green, AUTHOR, "Approved for the showcase.", page);
|
||||
dict.set("Name", "PrintCraftApproved");
|
||||
dict.set("Name", "PdfCraftApproved");
|
||||
dict.set("CA", 0.92);
|
||||
let (w, h) = (r.w(), r.h());
|
||||
let mut c = Content::new();
|
||||
@@ -479,7 +479,7 @@ impl Annotator {
|
||||
let size = 22.0;
|
||||
let tw = Font::HelvBold.width(title, size) + 3.0 * (title.len() as f32 - 1.0);
|
||||
c.tracked_text(Font::HelvBold, size, (w - tw) / 2.0, h * 0.42, 3.0, title);
|
||||
let date = format!("PRINTCRAFT QA \u{2022} {}", &self.now.iso[..10]);
|
||||
let date = format!("PDFCRAFT QA \u{2022} {}", &self.now.iso[..10]);
|
||||
let dw = Font::Helv.width(&date, 7.0) + 1.0 * (date.chars().count() as f32 - 1.0);
|
||||
c.tracked_text(Font::Helv, 7.0, (w - dw) / 2.0, h * 0.2, 1.0, &date);
|
||||
let res = self.fonts.resources();
|
||||
@@ -488,7 +488,7 @@ impl Annotator {
|
||||
}
|
||||
|
||||
fn attachment(&mut self, doc: &mut Document, page: ObjectId, r: Rect) -> Dictionary {
|
||||
let note = "Review notes for the PrintCraft showcase.\n\n\
|
||||
let note = "Review notes for the PdfCraft showcase.\n\n\
|
||||
- Page 9 exercises every common annotation type.\n\
|
||||
- Each annotation carries its own appearance stream.\n";
|
||||
let fs = embedded_file(doc, "review-notes.txt", "text/plain", note.as_bytes(), "Notes attached to a comment", &self.now);
|
||||
|
||||
@@ -11,7 +11,7 @@ use std::time::{Duration, Instant};
|
||||
use anyhow::{Context, Result, bail};
|
||||
|
||||
/// Find a Chrome/Chromium binary. `explicit` (from `--chrome`) wins, then the
|
||||
/// `PRINTCRAFT_CHROME` / `CHROME` environment variables, then well-known
|
||||
/// `PDFCRAFT_CHROME` / `CHROME` environment variables, then well-known
|
||||
/// install locations, then `PATH`.
|
||||
pub fn find(explicit: Option<&Path>) -> Result<PathBuf> {
|
||||
if let Some(path) = explicit {
|
||||
@@ -20,7 +20,7 @@ pub fn find(explicit: Option<&Path>) -> Result<PathBuf> {
|
||||
}
|
||||
bail!("--chrome {} does not exist", path.display());
|
||||
}
|
||||
for var in ["PRINTCRAFT_CHROME", "CHROME"] {
|
||||
for var in ["PDFCRAFT_CHROME", "CHROME"] {
|
||||
if let Some(path) = env::var_os(var).map(PathBuf::from)
|
||||
&& path.is_file()
|
||||
{
|
||||
@@ -63,7 +63,7 @@ pub fn find(explicit: Option<&Path>) -> Result<PathBuf> {
|
||||
}
|
||||
bail!(
|
||||
"could not find Google Chrome or Chromium; install one or pass --chrome <path> \
|
||||
(or set PRINTCRAFT_CHROME)"
|
||||
(or set PDFCRAFT_CHROME)"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ pub fn build(doc: &mut Document, annotator: &mut Annotator, inputs: FormInputs<'
|
||||
});
|
||||
|
||||
label(&mut c, LEFT, 510.0, "Comments (multiline)");
|
||||
let comments = "PrintCraft renders this field from its own appearance stream, so it looks \
|
||||
let comments = "PdfCraft renders this field from its own appearance stream, so it looks \
|
||||
the same in every viewer. Edit it to see your viewer regenerate the text.";
|
||||
b.text_field("comments", "Comments", Rect::new(LEFT, 446.0, RIGHT, 506.0), comments, None, 0, |d| {
|
||||
d.set("Ff", 4096);
|
||||
@@ -101,7 +101,7 @@ pub fn build(doc: &mut Document, annotator: &mut Annotator, inputs: FormInputs<'
|
||||
"hello",
|
||||
"Run JavaScript: app.alert",
|
||||
Rect::new(LEFT, 184.0, 250.0, 206.0),
|
||||
"app.alert(\"Hello from the PrintCraft showcase! This alert is a JavaScript action on a push button.\", 3);",
|
||||
"app.alert(\"Hello from the PdfCraft showcase! This alert is a JavaScript action on a push button.\", 3);",
|
||||
);
|
||||
c.fill_color(colors::INK_3).text(Font::Helv, 7.0, LEFT, 172.0, "JavaScript: shows app.alert(...) in viewers that run document scripts.");
|
||||
label(&mut c, COL2, 212.0, "Signature (unsigned /Sig field)");
|
||||
@@ -166,7 +166,7 @@ pub fn build(doc: &mut Document, annotator: &mut Annotator, inputs: FormInputs<'
|
||||
fn draw_page_chrome(c: &mut Content, folio: &str) {
|
||||
c.fill_color(colors::PAPER).rect(0.0, 0.0, PAGE_W, PAGE_H).fill();
|
||||
c.fill_color(colors::INK_3);
|
||||
c.tracked_text(Font::Helv, 6.5, LEFT, 757.0, 1.2, "PRINTCRAFT SHOWCASE");
|
||||
c.tracked_text(Font::Helv, 6.5, LEFT, 757.0, 1.2, "PDFCRAFT SHOWCASE");
|
||||
let right = "10 \u{00b7} INTERACTIVE FORM";
|
||||
let w = Font::Helv.width(right, 6.5) + 1.2 * (right.chars().count() as f32 - 1.0);
|
||||
c.tracked_text(Font::Helv, 6.5, RIGHT - w, 757.0, 1.2, right);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `cargo xtask demo-pdf`: build the PrintCraft showcase PDF.
|
||||
//! `cargo xtask demo-pdf`: build the PdfCraft showcase PDF.
|
||||
//!
|
||||
//! 1. Render a procedural PNG and fill the placeholders in `assets/demo/showcase.html`.
|
||||
//! 2. Print the HTML with headless Chrome (tagged PDF, outline from headings).
|
||||
@@ -36,7 +36,7 @@ pub fn run(args: &[String]) -> Result<()> {
|
||||
}
|
||||
|
||||
let root = repo_root();
|
||||
let out = out_override.unwrap_or_else(|| root.join("dist/demo/printcraft-showcase.pdf"));
|
||||
let out = out_override.unwrap_or_else(|| root.join("dist/demo/pdfcraft-showcase.pdf"));
|
||||
let build = root.join("dist/demo/build");
|
||||
fs::create_dir_all(&build).with_context(|| format!("creating {}", build.display()))?;
|
||||
if let Some(parent) = out.parent() {
|
||||
|
||||
@@ -8,14 +8,14 @@ use super::form::{self, FormInputs};
|
||||
use super::pdf::{Content, Font, Fonts, Stamp, colors, name, text};
|
||||
|
||||
/// Links to this prefix in showcase.html mark where annotations go.
|
||||
const MARKER_PREFIX: &str = "https://mark.printcraft.invalid/";
|
||||
const MARKER_PREFIX: &str = "https://mark.pdfcraft.invalid/";
|
||||
|
||||
const TITLE: &str = "PrintCraft Showcase";
|
||||
const AUTHOR: &str = "PrintCraft";
|
||||
const TITLE: &str = "PdfCraft Showcase";
|
||||
const AUTHOR: &str = "PdfCraft";
|
||||
const SUBJECT: &str = "A specimen document exercising typography, world scripts, vector graphics, \
|
||||
MathML, forms, annotations, layers and attachments.";
|
||||
const KEYWORDS: &[&str] = &["PDF", "typography", "OpenType", "AcroForm", "annotations", "optional content", "PrintCraft"];
|
||||
const CREATOR: &str = "printcraft xtask demo-pdf";
|
||||
const KEYWORDS: &[&str] = &["PDF", "typography", "OpenType", "AcroForm", "annotations", "optional content", "PdfCraft"];
|
||||
const CREATOR: &str = "pdfcraft xtask demo-pdf";
|
||||
|
||||
/// Chrome-printed page indices (0-based) that the post-processor refers to.
|
||||
const PAGE_CONTENTS: usize = 1;
|
||||
@@ -376,7 +376,7 @@ fn decode_text(bytes: &[u8]) -> String {
|
||||
fn tidy_title(title: &str) -> String {
|
||||
const ACRONYMS: &[&str] = &["PDF"];
|
||||
// Chrome drops the <br> between the two lines of the cover title.
|
||||
if title == "PrintCraftShowcase" {
|
||||
if title == "PdfCraftShowcase" {
|
||||
return TITLE.to_string();
|
||||
}
|
||||
let title = title.split_whitespace().collect::<Vec<_>>().join(" ");
|
||||
@@ -477,7 +477,7 @@ fn producer(doc: &Document) -> String {
|
||||
.and_then(Object::as_str)
|
||||
.map(|s| String::from_utf8_lossy(s).into_owned())
|
||||
.unwrap_or_else(|_| "Chrome".into());
|
||||
format!("{chrome}; lopdf 0.45 via printcraft xtask")
|
||||
format!("{chrome}; lopdf 0.45 via pdfcraft xtask")
|
||||
}
|
||||
|
||||
fn info_dict(producer: &str, now: &Stamp) -> Dictionary {
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! Seeds are small PDFs (synthetic ones, plus pdf.js corpus files under 64 KB when `corpus/` is
|
||||
//! present). Each iteration mutates a seed, writes it to `fuzz-out/work/`, and runs
|
||||
//! `printcraft-cli check-one <file> --edit` in a child process with a timeout. That opens,
|
||||
//! `pdfcraft-cli check-one <file> --edit` in a child process with a timeout. That opens,
|
||||
//! inspects, renders and extracts text from every page (the bootstrap renderer), then runs our
|
||||
//! object layer end to end: decode streams, edit, save incrementally and in full, reopen.
|
||||
//!
|
||||
@@ -330,10 +330,10 @@ pub fn run(args: &[String]) -> Result<()> {
|
||||
|
||||
let status = Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
|
||||
.current_dir(root())
|
||||
.args(["build", "--release", "-q", "-p", "printcraft-cli"])
|
||||
.args(["build", "--release", "-q", "-p", "pdfcraft-cli"])
|
||||
.status()?;
|
||||
if !status.success() {
|
||||
bail!("building printcraft-cli failed");
|
||||
bail!("building pdfcraft-cli failed");
|
||||
}
|
||||
let exe = release_cli();
|
||||
if !exe.is_file() {
|
||||
|
||||
+7
-7
@@ -18,9 +18,9 @@ pub fn target_dir() -> PathBuf {
|
||||
std::env::var_os("CARGO_TARGET_DIR").map_or_else(|| root().join("target"), |d| root().join(d))
|
||||
}
|
||||
|
||||
/// The release `printcraft-cli` that `cargo build --release -p printcraft-cli` produced.
|
||||
/// The release `pdfcraft-cli` that `cargo build --release -p pdfcraft-cli` produced.
|
||||
pub fn release_cli() -> PathBuf {
|
||||
target_dir().join("release").join(format!("printcraft-cli{}", std::env::consts::EXE_SUFFIX))
|
||||
target_dir().join("release").join(format!("pdfcraft-cli{}", std::env::consts::EXE_SUFFIX))
|
||||
}
|
||||
|
||||
fn cargo() -> Command {
|
||||
@@ -157,7 +157,7 @@ pub fn corpus(_: &[String]) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `cargo xtask check [--update-baseline]`: run `printcraft-cli check` over the corpus and compare
|
||||
/// `cargo xtask check [--update-baseline]`: run `pdfcraft-cli check` over the corpus and compare
|
||||
/// with `xtask/baselines/<corpus>.json` (the list of files known not to open/render cleanly).
|
||||
/// Fails on any crash, and on any file that regressed from `ok`.
|
||||
pub fn check(args: &[String]) -> anyhow::Result<()> {
|
||||
@@ -166,11 +166,11 @@ pub fn check(args: &[String]) -> anyhow::Result<()> {
|
||||
if !corpus.is_dir() {
|
||||
bail!("corpus missing: run `cargo xtask corpus` first");
|
||||
}
|
||||
run_args(&["build", "--release", "-p", "printcraft-cli"])?;
|
||||
run_args(&["build", "--release", "-p", "pdfcraft-cli"])?;
|
||||
let report = target_dir().join("check-pdfjs.json");
|
||||
let mut c = Command::new(release_cli());
|
||||
c.args(["check"]).arg(&corpus).args(["--timeout", "20", "--dpi", "36", "--json"]).arg(&report);
|
||||
run(c, "printcraft-cli check corpus/pdfjs")?;
|
||||
run(c, "pdfcraft-cli check corpus/pdfjs")?;
|
||||
let results: Vec<serde_json::Value> = serde_json::from_str(&std::fs::read_to_string(&report)?)?;
|
||||
let name =
|
||||
|v: &serde_json::Value| Path::new(v["file"].as_str().unwrap_or("")).file_name().map(|n| n.to_string_lossy().into_owned()).unwrap_or_default();
|
||||
@@ -208,7 +208,7 @@ pub fn check(args: &[String]) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `cargo xtask text-oracle [--limit N]`: compare `printcraft-cli text` with poppler's `pdftotext`
|
||||
/// `cargo xtask text-oracle [--limit N]`: compare `pdfcraft-cli text` with poppler's `pdftotext`
|
||||
/// (external oracle process, never linked) over the corpus. Reports word-level F1 per file and the
|
||||
/// median. Plan target (M2): median ≥ 0.97.
|
||||
pub fn text_oracle(args: &[String]) -> anyhow::Result<()> {
|
||||
@@ -216,7 +216,7 @@ pub fn text_oracle(args: &[String]) -> anyhow::Result<()> {
|
||||
if Command::new("pdftotext").arg("-v").output().is_err() {
|
||||
bail!("pdftotext (poppler) not found; install it to run the text oracle (brew install poppler / apt install poppler-utils)");
|
||||
}
|
||||
run_args(&["build", "--release", "-p", "printcraft-cli"])?;
|
||||
run_args(&["build", "--release", "-p", "pdfcraft-cli"])?;
|
||||
let corpus = root().join("corpus/pdfjs/test/pdfs");
|
||||
let mut files: Vec<PathBuf> =
|
||||
std::fs::read_dir(&corpus)?.flatten().map(|e| e.path()).filter(|p| p.extension().is_some_and(|e| e == "pdf")).collect();
|
||||
|
||||
+24
-24
@@ -27,7 +27,7 @@ impl Class {
|
||||
}
|
||||
}
|
||||
|
||||
/// Every workspace crate (package name without the `printcraft-` prefix) and its layer.
|
||||
/// Every workspace crate (package name without the `pdfcraft-` prefix) and its layer.
|
||||
pub const TABLE: &[(&str, Class)] = &[
|
||||
// L0 foundation (standalone, publishable)
|
||||
("geom", Class::Standalone(0)),
|
||||
@@ -84,7 +84,7 @@ pub const TABLE: &[(&str, Class)] = &[
|
||||
("testkit", Class::Testkit),
|
||||
("oracle", Class::Testkit),
|
||||
// L8 apps + tooling
|
||||
("printcraft", Class::Exempt),
|
||||
("pdfcraft", Class::Exempt),
|
||||
("cli", Class::Exempt),
|
||||
("web", Class::Exempt),
|
||||
("xtask", Class::Exempt),
|
||||
@@ -110,7 +110,7 @@ pub const UI_CRATES: &[&str] = &["egui", "eframe", "winit", "egui_kittest", "egu
|
||||
pub const UI_MIN_LAYER: u8 = 7;
|
||||
|
||||
pub fn short_name(pkg: &str) -> &str {
|
||||
pkg.strip_prefix("printcraft-").unwrap_or(pkg)
|
||||
pkg.strip_prefix("pdfcraft-").unwrap_or(pkg)
|
||||
}
|
||||
|
||||
pub fn classify(pkg: &str) -> Option<Class> {
|
||||
@@ -271,67 +271,67 @@ mod tests {
|
||||
#[test]
|
||||
fn current_graph_shape_passes() {
|
||||
let g = [
|
||||
c("printcraft-geom", &[]),
|
||||
c("printcraft-render", &[("hayro", Normal, false), ("lopdf", Normal, false)]),
|
||||
c("printcraft-engine", &[("printcraft-render", Normal, true)]),
|
||||
c("printcraft-ui-egui", &[("printcraft-engine", Normal, true), ("egui", Normal, false), ("egui_kittest", Dev, false)]),
|
||||
c("printcraft", &[("printcraft-ui-egui", Normal, true)]),
|
||||
c("pdfcraft-geom", &[]),
|
||||
c("pdfcraft-render", &[("hayro", Normal, false), ("lopdf", Normal, false)]),
|
||||
c("pdfcraft-engine", &[("pdfcraft-render", Normal, true)]),
|
||||
c("pdfcraft-ui-egui", &[("pdfcraft-engine", Normal, true), ("egui", Normal, false), ("egui_kittest", Dev, false)]),
|
||||
c("pdfcraft", &[("pdfcraft-ui-egui", Normal, true)]),
|
||||
];
|
||||
assert!(check(&g).is_empty(), "{:?}", check(&g));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upward_and_sideways_edges_flagged() {
|
||||
let v = check(&[c("printcraft-model", &[("printcraft-engine", Normal, true)])]);
|
||||
let v = check(&[c("pdfcraft-model", &[("pdfcraft-engine", Normal, true)])]);
|
||||
assert!(matches!(v[..], [Violation::Upward { from: 2, to: 6, .. }]));
|
||||
// forms -> js must go through the ActionRunner trait, never directly.
|
||||
let v = check(&[c("printcraft-forms", &[("printcraft-js", Normal, true)])]);
|
||||
let v = check(&[c("pdfcraft-forms", &[("pdfcraft-js", Normal, true)])]);
|
||||
assert!(matches!(v[..], [Violation::Upward { from: 3, to: 3, .. }]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn listed_sideways_edges_allowed() {
|
||||
assert!(check(&[c("printcraft-redact", &[("printcraft-edit", Normal, true)])]).is_empty());
|
||||
assert!(!check(&[c("printcraft-edit", &[("printcraft-redact", Normal, true)])]).is_empty());
|
||||
assert!(check(&[c("pdfcraft-redact", &[("pdfcraft-edit", Normal, true)])]).is_empty());
|
||||
assert!(!check(&[c("pdfcraft-edit", &[("pdfcraft-redact", Normal, true)])]).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cos_may_use_only_its_listed_foundation() {
|
||||
assert!(check(&[c("printcraft-cos", &[("printcraft-filters", Normal, true), ("printcraft-crypt", Normal, true)])]).is_empty());
|
||||
let v = check(&[c("printcraft-cos", &[("printcraft-arlington", Normal, true)])]);
|
||||
assert!(check(&[c("pdfcraft-cos", &[("pdfcraft-filters", Normal, true), ("pdfcraft-crypt", Normal, true)])]).is_empty());
|
||||
let v = check(&[c("pdfcraft-cos", &[("pdfcraft-arlington", Normal, true)])]);
|
||||
assert!(matches!(v[..], [Violation::StandaloneHasWorkspaceDep { .. }]));
|
||||
let v = check(&[c("printcraft-filters", &[("printcraft-geom", Normal, true)])]);
|
||||
let v = check(&[c("pdfcraft-filters", &[("pdfcraft-geom", Normal, true)])]);
|
||||
assert!(matches!(v[..], [Violation::StandaloneHasWorkspaceDep { .. }]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ui_crates_below_l7_flagged() {
|
||||
for dep in ["egui", "eframe", "winit", "rfd", "wgpu", "wgpu-core", "egui_dock"] {
|
||||
let v = check(&[c("printcraft-engine", &[(dep, Normal, false)])]);
|
||||
let v = check(&[c("pdfcraft-engine", &[(dep, Normal, false)])]);
|
||||
assert!(matches!(v[..], [Violation::UiBelowL7 { layer: 6, .. }]), "{dep}");
|
||||
}
|
||||
assert!(check(&[c("printcraft-platform", &[("winit", Normal, false)])]).is_empty());
|
||||
assert!(check(&[c("pdfcraft-platform", &[("winit", Normal, false)])]).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_support_only_as_dev_dependency() {
|
||||
let v = check(&[c("printcraft-cos", &[("printcraft-testkit", Normal, true)])]);
|
||||
let v = check(&[c("pdfcraft-cos", &[("pdfcraft-testkit", Normal, true)])]);
|
||||
assert!(!v.is_empty());
|
||||
assert!(check(&[c("printcraft-render", &[("printcraft-oracle", Dev, true)])]).is_empty());
|
||||
let v = check(&[c("printcraft-render", &[("printcraft-oracle", Normal, true)])]);
|
||||
assert!(check(&[c("pdfcraft-render", &[("pdfcraft-oracle", Dev, true)])]).is_empty());
|
||||
let v = check(&[c("pdfcraft-render", &[("pdfcraft-oracle", Normal, true)])]);
|
||||
assert!(matches!(v[..], [Violation::TestkitAsNormalDep { .. }]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unregistered_crate_is_error() {
|
||||
let v = check(&[c("printcraft-mystery", &[])]);
|
||||
assert!(matches!(&v[..], [Violation::Unregistered { krate }] if krate == "printcraft-mystery"));
|
||||
let v = check(&[c("pdfcraft-mystery", &[])]);
|
||||
assert!(matches!(&v[..], [Violation::Unregistered { krate }] if krate == "pdfcraft-mystery"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apps_exempt() {
|
||||
for app in ["printcraft", "printcraft-cli", "printcraft-web", "xtask"] {
|
||||
assert!(check(&[c(app, &[("egui", Normal, false), ("printcraft-ui-egui", Normal, true)])]).is_empty());
|
||||
for app in ["pdfcraft", "pdfcraft-cli", "pdfcraft-web", "xtask"] {
|
||||
assert!(check(&[c(app, &[("egui", Normal, false), ("pdfcraft-ui-egui", Normal, true)])]).is_empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
//! Project automation for PrintCraft, run via `cargo xtask <command>`.
|
||||
//! Project automation for PdfCraft, run via `cargo xtask <command>`.
|
||||
|
||||
use std::process::ExitCode;
|
||||
|
||||
@@ -22,13 +22,13 @@ const COMMANDS: &[(&str, &str, Command)] = &[
|
||||
("ci", "fmt --check, clippy -D warnings, test, layers, wasm, assets, deny, parity (stops at first failure)", gates::ci),
|
||||
("assets", "Enforce the asset policy (AGENTS.md §1) against ATTRIBUTION.toml; --write regenerates ATTRIBUTION.md", assets::run),
|
||||
("corpus", "Fetch test corpora into corpus/ (git-ignored): pdf.js test PDFs", gates::corpus),
|
||||
("check", "Robustness sweep over corpus/ with printcraft-cli; fails on crashes or regressions vs xtask/baselines", gates::check),
|
||||
("check", "Robustness sweep over corpus/ with pdfcraft-cli; fails on crashes or regressions vs xtask/baselines", gates::check),
|
||||
("fuzz", "Mutation fuzzing of open/render/edit/save in child processes; findings in fuzz-out/ (--time 300)", fuzz::run),
|
||||
("parity", "Validate parity/acrobat-features.toml against the registry, tools and tests; report progress (--json, --partial)", parity::run),
|
||||
("text-oracle", "Compare text extraction with pdftotext over corpus/ (word F1; target median ≥ 0.97)", gates::text_oracle),
|
||||
("screenshots", "Regenerate the README screenshots in docs/images/ and their ATTRIBUTION entries", screenshots::run),
|
||||
("models", "Fetch the OCR models (ATTRIBUTION.toml kind = \"model\") into assets/models/, verified by SHA-256", assets::models),
|
||||
("demo-pdf", "Build dist/demo/printcraft-showcase.pdf (needs Google Chrome or Chromium)", demo_pdf::run),
|
||||
("demo-pdf", "Build dist/demo/pdfcraft-showcase.pdf (needs Google Chrome or Chromium)", demo_pdf::run),
|
||||
];
|
||||
|
||||
fn version_cmd(args: &[String]) -> anyhow::Result<()> {
|
||||
|
||||
+19
-24
@@ -16,37 +16,32 @@ use anyhow::{Context, Result, bail};
|
||||
|
||||
/// (file stem, title, what it shows, shot arguments after the PDF path).
|
||||
pub const SCENES: &[(&str, &str, &str, &[&str])] = &[
|
||||
("printcraft-viewer", "Viewer with comments", "README hero", &["--panel", "comments", "--zoom", "72"]),
|
||||
("pdfcraft-viewer", "Viewer with comments", "README hero", &["--panel", "comments", "--zoom", "72"]),
|
||||
("pdfcraft-dark", "Dark theme", "README: themes", &["--theme", "dark", "--panel", "comments", "--page", "11", "--zoom", "80", "--notice", "off"]),
|
||||
("pdfcraft-organize", "Organize pages", "README: organize", &["--organize", "on", "--panel", "none", "--select", "5,6,7"]),
|
||||
("pdfcraft-find", "Find text", "README: search", &["--find", "type", "--page", "4", "--zoom", "90", "--panel", "none", "--notice", "off"]),
|
||||
("pdfcraft-bookmarks", "Bookmarks", "README: navigation", &["--panel", "bookmarks", "--page", "7", "--zoom", "75", "--notice", "off"]),
|
||||
("pdfcraft-forms", "Interactive form", "README: forms", &["--page", "13", "--fields", "on", "--zoom", "80", "--panel", "fields"]),
|
||||
("pdfcraft-layers", "Layers", "README: layers", &["--panel", "layers", "--page", "12", "--zoom", "75", "--notice", "off"]),
|
||||
(
|
||||
"printcraft-dark",
|
||||
"Dark theme",
|
||||
"README: themes",
|
||||
&["--theme", "dark", "--panel", "comments", "--page", "11", "--zoom", "80", "--notice", "off"],
|
||||
),
|
||||
("printcraft-organize", "Organize pages", "README: organize", &["--organize", "on", "--panel", "none", "--select", "5,6,7"]),
|
||||
("printcraft-find", "Find text", "README: search", &["--find", "type", "--page", "4", "--zoom", "90", "--panel", "none", "--notice", "off"]),
|
||||
("printcraft-bookmarks", "Bookmarks", "README: navigation", &["--panel", "bookmarks", "--page", "7", "--zoom", "75", "--notice", "off"]),
|
||||
("printcraft-forms", "Interactive form", "README: forms", &["--page", "13", "--fields", "on", "--zoom", "80", "--panel", "fields"]),
|
||||
("printcraft-layers", "Layers", "README: layers", &["--panel", "layers", "--page", "12", "--zoom", "75", "--notice", "off"]),
|
||||
(
|
||||
"printcraft-scripts",
|
||||
"pdfcraft-scripts",
|
||||
"Scripts of the world",
|
||||
"README: rendering",
|
||||
&["--page", "7", "--zoom", "125", "--left", "closed", "--panel", "none", "--notice", "off"],
|
||||
),
|
||||
("printcraft-properties", "Document properties", "README: metadata", &["--dialog", "properties", "--notice", "off"]),
|
||||
("printcraft-palette", "Command palette", "README: tools", &["--palette", "page", "--notice", "off"]),
|
||||
("printcraft-split", "Split dialog", "README: split", &["--organize", "on", "--panel", "none", "--dialog", "split", "--notice", "off"]),
|
||||
("printcraft-tools", "All tools", "README: tool catalogue", &["--tools", "expanded", "--home", "on"]),
|
||||
("pdfcraft-properties", "Document properties", "README: metadata", &["--dialog", "properties", "--notice", "off"]),
|
||||
("pdfcraft-palette", "Command palette", "README: tools", &["--palette", "page", "--notice", "off"]),
|
||||
("pdfcraft-split", "Split dialog", "README: split", &["--organize", "on", "--panel", "none", "--dialog", "split", "--notice", "off"]),
|
||||
("pdfcraft-tools", "All tools", "README: tool catalogue", &["--tools", "expanded", "--home", "on"]),
|
||||
(
|
||||
"printcraft-twoup",
|
||||
"pdfcraft-twoup",
|
||||
"Two-up reading",
|
||||
"README: layouts",
|
||||
&["--layout", "two-up", "--mode", "read", "--page", "4", "--theme", "dark", "--notice", "off"],
|
||||
),
|
||||
];
|
||||
|
||||
const PDF: &str = "dist/demo/printcraft-showcase.pdf";
|
||||
const PDF: &str = "dist/demo/pdfcraft-showcase.pdf";
|
||||
|
||||
pub fn run(args: &[String]) -> Result<()> {
|
||||
let root = crate::assets::root();
|
||||
@@ -55,7 +50,7 @@ pub fn run(args: &[String]) -> Result<()> {
|
||||
crate::demo_pdf::run(&[])?;
|
||||
}
|
||||
let status = Command::new(env!("CARGO"))
|
||||
.args(["build", "--release", "-p", "printcraft-ui-egui", "--example", "shot"])
|
||||
.args(["build", "--release", "-p", "pdfcraft-ui-egui", "--example", "shot"])
|
||||
.current_dir(&root)
|
||||
.status()
|
||||
.context("building the shot example")?;
|
||||
@@ -108,18 +103,18 @@ fn update_attribution(root: &Path) -> Result<()> {
|
||||
for file in images.iter().filter(|f| f.ends_with(".png")) {
|
||||
let stem = file.trim_end_matches(".png");
|
||||
let scene = SCENES.iter().find(|(n, ..)| *n == stem);
|
||||
let (title, usage) = scene.map(|(_, t, u, _)| (*t, *u)).unwrap_or(("PrintCraft screenshot", "README"));
|
||||
let (title, usage) = scene.map(|(_, t, u, _)| (*t, *u)).unwrap_or(("PdfCraft screenshot", "README"));
|
||||
// The home screen shows the ArtCraft mark: say so (it is a Storyteller trademark, not open
|
||||
// source; AGENTS.md §1.2), so the screenshot's licence is not read as covering it.
|
||||
let shows_home = scene.is_some_and(|(.., args)| args.windows(2).any(|w| w[0] == "--home" && w[1] == "on"));
|
||||
let contents = if shows_home {
|
||||
"Shows PrintCraft UI, Lucide icons, the showcase PDF built from the [[fetched]] OFL fonts, and the ArtCraft mark (Storyteller trademark from docs/brand/, not covered by this licence)"
|
||||
"Shows PdfCraft UI, Lucide icons, the showcase PDF built from the [[fetched]] OFL fonts, and the ArtCraft mark (Storyteller trademark from docs/brand/, not covered by this licence)"
|
||||
} else {
|
||||
"Shows only PrintCraft UI, Lucide icons and the showcase PDF built from the [[fetched]] OFL fonts"
|
||||
"Shows only PdfCraft UI, Lucide icons and the showcase PDF built from the [[fetched]] OFL fonts"
|
||||
};
|
||||
let bytes = std::fs::read(root.join("docs/images").join(file))?;
|
||||
out.push_str(&format!(
|
||||
"\n[[asset]]\npath = \"docs/images/{file}\"\ntitle = \"PrintCraft screenshot: {title}\"\nauthor = \"PrintCraft contributors (generated by `cargo xtask screenshots`)\"\nsource = \"this repository\"\nlicence = \"MIT OR Apache-2.0\"\nlicence_file = \"LICENSE-MIT\"\nkind = \"image\"\nusage = \"{usage}. {contents}\"\nsha256 = \"{}\"\n",
|
||||
"\n[[asset]]\npath = \"docs/images/{file}\"\ntitle = \"PdfCraft screenshot: {title}\"\nauthor = \"PdfCraft contributors (generated by `cargo xtask screenshots`)\"\nsource = \"this repository\"\nlicence = \"MIT OR Apache-2.0\"\nlicence_file = \"LICENSE-MIT\"\nkind = \"image\"\nusage = \"{usage}. {contents}\"\nsha256 = \"{}\"\n",
|
||||
crate::assets::sha256_hex(&bytes)
|
||||
));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user