diff --git a/Cargo.lock b/Cargo.lock index 05c2f7340..c65431ea0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3567,6 +3567,7 @@ dependencies = [ "egui", "fmv-macos-events", "log", + "pdfcraft-engine", "pdfcraft-ui-egui", "pollster 1.0.1", "rustls-native-certs", diff --git a/ROADMAP.md b/ROADMAP.md index 1f774cd56..5eacf8348 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -179,6 +179,7 @@ Ranked; detail and the full milestone table in [docs/roadmap.md](docs/roadmap.md Newest first: the date, what moved, and the new overall estimate. +- **2026-10-12 (M0, build label):** A local or patched build can carry a label, set at compile time with `PDFCRAFT_BUILD_LABEL`: `pdfcraft --version`, `pdfcraft-cli --version`, About and the log show `0.7.1 (titlebar)`, and `packaging/linux/package.sh` names the files `pdfcraft-0.7.1-titlebar-…` and makes the .rpm/.deb release `1.titlebar`, which upgrades over the release of the same version (and the next release over it). The version itself is unchanged, so the update check still compares it with published releases. Unlabelled builds are as before. Estimates unchanged. - 2026-10-11: Organize Pages shows an editable, synchronized physical page-range selection (#1092), with bounded validation and UI/operation regression coverage; overall estimate unchanged. - **2026-10-11 (M5.7, scanned signatures, #1110):** Fill & Sign ▸ Add signature has a Scan tab: a phone photo of a signature on paper is framed automatically (all words; table, sheet edge and dust ignored; a sideways signature turned upright), the frame can be moved, resized or redrawn, the photo turned, and an ink-sensitivity slider picks up faint ink. A morphological paper estimate removes shadows and uneven light, and the trimmed ink is saved and reused like an image signature. Photos and images can also be dropped on the Image and Scan tabs. Photos keep the image-signature limits (#1124: a 4 MiB file, up to 32 megapixels decoded, scaled down to 4 megapixels), so a scan never processes more than 4 megapixels. `fill_sign_add` takes `scan` and `sensitivity`. Synthetic engine, UI and automation tests; checked by hand on real phone photos. Milestone and effort estimates unchanged. diff --git a/apps/pdfcraft-cli/src/main.rs b/apps/pdfcraft-cli/src/main.rs index b33cd894c..fcb1f8feb 100644 --- a/apps/pdfcraft-cli/src/main.rs +++ b/apps/pdfcraft-cli/src/main.rs @@ -141,7 +141,7 @@ fn stdout_line(line: std::fmt::Arguments<'_>) -> Result<(), CliError> { } fn version() -> Result<(), CliError> { - stdout_line(format_args!("pdfcraft-cli {}", env!("CARGO_PKG_VERSION")))?; + stdout_line(format_args!("pdfcraft-cli {}", pdfcraft_engine::build_info::version()))?; stdout_line(format_args!("Discord: {} (help and feedback)", pdfcraft_engine::links::DISCORD))?; stdout_line(format_args!("Web: {}", pdfcraft_engine::links::APP_PAGE))?; stdout_line(format_args!("Source: {}", pdfcraft_engine::links::GITHUB))?; diff --git a/apps/pdfcraft/Cargo.toml b/apps/pdfcraft/Cargo.toml index a0f051c11..4a185d1f9 100644 --- a/apps/pdfcraft/Cargo.toml +++ b/apps/pdfcraft/Cargo.toml @@ -14,6 +14,8 @@ workspace = true ureq = { version = "3.4", default-features = false, features = ["rustls-no-provider", "_ring"] } rustls-native-certs = "0.8" pdfcraft-ui-egui = { workspace = true } +# `build_info`: the version and compile-time build label for `--version` and the log. +pdfcraft-engine = { workspace = true } # wgpu draws; OpenGL (glow) is the fallback when wgpu can't start on an old or unusual GPU or # driver (#461, #435, #392), and can be chosen with PDFCRAFT_RENDERER=gl. eframe = { workspace = true, features = ["default_fonts", "wgpu", "glow", "accesskit", "wayland", "x11", "persistence", "links"] } diff --git a/apps/pdfcraft/src/main.rs b/apps/pdfcraft/src/main.rs index c66d2c68f..bf3c7ed9f 100644 --- a/apps/pdfcraft/src/main.rs +++ b/apps/pdfcraft/src/main.rs @@ -155,7 +155,7 @@ fn main() -> eframe::Result { while let Some(a) = args.next() { match a.as_str() { "--version" => { - println!("pdfcraft {}", env!("CARGO_PKG_VERSION")); + println!("pdfcraft {}", pdfcraft_engine::build_info::version()); return Ok(()); } "--control" => control_file = args.next(), @@ -175,7 +175,7 @@ fn main() -> eframe::Result { // Records logged until now are written to it first. if let (Some(logger), Some(dir)) = (logger, settings_dir()) { match logger.attach_dir(&dir.join("logs")) { - Ok(path) => log::info!("PdfCraft {}, log file {}", env!("CARGO_PKG_VERSION"), path.display()), + Ok(path) => log::info!("PdfCraft {}, log file {}", pdfcraft_engine::build_info::version(), path.display()), // Standard error only by now (`attach_dir` gave up on the file); unlike `eprintln!`, never panics. Err(e) => log::warn!("no log file: {e}"), } diff --git a/crates/engine/src/build_info.rs b/crates/engine/src/build_info.rs new file mode 100644 index 000000000..778922504 --- /dev/null +++ b/crates/engine/src/build_info.rs @@ -0,0 +1,35 @@ +//! What this build calls itself. A local or patched build sets `PDFCRAFT_BUILD_LABEL` at compile +//! time (`PDFCRAFT_BUILD_LABEL=titlebar cargo build`, or through `packaging/linux/package.sh`), so +//! it can be told from the release of the same version. The version itself stays the release's: +//! the update check compares it with published releases. + +/// The release version from the workspace `Cargo.toml`. +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); + +/// The compile-time build label, if any. +pub const LABEL: Option<&str> = option_env!("PDFCRAFT_BUILD_LABEL"); + +/// The version as shown to people: `0.7.1`, or `0.7.1 (titlebar)` for a labelled build. +pub fn version() -> String { + describe(VERSION, LABEL) +} + +fn describe(version: &str, label: Option<&str>) -> String { + match label.map(str::trim).filter(|l| !l.is_empty()) { + Some(label) => format!("{version} ({label})"), + None => version.to_owned(), + } +} + +#[cfg(test)] +mod tests { + use super::describe; + + #[test] + fn a_label_follows_the_version_and_a_blank_one_is_ignored() { + assert_eq!(describe("0.7.1", None), "0.7.1"); + assert_eq!(describe("0.7.1", Some("")), "0.7.1"); + assert_eq!(describe("0.7.1", Some(" ")), "0.7.1"); + assert_eq!(describe("0.7.1", Some("titlebar")), "0.7.1 (titlebar)"); + } +} diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index 76bdc381b..9073115e1 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -14,6 +14,7 @@ pub mod actions; pub mod autotag; +pub mod build_info; pub mod camera; pub mod camera_clean; pub mod camera_edges; diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index 499d0e4f8..9de94341a 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -1098,7 +1098,7 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) { widgets::artcraft_mark(ui, 40.0); ui.vertical(|ui| { ui.label(egui::RichText::new("PdfCraft").font(theme::semibold(20.0))); - ui.label(crate::i18n::fmt(tl!("Version {v}"), &[("v", env!("CARGO_PKG_VERSION"))])); + ui.label(crate::i18n::fmt(tl!("Version {v}"), &[("v", &pdfcraft_engine::build_info::version())])); }); }); ui.add_space(6.0); diff --git a/docs/development.md b/docs/development.md index 634c4bb26..4a05d9e3f 100644 --- a/docs/development.md +++ b/docs/development.md @@ -115,5 +115,6 @@ driver's shader compilation, and explicit `PDFCRAFT_RENDERER=wgpu` still disable | `WGPU_POWER_PREF` | GPU choice; by default PdfCraft draws on the GPU the Wayland compositor draws with (it says so through linux-dmabuf feedback), else the GPU that drives the primary display (Windows) or the built-in panel (Linux), else the low-power (integrated) GPU. The log says which one was chosen | | `WGPU_BACKEND` | Graphics backend; by default Windows uses Direct3D 12, falling back to OpenGL | | `PDFCRAFT_RENDERER` | `gl` starts with OpenGL (glow) and never loads wgpu; `wgpu` reports a wgpu failure instead of retrying with OpenGL; unset, wgpu is retried with OpenGL when it can't start (see [Renderer fallback](#renderer-fallback)) | +| `PDFCRAFT_BUILD_LABEL` | Build time: a label for a local or patched build (letters, digits, inner dots), shown after the version in `--version`, About and the log (`0.7.1 (titlebar)`). `packaging/linux/package.sh` also puts it in the file names and the .rpm/.deb release (`1.titlebar`), which upgrades over the release of the same version | | `CRAFT_FONTS_DIR` | Build time: a [craft-fonts](https://github.com/storytold/craft-fonts) checkout to embed (Japanese fonts) | | `PDFCRAFT_SYSTEM_FONTS` | `0` stops the desktop app from using an installed font for characters its embedded fonts lack (`cargo xtask screenshots` sets it) | diff --git a/packaging/env.sh b/packaging/env.sh index f34f17bd7..1832702eb 100644 --- a/packaging/env.sh +++ b/packaging/env.sh @@ -4,6 +4,8 @@ # Exports: # ROOT workspace root # VERSION [workspace.package] version from Cargo.toml (override: PDFCRAFT_VERSION) +# PDFCRAFT_BUILD_LABEL optional label for a local or patched build (letters, digits, dots); +# compiled into `--version` and About, and appended to the package release # DIST output directory for release artifacts (default: $ROOT/dist/release) # PDFCRAFT_BUILD_SHA git commit, recorded in the macOS Info.plist (PdfCraftBuildCommit) # PDFCRAFT_BUILD_DATE UTC build date, YYYY-MM-DD @@ -28,6 +30,18 @@ if [ -z "$VERSION" ]; then fi export VERSION +# A labelled build says so in `--version` and About (pdfcraft_engine::build_info) and in its +# package release (packaging/linux/nfpm.yaml), so it can't be mistaken for the release. +if [ -n "${PDFCRAFT_BUILD_LABEL:-}" ]; then + case "$PDFCRAFT_BUILD_LABEL" in + *[!A-Za-z0-9.]* | .* | *.) + echo "error: PDFCRAFT_BUILD_LABEL may hold only letters, digits and inner dots: $PDFCRAFT_BUILD_LABEL" >&2 + exit 1 + ;; + esac + export PDFCRAFT_BUILD_LABEL +fi + DIST="${DIST:-$ROOT/dist/release}" mkdir -p "$DIST" export DIST diff --git a/packaging/linux/nfpm.yaml b/packaging/linux/nfpm.yaml index b20bcb672..b1b8a1404 100644 --- a/packaging/linux/nfpm.yaml +++ b/packaging/linux/nfpm.yaml @@ -1,6 +1,6 @@ # .deb and .rpm packages (nfpm: https://nfpm.goreleaser.com). Run by package.sh, which stages # the file tree, substitutes its path for ${STAGE} (nfpm doesn't expand env vars in `src`) and -# exports VERSION and NFPM_ARCH. +# exports VERSION, RELEASE and NFPM_ARCH. # # nfpm package -f packaging/linux/nfpm.yaml -p deb -t dist/release/ name: pdfcraft @@ -9,7 +9,9 @@ platform: linux version: ${VERSION} # semver: 0.2.0-rc.1 becomes 0.2.0~rc.1 in deb/rpm, so pre-releases sort before the release. version_schema: semver -release: 1 +# 1 for releases; 1.