Picks up the startup prompt without the glyphs a font may lack (storytold/craft-libs#343, #344): the close mark is painted and the version pill is ASCII, so apps that panic on a missing glyph no longer take the prompt down.
Part of tracking issue storytold/craft-libs#319
Brings in #1374 with upstream main merged (egui 0.37, craft-libs at
storytold/craft-libs e3b9e239).
Conflicts:
- Cargo.toml: print-color's pins (storytold/craft-libs e3b9e239 for
craft-pdf, craft-prepress, krilla and the hayro crates; winit and egui
patches), with craft-prepress keeping the `flatten` feature.
- xtask/src/pdf_deps.rs: print-color's REPO/REV.
- Cargo.lock: print-color's, relocked with cargo metadata; adds only
craft-prepress and linesweeper.
- crates/pdf/src/export.rs: main's optional-content registration and
add_form_fields helper; the helper leaves form fields and media out of
PDF/X-1a with a warning.
- crates/engine/src/cmd/export.rs: the X-1a parameter docs plus main's
`created`; the X-1a flatten and shadow-mask path on top of main's
PDF/A-2b-only rasterize_layered.
Also: x1a_tests builds FlattenTotals through Default, since the struct is
non_exhaustive at e3b9e239.
print_pdf, shared by Export PDF, File › Print and Separations Preview, takes main's two
changes to the inline export pipeline: rasterize_layered runs only for PDF/A-2b (other
standards keep hidden placed-PDF layers as optional content), and the report gets
GRAPHIC_LAYER_RENDERING_WARNING when the source document has graphic layer visibility
overrides and the writer did not report it. The Separations Preview command matches
craft_prepress::Conversion with a fallback arm, since the enum is non-exhaustive at
craft-libs e3b9e239.
PDF/X and CMYK groups keep this branch's approach: krilla's serialize
settings (pdfx::print_settings) write the CMYK page and transparency
groups, the GTS_PDFX output intent and the PDF/X Info and XMP entries.
make_pdfx4 adds only the XMP dates, so main's title argument and its
output-intent, Info and pdfxid XMP edits are dropped, and both
cmyk_group_spaces calls after writer.finish() (Export PDF, Print
Booklet) stay removed. Main's new parts are kept: file_info_metadata
(also for Print Booklet, on top of the print settings), optional content
registration and layer errors, add_form_fields, and pdfx's fonts module.
Main's metadata panic test calls the two-argument make_pdfx4.
craft-libs and the krilla patch are pinned to storytold/craft-libs
e3b9e239; Cargo.lock is main's, relocked without updates.
craft-keymap with the dialog comes from storytold/craft-libs main e3b9e239. egui 0.37's
shaper owns bidi, so the dialog's labels go to it in logical order: rtl::visual_text and the
dialog's visual hook are gone. ForeignSet keeps the dropped-entry count through its builder.
Main's shortcut dispatch (shortcut_key, used for keys forwarded from floating panel windows)
becomes the keymap's: shortcuts and forwarded_shortcuts both take their presses through the
key dispatcher and run them once the input is released. craft-keymap comes from
storytold/craft-libs, ForeignSet is built through its constructor, and the panel shortcut test
asks the docking layout which panel shows.
* UI: the application bar uses craft-window's title bar
DesignCraft's title-bar code moved to craft-libs as craft_window::title_bar.
The application bar calls it instead of its own copy, and the macOS shell
takes sync_movable from it. Differences: a drag on an in-window panel lying
over the bar no longer moves the window, full screen ignores the bar's drag
and double-click, and floating panels in their own windows keep their own
movable flag.
* UI: craft-window from craft-libs main
* UI: the application bar ignores the title bar's response
Every craft-libs crate and the Krilla/Hayro patches use one revision, which
also vendors hayro-postscript. Clipping text paints nothing in the PDF bounds
device; its clip arrives through push_clip_path.
Pin the final egui, winit and shared UI revisions and reconcile current main.
Preserve document-epoch input and screenshot boundaries, host action ordering,
and Windows path regression coverage. Add click/End batch characterization.
Validation: all nine Linux CI stages pass (3230 tests, zero failures); Windows
CI and bounded native editing workflows pass on the same production source.
* PDF: craft-pdf, krilla and hayro from craft-libs main (input bounds and crash fixes)
* PDF: craft-libs 9f75d3dc, with hayro-cmap from its vendored copy
* Dedupe the 400-year-cycle UTC conversion and the EPUB package writer
SonarCloud flagged new-code duplication on main (17%): five copies of the
"move the instant into chrono's range by whole 400-year cycles" conversion
and two copies of the EPUB OPF/container/zip writer.
- designcraft-doc gains a `time` module: `date_parts(days)` and
`utc_parts(secs, nanos)`. App logging, EPUB `iso8601`, PDF `civil`, PPTX
`civil` and text-variable `civil` are now thin callers; each keeps its own
format string and year clamp. The app reaches it through
`designcraft_engine::doc`, so its direct chrono dependency is dropped.
- EPUB: `package_opf` + `write_package` shared by fixed-layout and
reflowable export (same entry order and compression).
- Boundary tests on the helper: pre-1970, year 9999 -> 10000, i64 extremes,
nanos past a second.
Output is unchanged: existing date tests pass, and fixed/reflowable EPUBs
(with/without cover, author, pre-1970 and i64::MAX dates) are byte-identical
to origin/main.
* time: drop the unreachable i128 clamp and match arms in utc_parts
Whole 400-year cycles times 400 stay under 2.6e16 for any i64 day count,
so the year can't overflow i64 and the i128 + saturate was dead code
(carried over from the copies this replaced). The date and time lookups
can't fail either; each keeps one unwrap_or_default instead of a match
with a fake 1970 fallback. Output and tests unchanged.
* Use chrono for UTC/calendar conversion instead of hand-rolled copies
Replaces the hand-rolled civil / civil_from_days / days_from_civil copies (the
146_097 / 719_468 magic numbers) across the logging timestamp, text-variable
dates, EPUB dcterms:modified, PDF metadata dates and PPTX core.xml dates with
chrono, added as a workspace dependency (default-features = false, std only, so
wasm is unaffected).
- apps/designcraft logging `timestamp` (private civil_from_days deleted).
- doc text variables: `civil` and `days_from_civil` use chrono::NaiveDate;
`format_date` and `parse_iso_date` keep their own format/parse logic and
still call these helpers.
- epub `iso8601`, pdf `civil`, pptx `civil`: UTC datetime via
chrono::DateTime::from_timestamp.
Output is byte-identical and pinned by the existing date, ISO-date, EPUB,
image, EPS and logging tests. The OOXML/EPUB dcterms timestamps keep their
exact strings. Each converted helper keeps a proleptic-Gregorian fallback for
dates outside chrono's NaiveDate range (and for the serial/overflow dates
parse_iso_date can hand days_from_civil), so no input changes or panics.
AGENTS.md now says time goes through chrono.
Follow-up to storytold/cadcraft#1785
* Never panic on a clock outside chrono's range; keep 5-digit years unprefixed
logging.rs timestamp(): chrono's From<SystemTime> unwraps, so a clock past
year 262143 panicked inside the logger. Use the fallible DateTime::from_timestamp
and read such a clock, or a pre-epoch clock, as the epoch like the old saturating
code did. Years past 9999 are written with {:04} like before, because %Y adds a
'+'. Tests cover both cases.
epub iso8601(): write the year with {:04} instead of %Y, matching the old
{year:04} output, so years past 9999 are not prefixed with '+'. Test added.
AGENTS.md: the "Time goes through chrono" rule now notes the narrow fallback
for dates outside chrono::NaiveDate's range, which crates/doc/src/vars.rs
retains (Howard Hinnant's algorithm).
* 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.
* Route out-of-range dates through chrono's 400-year cycle, not hand-rolled copies
doc/vars.rs kept hand-rolled Hinnant `civil_fallback`/`days_from_civil_fallback`
copies for dates outside chrono's ±262143-year range, and AGENTS.md carved a
"narrow fallback" exception for them, which left calendar arithmetic outside
chrono after all.
Replace both copies with the Gregorian 400-year cycle (146_097 days): chrono
converts the day/year within one cycle and whole cycles are added back via i128
(so i64::MIN/MAX can't overflow). `civil` is reached by the system clock through
`format_date(now(), ..)`, so it must stay exact; `days_from_civil` only ever
sees a 4-digit parsed year via `parse_iso_date`, but it uses the same cycle form
for consistency. Both reproduce the pre-chrono proleptic Gregorian result, so
outputs are unchanged (verified against the old formula over wide ranges and at
the i64 extremes).
New test pins dates past chrono's range on both sides of the epoch, through the
public `format_date` and round-tripping `days_from_civil`/`civil`, with values
from the old formula. Removed the AGENTS.md "narrow fallback" exception.
epub/pdf/pptx `civil`/`iso8601` are left as-is: the PR already dropped their
hand-rolled copies for pure chrono, and their remaining epoch (`unwrap_or_default`)
fallback only triggers for a metadata/clock timestamp past year 262143 -- the
live-clock/file-mtime case FredApps accepted, with outputs that clamp the year
anyway (pptx 0..9999, PDF DateTime). No hand-rolled copy remains there to replace.
Tested: cargo test -p designcraft-doc; fmt --check; clippy --all-targets -D warnings.
* Carry an overflowing day into the next month again in parse_iso_date
parse_iso_date accepts any day 1..=31, and the old days_from_civil
carried a day past the end of its month into the next one (2019-02-30 is
2019-03-02). from_ymd_opt rejects such a day, and unwrap_or(0) turned it
into 1970-01-01. chrono now places the 1st of the month and the day is
added on, which restores the old normalization. Tested through days_from_civil
and parse_iso_date.
* time: preserve old out-of-range date conversion (no epoch fallback)
`DateTime::from_timestamp(...).unwrap_or_default()` silently reset any
instant past chrono's year range to 1970-01-01, changing output the old
hand-rolled Howard-Hinnant code converted exactly (e.g. 10_000_000_000_000 s
was 318857-05-20 17:46:40, not the epoch). Restore the old conversion domain
with a cycle-based helper that routes the calendar through chrono (the
calendar repeats every 400 years = 146_097 days, so month/day/time come from
chrono and only the year is carried, keeping every i64 exact), then apply each
format's existing year clamp afterwards.
- apps/designcraft/src/logging.rs: `timestamp` via `utc_parts`; keep the
pre-1970 epoch clamp; u64 secs past i64::MAX saturate, not epoch.
- crates/epub/src/lib.rs: `iso8601` via `utc_parts`.
- crates/pdf/src/export.rs: `civil` cycle-based; `date_time` keeps its
0..=9999 year clamp.
- crates/pptx/src/package.rs: `civil` cycle-based, keeps its 0..=9999 year
clamp on the year only (month/day/time keep the real instant).
Pinned old outputs in tests (through the public helpers):
logging u64::MAX/2 s -> 292277026596-12-04T15:30:07.000Z (was epoch)
epub 10_000_000_000_000 -> 318857-05-20T17:46:40Z
-10_000_000_000_000 -> -314918-08-13T06:13:20Z
i64::MAX -> 292277026596-12-04T15:30:07Z
i64::MIN -> -292277022657-01-27T08:29:52Z
pdf civil(10_000_000_000_000) -> (318857,5,20,17,46,40)
pptx civil(10_000_000_000_000) -> (9999,5,20,17,46,40) (year clamped)
* 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.
Repin craft-pdf, craft-prepress, krilla and the hayro crates to d30598d.
flatten_document writes the X-1a file at PDF 1.4 with GTS_PDFXVersion
PDF/X-1:2003 in the Info dictionary and XMP, so DesignCraft's header
rewrite and incremental update are gone. check_pdfx1a refuses DeviceN
NChannel attributes and warns about an ICC v4 output intent profile in a
file of PDF 1.5 or lower; the export names the built-in Coated FOGRA39
(v4) when it is the intent. validate-pdf lists warnings apart from issues.