mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-24 10:08:00 -06:00
* Use chrono for UTC/calendar conversion instead of hand-rolled copies Replaces the hand-rolled civil_from_days/days_from_civil copies and the leap-year/month math around them (log timestamp, PDF /Info dates, ASN.1 UTCTime/GeneralizedTime, FormCalc Date/Num2Date, the Combine modified-time stamp, the form date picker, and the demo-PDF timestamps) with chrono as a workspace dependency. The engine's existing chrono (clock) is lifted to the workspace. Every output format is unchanged and pinned by tests. CLAUDE.md now says time goes through chrono. Follow-up to storytold/cadcraft#1785 * Guard chrono date paths: no panic on overflow, no '+' on 5-digit years Systemic chrono follow-ups to the UTC/calendar PR: - logging::timestamp and combine_ui::utc_stamp dropped chrono's From<SystemTime> (which unwraps internally and panics on a clock past year 262143) for the fallible DateTime::from_timestamp, reading an out-of-range clock as the epoch, as the old clamping code did. - timestamp, utc_stamp, pdf_date and the xtask Stamp now write the year with {:04} instead of %Y, which prefixes a '+' before years past 9999; the old hand-rolled code wrote the plain zero-padded year. - forms_ui calendar navigation and length, and formcalc days_in, use checked_add for the December year successor so y + 1 can't overflow at i32::MAX / i64::MAX (reachable from a parsed or persisted date). Docstrings added to the chrono-touched civil_from_days and Time::from_unix for the docstring-coverage gate. Tests cover the out-of-range-epoch and 10000-01-01 cases for each. * Don't build an unrepresentable SystemTime in the far-future test UNIX_EPOCH + Duration::from_secs(u64::MAX / 2) overflows Windows' SystemTime and panicked before timestamp() ran, failing the Windows test job. Build it with checked_add and only assert where it exists. * Keep FormCalc dates exact past chrono's range MAX_DAYS accepts day numbers up to 1e8 (years past 270000), but NaiveDate stops near ±262143, so Num2Date(99000000) and Format fell back to 1900-01-01. days_from_civil, civil_from_days and days_in now have chrono convert within the date's 400-year Gregorian cycle and add whole cycles back (via i128), which gives the pre-chrono results for every year. Tests cover accepted inputs past chrono's range on both sides of the epoch, through Num2Date and Format, with values from the old formula. * Bump rustls-webpki and webpki-root-certs for RUSTSEC-2026-0382/0383 rustls-webpki 0.103.15 -> 0.103.16 and webpki-root-certs 1.0.9 -> 1.0.10 (both semver-compatible, lockfile only) drop the distrusted emSign intermediates and root. Only these two entries change; cargo deny check advisories passes. Upstream main fails the same check. * logging/cos/sign/combine/demo: keep extended-year dates, no epoch fallback For consistency with the stricter no-epoch-fallback rule now agreed across the sibling chrono PRs (wordcraft#3427, filmcraft#2662): `DateTime::from_timestamp(..).unwrap_or_default()` changes a timestamp the old code converted past chrono's year 262143 range — it collapses to 1970-01-01 instead of the real proleptic-Gregorian date. The FormCalc Num2Date path was already fixed (FredApps approved); this applies the same fix to every remaining conversion. Each site uses the 400-year Gregorian cycle (146_097 days) with chrono as the only calendar logic: chrono converts the day within one cycle, whole cycles carry in the year (i128, saturated into i64). PDF/ASN.1 format-specific year handling is kept exactly as the old code had it. Verified the cycle result equals the old formula across a wide range and at the extremes. - apps/pdfcraft/src/logging.rs `timestamp`: cycle `civil_from_days(u64)`; pre-1970 epoch clamp and ms/time-of-day format kept. Pins: +10_000_000_000_000 s -> 318857-05-20T17:46:40.000Z (was 1970); checked_add u64::MAX/2 s -> 292277026596-12-04T15:30:07.000Z (was epoch). - crates/cos/src/writer.rs `pdf_date` (i64): cycle converter, no year clamp (old code wrote `y` as-is, so a negative year keeps its sign). Pins: pdf_date(10_000_000_000_000)="D:3188570520174640Z", pdf_date(-10_000_000_000_000)="D:-3149180813061320Z". - crates/sign/src/der.rs `Time::from_unix` (i64): cycle converter; the `year` field is a u32, so the year is cast `as u32` (wrapping) exactly as the old code did — ASN.1 year width preserved byte-for-byte. Pins: from_unix(10e12).year=318857, from_unix(-10e12).year=4_294_652_378, from_unix(i64::MAX).year=219_250_468, from_unix(i64::MIN).year=4_075_720_767. - crates/ui-egui/src/combine_ui.rs `utc_stamp` (u64): cycle converter; pre-1970 epoch clamp kept. Pins: utc_stamp(10e12)="318857-05-20 17:46 UTC", u64::MAX/2 s="292277026596-12-04 15:30 UTC". - xtask/src/demo_pdf/pdf.rs `Stamp::from_unix` (i64): cycle converter, no year clamp. Pins: from_unix(10e12).iso="318857-05-20T17:46:40Z"/.pdf="D:3188570520174640Z", from_unix(-10e12).iso="-314918-08-13T06:13:20Z". cos/sign/xtask built, tested (pdf_dates_are_utc_and_byte_identical, der::tests::times, stamps_round_trip), fmt and clippy clean. pdfcraft-ui-egui and the pdfcraft app can't link here (unrelated LLVM intrinsic bug in the rten-gemm dependency, 1.98.1), so their logging and combine_ui functions + tests were copied verbatim into a scratch crate with chrono 0.4.45 and pass there; fmt --check clean on all five packages. CI builds these on all platforms. * Don't build a year-318857 SystemTime unchecked in the logging test Windows' SystemTime only reaches about year 30828, so UNIX_EPOCH + Duration::from_secs(10_000_000_000_000) panicked before timestamp() ran and failed the Windows test job. Build it with checked_add and assert where the platform can hold it. The year-10000 case fits everywhere and stays unconditional. * Name the 400-year range extension as the one exception to the time rule The rule said never to hand-roll calendar math, but the range fix (shifting by whole 400-year Gregorian cycles so dates past chrono's year range keep their old output) is cycle arithmetic, so the rule and the code disagreed. The rule now names that exception: the library still computes month, day and time, and only whole cycles are carried in the year. * Say the library computes month and day; time-of-day is plain arithmetic Narrows the range-extension wording: the date library computes the calendar fields, and splitting seconds into hours, minutes and seconds is ordinary arithmetic, which is what the code does.
122 lines
5.2 KiB
TOML
122 lines
5.2 KiB
TOML
[workspace]
|
|
resolver = "3"
|
|
members = ["crates/*", "apps/*", "xtask"]
|
|
exclude = ["vendor"]
|
|
default-members = ["crates/*", "apps/pdfcraft", "apps/pdfcraft-cli", "xtask"]
|
|
|
|
[workspace.package]
|
|
version = "0.11.0"
|
|
edition = "2024"
|
|
license = "MIT OR Apache-2.0"
|
|
rust-version = "1.95"
|
|
repository = "https://github.com/storytold/pdfcraft"
|
|
|
|
[workspace.dependencies]
|
|
# Shared frontend components; engines remain independent of egui.
|
|
craft-ui = { git = "https://github.com/storytold/craft-libs", version = "0.1.0", rev = "5c874d51eeb3de70e735c8ce87a59a7dedd47675", features = ["serde"] }
|
|
# Quit the macOS way (#1136): ⌘Q asks about unsaved changes, and closing quits through `terminate:`.
|
|
craft-quit = { git = "https://github.com/storytold/craft-libs", version = "0.1.0", rev = "f58bc1a3df490992d633393d0898af0588ce882c" }
|
|
# Shared system spooler; document composition remains in pdfcraft-print.
|
|
craft-print = { git = "https://github.com/storytold/craft-libs", version = "0.1.0", rev = "666c358065261a2816b376fa3bb10538dbf53040" }
|
|
# Whether a path stored in a document may be opened (GoToR link targets).
|
|
craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "82a24b006eefa036b43c09a67fb9a3fbd17ffe63" }
|
|
# internal
|
|
pdfcraft-geom = { path = "crates/geom", version = "0.11.0" }
|
|
pdfcraft-filters = { path = "crates/filters", version = "0.11.0" }
|
|
pdfcraft-cos = { path = "crates/cos", version = "0.11.0" }
|
|
pdfcraft-crypt = { path = "crates/crypt", version = "0.11.0" }
|
|
pdfcraft-organize = { path = "crates/organize", version = "0.11.0" }
|
|
pdfcraft-measure = { path = "crates/measure", version = "0.11.0" }
|
|
pdfcraft-annot = { path = "crates/annot", version = "0.11.0" }
|
|
pdfcraft-audit = { path = "crates/audit", version = "0.11.0" }
|
|
pdfcraft-fonts = { path = "crates/fonts", version = "0.11.0" }
|
|
pdfcraft-model = { path = "crates/model", version = "0.11.0" }
|
|
pdfcraft-content = { path = "crates/content", version = "0.11.0" }
|
|
pdfcraft-redact = { path = "crates/redact", version = "0.11.0" }
|
|
pdfcraft-print = { path = "crates/print", version = "0.11.0" }
|
|
pdfcraft-xfdf = { path = "crates/xfdf", version = "0.11.0" }
|
|
pdfcraft-sign = { path = "crates/sign", version = "0.11.0" }
|
|
pdfcraft-optimize = { path = "crates/optimize", version = "0.11.0" }
|
|
pdfcraft-a11y = { path = "crates/a11y", version = "0.11.0" }
|
|
pdfcraft-oracle = { path = "crates/oracle", version = "0.11.0" }
|
|
pdfcraft-testkit = { path = "crates/testkit", version = "0.11.0" }
|
|
pdfcraft-ocr = { path = "crates/ocr", version = "0.11.0" }
|
|
pdfcraft-js = { path = "crates/js", version = "0.11.0" }
|
|
pdfcraft-compare = { path = "crates/compare", version = "0.11.0" }
|
|
pdfcraft-preflight = { path = "crates/preflight", version = "0.11.0" }
|
|
pdfcraft-export = { path = "crates/export", version = "0.11.0" }
|
|
pdfcraft-edit = { path = "crates/edit", version = "0.11.0" }
|
|
pdfcraft-create = { path = "crates/create", version = "0.11.0" }
|
|
pdfcraft-forms = { path = "crates/forms", version = "0.11.0" }
|
|
pdfcraft-xfa = { path = "crates/xfa", version = "0.11.0" }
|
|
pdfcraft-render = { path = "crates/render", version = "0.11.0" }
|
|
pdfcraft-engine = { path = "crates/engine", version = "0.11.0" }
|
|
pdfcraft-platform = { path = "crates/platform", version = "0.11.0" }
|
|
pdfcraft-ui-egui = { path = "crates/ui-egui", version = "0.11.0" }
|
|
pdfcraft-automation = { path = "crates/automation", version = "0.11.0", default-features = false }
|
|
|
|
# pdf
|
|
hayro = "0.7.1"
|
|
hayro-syntax = "0.7.2"
|
|
lopdf = "0.45"
|
|
flate2 = { version = "1.1", default-features = false, features = ["rust_backend"] }
|
|
weezl = "0.2"
|
|
|
|
# foundation
|
|
thiserror = "2"
|
|
serde = { version = "1", features = ["derive", "rc"] }
|
|
serde_json = "1"
|
|
log = "0.4"
|
|
kurbo = "0.13"
|
|
# All date/time conversion goes through chrono (no hand-rolled calendar code beyond the 400-year range extension; AGENTS.md).
|
|
chrono = { version = "0.4.45", default-features = false, features = ["std"] }
|
|
|
|
# testing
|
|
proptest = "1"
|
|
|
|
# ui
|
|
egui = "0.36"
|
|
egui_extras = { version = "0.36", default-features = false, features = ["svg"] }
|
|
eframe = { version = "0.36", default-features = false }
|
|
egui_kittest = "0.36"
|
|
rfd = "0.17"
|
|
|
|
[workspace.lints.rust]
|
|
unsafe_code = "forbid"
|
|
|
|
[workspace.lints.clippy]
|
|
dbg_macro = "warn"
|
|
todo = "warn"
|
|
|
|
# `"*"` matches every non-workspace package: crates.io dependencies and the
|
|
# vendored crates under `vendor/` (excluded from the workspace). Our own crates
|
|
# keep full debug info. Dependencies keep only line tables, so backtraces still
|
|
# show file and line, but debug info for the whole dependency graph stays out of
|
|
# every test binary (each UI test binary had a 500-600 MB PDB on Windows, #454).
|
|
[profile.dev.package."*"]
|
|
opt-level = 2
|
|
debug = "line-tables-only"
|
|
|
|
[profile.dev]
|
|
opt-level = 1
|
|
|
|
[profile.low-disk]
|
|
inherits = "dev"
|
|
debug = "line-tables-only"
|
|
incremental = false
|
|
|
|
[profile.release]
|
|
lto = "thin"
|
|
codegen-units = 1
|
|
|
|
# Vendored, patched third-party crates (see vendor/README.md). Each patch is meant to go upstream.
|
|
[patch.crates-io]
|
|
hayro-interpret = { path = "vendor/hayro-interpret" }
|
|
hayro = { path = "vendor/hayro" }
|
|
hayro-syntax = { path = "vendor/hayro-syntax" }
|
|
hayro-jbig2 = { path = "vendor/hayro-jbig2" }
|
|
lopdf = { path = "vendor/lopdf" }
|
|
winit = { path = "vendor/winit" }
|
|
egui-wgpu = { path = "vendor/egui-wgpu" }
|
|
vello_common = { path = "vendor/vello_common" }
|