Commit Graph
1540 Commits
Author SHA1 Message Date
Dylan M. Taylor c0df5be07d Reuse verified objects in directory bundles 2026-11-13 02:26:51 -05:00
Dylan M. Taylor 6b8e79294d Expose bounded pcraft object codecs 2026-11-13 02:23:08 -05:00
Dylan M. Taylor de087a4524 Expose bounded directory bundle entries 2026-11-13 02:23:08 -05:00
Dylan M. Taylor b0284aad09 Expose bounded pcraft entry reads 2026-11-13 02:23:08 -05:00
Dylan M. Taylor fd5133174b Add shared pcraft bundle storage 2026-11-13 02:23:08 -05:00
Dylan M. Taylor 47107f6601 craft-mesh: retain glTF texture and sampler inventory (#1151)
* craft-mesh: expose unreferenced glTF texture variants

* mesh: validate texture inventory image indices

* Fix PR 1151 validation and workspace checks

* Fix remaining Rust 1.95 workspace lint findings

* Fix proof workspace Clippy warning

* Fix remaining workspace Clippy findings

* Fix video all-features Clippy warning

* KRA: parse nested layers without recursion

* Preserve KRA layer limit traversal order

* Avoid expect in iterative KRA parser

* OpenRaster: parse nested stacks without recursion

* Plate tests honor optional font fixtures

* Keep bundled font index bytes unchanged

* Preserve font build license fixture bytes

* ci: allow longer workspace test matrix

* test: respect Windows Hunspell path behavior

* ci: provision keymap fonts for all-feature tests

* ci: provision PSD corpus for full workspace tests
2026-11-13 02:17:30 -05:00
Dylan M. Taylor 72d544b707 Merge pull request #1153 from storytold/fix/psd-head-view
psd: expose bounded borrowed header resources
2026-11-13 02:15:50 -05:00
Dylan M. Taylor e2cab3f651 Merge pull request #1158 from storytold/feat/stencilcraft-format
Add versioned flattened JSON format schema
2026-11-13 02:13:29 -05:00
Dylan M. Taylor c5d1e937ae feat(materialx): add bounded reader and OpenPBR writer (#1167)
* feat(materialx): add bounded document reader and OpenPBR writer

* proof, video, latex, cms: satisfy the clippy lints that ship with Rust 1.100

The newer clippy flags needless bool guards and range loops whose index
only indexes one slice; express both with the equivalent iterators so
the workspace lint stays green on every platform.

* cms: index the ink loops instead of a range loop, which newer clippy rejects

* feat(materialx): resolve shader graphs and write OpenPBR materials

* feat(materialx): resolve shader graphs and write OpenPBR materials
2026-11-13 02:03:31 -05:00
Dylan M. Taylor 63196e66f3 Merge pull request #1157 from dylanmtaylor/codex/craft-metafile-hatch-percent
metafile: render GDI+ percentage hatch brushes
2026-11-13 02:03:14 -05:00
Dylan M. Taylor 79923702ff Merge remote-tracking branch 'origin/main' into fix/psd-head-view 2026-11-13 06:06:44 +00:00
Dylan M. Taylor 21d7c1267e metafile: draw GDI+ percentage hatch styles 2026-11-13 06:05:42 +00:00
Dylan M. Taylor 07a4cd0fc7 #778: preserve consumer VP8 frame limits 2026-11-13 06:05:39 +00:00
Dylan M. Taylor 93d75f44d7 Merge remote-tracking branch 'origin/main' into feat/stencilcraft-format 2026-11-13 06:05:00 +00:00
Dylan M. Taylor 4b8dde4c12 test(stencilcraft-format): exercise reader scalar root variants 2026-11-13 05:52:28 +00:00
Dylan M. Taylor eb379cf3d2 test(stencilcraft-format): cover scalar JSON roots 2026-11-13 05:51:08 +00:00
Dylan M. Taylor 7c3a099ad8 Read native format JSON from streams 2026-11-13 05:21:58 +00:00
Dylan M. Taylor 5407ea8220 Document required kerf field in native format example 2026-11-13 00:04:29 -05:00
Dylan M. Taylor 4c25109473 test(openpgp): align signature time with generated key 2026-11-13 03:47:36 +00:00
Dylan M. Taylor 7316e4e223 fix: clear workspace baseline gates on Rust 1.100 2026-11-13 03:13:53 +00:00
Nicolas Bohn 5056c13ef7 fix(dxf): split binary hex payloads into chunks of at most 127 bytes (#1147) (#1148) 2026-11-12 21:49:37 -05:00
Dylan M. Taylor 1b53814a38 Apply verified workspace format fixes 2026-11-13 02:37:09 +00:00
Dylan M. Taylor 7a639e04a7 Fix workspace clippy loop in LaTeX tests 2026-11-13 02:35:01 +00:00
Dylan M. Taylor d31b04810b Fix current workspace clippy diagnostics 2026-11-13 02:28:06 +00:00
Dylan M. Taylor c7fb7544c2 craft-stencilcraft-format: add native JSON schema 2026-11-13 02:23:04 +00:00
Dylan M. Taylor 61d9de7846 psd: bound resource iterator size hint 2026-11-13 00:21:00 +00:00
Dylan M. Taylor cc9c380543 Fix PSD resource iterator size hint 2026-11-13 00:05:37 +00:00
Dylan M. Taylor 341c84d1b1 Add borrowed PSD head resource views 2026-11-13 00:03:19 +00:00
Dylan M. TaylorandDylan Taylor f92a91afa4 plotter: add TB54 registration-mark offset command (#1139)
* plotter: add TB54 registration-mark offset command with py_silhouette citation

The full Silhouette registration sequence (TB99 through TB55, plus
TB123/TB124 for search and TB23 for manual positioning) is documented
in the community driver py_silhouette (zero_on_registration_mark()),
which reverse-engineered the protocol from public behaviour — not from
any proprietary binary.

TB54,0,0 sets the registration-mark offset to zero (mark at expected
position). Without it the machine may use a non-zero default offset.

* workspace: allow unused_workspace_dependencies lint

The root Cargo.toml declares many workspace dependencies that are not
referenced by any crate's Cargo.toml (e.g. craft-aep, craft-ai-core,
craft-cms, etc.). This is intentional — they are available for apps to
use. Suppress the clippy warning so CI passes.

* workspace: allow unused_workspace_dependencies (cargo lint)

This is a cargo-level lint (not clippy) that fires when workspace
dependencies declared in [workspace.dependencies] are not referenced
by any crate's Cargo.toml. Many entries are intentionally available
for apps to use.

* cms, prepress, proof: allow unused_workspace_dependencies lint

These crates are built individually by CI (cargo clippy -p <crate>)
which does not inherit workspace lints. Add per-crate suppression.

* cms, prepress, proof: inherit workspace lints

Add [lints] workspace = true so these crates pick up the
unused_workspace_dependencies suppression from the root Cargo.toml.

* revert: remove duplicate [lints] sections

The cms, prepress, and proof crates already had [lints] workspace = true.
My previous commits accidentally added duplicate sections.

* cargo: suppress unused_workspace_dependencies lint via config

This is a cargo-level lint (not clippy) that fires when workspace
dependencies declared in [workspace.dependencies] are not referenced
by any crate's Cargo.toml. Many entries are intentionally available
for apps to use.

---------

Co-authored-by: Dylan Taylor <dylanmtaylor@gmail.com>
2026-11-12 15:44:47 -05:00
.CooCoo d23f5f36bb craft-drawingml: outline digests print a near-zero coordinate without its sign (#1127)
outlines_match_the_string_dispatch failed on macOS for curvedRightArrow
while Linux and Windows passed. The digest prints each coordinate with
{:.3}, which writes a residue such as -1.4e-14 from the sine or cosine of
a right angle as "-0.000". The sign of that residue depends on the
platform's libm, so the digest did too.

digest_coord now maps every coordinate that prints as zero to 0.0 before
it is printed. The digests of pieWedge, curvedRightArrow and
curvedDownArrow, the only names whose outlines held such residues, are
taken again; no outline changes.
2026-11-12 13:45:57 -06:00
Dylan M. Taylor e98299546f crs: a repeated name keeps its first value, as craft-metadata reads it
craft-metadata #1042 keeps the first text-bearing value of a repeated
property so a blank repeat cannot hide a location, but craft-crs's doc
comment and test still claimed the last repeat wins. The workspace-wide
test job has been red since it landed because of that assertion.
2026-11-12 11:26:10 -05:00
nonplessesdsnrffffandBen Mathews 65cf3f9b0d craft-web-host: accept PDF page attachments from the host
* craft-web-host: accept PDF page attachments from the host

---------

Co-authored-by: Ben Mathews <ben@themathewsgroup.com>
2026-11-12 10:43:32 -05:00
Ken MantleandKenM76 f3b643d081 prepress: separate a page in bands, past the 4 MP plate cap
* prepress: separate a page in bands, past the 4 MP plate cap

`plates_in_bands` finds a page's inks once, then separates it in row
bands of at most MAX_PLATE_PIXELS each, rendering every band through
craft-pdf raster's size/offset viewport. A page can now be separated at
print resolution with bounded memory: only the renderer's 8192-pixel
side limit and a 256 MP per-plate total (MAX_BANDED_PIXELS) lower the
resolution. `plates` is unchanged in behaviour: it is the one-band case
of the same code.

Tests: bands of 7 rows join byte for byte into the whole-page render of
an anti-aliased disc under an overprinting spot (fails when the band
offset is dropped), and a 40 pt page at 3700 dpi separates at full
resolution in bands under the cap (fails when banding uses the plate
cap's scale).

Refs #838.

* prepress: bands pass the renderer's whole-page limits

The page's pixel size came from `raster::dimensions` on the whole page,
which enforces the renderer's 64 MP and 8192-pixel side limits, so a
Letter page past about 830 dpi failed with OutputLimit instead of
banding. The size is now computed as the renderer floors it, and only
each band's viewport reaches the renderer. Bands are also capped at
MAX_SIDE rows, and ink discovery renders within the renderer's limits.

A Letter page at 900 dpi now separates in 19 bands at full resolution;
across the page the side limit still lowers it, near 960 dpi.

Test: a 10 x 2000 pt strip at 600 dpi (16 666 rows) separates in three
bands with its black square on the right rows. It fails with either the
whole-page dimensions call or the uncapped band height restored.

* prepress: check the page before discovery; say what the banded cap bounds

The page-size check now runs before ink discovery renders anything.
MAX_BANDED_PIXELS bounds pixels, not the number of bands; the doc
comment and the README caps table say so.

* prepress: count a sub-pixel side as one pixel in the banded cap

A side scaled under one pixel still renders one, so a very thin, tall
page could pass MAX_BANDED_PIXELS by far. banded_scale now fits the
other side alone in that case, and separate() refuses any floored size
over the cap before it renders a band.

* prepress: keep the banded area scale under the cap after rounding

The area scale came from the exact cap, so f32 rounding could floor a
page one row past it (17 x 15 058 824 pt at 72 dpi), which then failed
with OutputLimit. The same 0.1% margin as the one-pixel branches now
applies to the area scale too, with a test on that page.

---------

Co-authored-by: KenM76 <KenM76>
2026-11-12 10:41:24 -05:00
aionggandaiongg 270db68c1e craft-cms: run the CMS suite in CI
* craft-cms: run the CMS suite in CI; say when system-profile checks are skipped

* craft-cms: satisfy clippy's chunks_exact_to_as_chunks lint in the total-ink test

---------

Co-authored-by: aiongg <aiongtaigi@gmail.com>
2026-11-12 10:38:37 -05:00
Zach Shallbetter b6cbd59671 craft-atomic: eliminate runtime panic on wasm32-unknown-unknown (closes #81)
* craft-atomic: eliminate runtime panic on wasm32-unknown-unknown (closes #81)

---------

Co-authored-by: Zach Shallbetter <zachshallbetter@icloud.com>
2026-11-12 10:38:30 -05:00
Hamza NaveedandHamza 75af6f589a PDF interpret: a glyph shown in a clipping mode clips even with no outline
* PDF interpret: a glyph shown in a clipping mode clips even with no outline

A glyph shown in a clipping render mode (`4 Tr` to `7 Tr`) adds its outline to
the clipping path, and that clip applies to everything painted after `ET` until
the enclosing `Q`. When the glyphs shown have no outline -- a space, say -- the
clip they contribute is empty, and an empty clip admits nothing.

`interpret_window` applied a clip only once a glyph had contributed an outline,
so such a text object applied *no* clip and what followed painted unmasked.

A text object that shows no glyph at all is a different case and is left alone:
the clipping path is unchanged there, as readers leave it, and a glyph whose
outline is unavailable (Type 3) leaves the real clip unknown, so both stay
unclipped. pdf.js draws the same line in `endText`: it builds the clip from the
glyphs shown, empty outlines and all, and applies none when none were shown.

Measured on a page filled red behind the text object, Preview (CoreGraphics)
and Firefox (pdf.js) agreeing on the case this changes:

    a space shown in 7 Tr    before: the fill paints   after: nothing paints
    no glyph shown in 7 Tr   unchanged: the fill paints

Companion to storytold/pdfcraft#3451.

The first push of this branch also carried 803 build-artifact files from
`crates/pdf/read-only-contract/target/`, which `.gitignore` did not cover: its
`/target` is anchored to the repository root, so a sub-crate's own build
directory is not ignored. Recommitted without them, and `target/` added so the
next one cannot slip in either (#185 review).

* pdf: test visible glyphs in a clipping mode, and what each clip leaves on the page

---------

Co-authored-by: Hamza <mail.hamzanaveed@gmail.com>
2026-11-12 10:38:02 -05:00
638ee75115 Paint dialog tabs that join their page
* Paint dialog tabs that join their page

* Discard texture updates explicitly in headless rendering tests

* Cover centered baselines below selected dialog tabs

* Tests: use Id::unique, as main's egui requires

---------

Co-authored-by: Fluorine <joseph.anglada92@gmail.com>
Co-authored-by: Joseph Anglada Muñiz <janglada@nd-soft.com>
2026-11-12 10:37:55 -05:00
b903f08e7b Share bounded frosted backdrop composition
* Share bounded frosted backdrop composition

* Scope upload assertions to the backdrop texture

* Discard texture updates explicitly in headless rendering tests

* Correct texture ID borrowing in upload-count regression

* Bound texture sides before queuing backdrop uploads

---------

Co-authored-by: Fluorine <joseph.anglada92@gmail.com>
Co-authored-by: Joseph Anglada Muñiz <janglada@nd-soft.com>
2026-11-12 10:37:46 -05:00
6026ad570f Share shape gallery choices and procedural previews
* Share shape gallery choices and procedural previews

* Clip shape previews to their supplied rectangles

* Discard texture updates explicitly in headless rendering tests

* Honor fill colors across complete star and heart contours

* Make heart contour collection type explicit

* Use fixed-size triangle chunks in fill regression

---------

Co-authored-by: Fluorine <joseph.anglada92@gmail.com>
Co-authored-by: Joseph Anglada Muñiz <janglada@nd-soft.com>
2026-11-12 10:37:36 -05:00
aionggandaiongg e56b2f1220 craft-window: frame policy v2: the app's bar is the title bar by default
* craft-window: frame policy v2: integrated title bar by default, desktop and backend detection

Add the policy module: Backend, Desktop::detect, TitleBarPref, Os, Frame
{Native, Inset, Integrated}, pure decide, decide_for and apply. Caption
layouts come from GNOME gsettings, kwinrc and GTK settings.ini parsers.
Choice::probe, the root Frame and decorate keep their behaviour.

* craft-window: review fixes for the frame policy

Desktop gains icon_theme, Decision gains desktop, Backend::from_env,
GNOME layout falls back to settings.ini, compositor socket variables mark
tiling, GTK_THEME wins over settings.ini, layout is read only for an
integrated frame, Layout::mirrored.

* craft-window: CRAFT_TITLEBAR applies when the app's own variable is unset

---------

Co-authored-by: aiongg <aiongtaigi@gmail.com>
2026-11-12 10:37:27 -05:00
aionggandaiongg bcf1e69cf4 craft-window: restore store: window.json beside config.toml and a pure place()
* craft-window: restore store: window.json beside config.toml and a pure place()

* craft-window: restore: review fixes (bar strip, mixed DPI, pending writes, work area)

---------

Co-authored-by: aiongg <aiongtaigi@gmail.com>
2026-11-12 10:37:20 -05:00
aionggandaiongg 1329b6bf04 platform: GNOME's light setting reads as light, and Watcher::wait_first for the first frame
* platform: GNOME's light setting reads as light, and Watcher::wait_first for the first frame

GNOME 42-46 writes color-scheme 'default' for Settings > Appearance > Light and the portal
reports 0. Both now decode as Light on GNOME (XDG_CURRENT_DESKTOP, read once at start);
0 stays no preference on other desktops.

Watcher::wait_first(timeout) blocks until the first answer, a known-absent portal with an
empty fallback, a no-preference answer, worker exit or drop, or the timeout.

* platform: GNOME's 'default' is light only on a GNOME desktop

---------

Co-authored-by: aiongg <aiongtaigi@gmail.com>
2026-11-12 10:37:13 -05:00
VoiceLessQandVoiceLessQ 5afeda8327 craft-proof it: a ‘ quote can open the next sentence, as “ and « do
In "Vado. ‘poi’ torno." the capital-letter check skipped poi, because ends_sentence didn't count ‘ among the characters that may open the next sentence. This is the last part of storytold/wordcraft#3675; the rest came with #484.

---------

Co-authored-by: VoiceLessQ <t12kaem@gmail.com>
2026-11-12 10:37:00 -05:00
Hamza NaveedandHamza 9b7b748fbb krilla: TrueType subsets keep the font's OS/2 table (embedding permissions)
* krilla: TrueType subsets keep the font's OS/2 table (embedding permissions)

The subsetter writes only the tables it subsets, so a FontFile2 program
had no OS/2 table and the font's embedding permissions (OS/2.fsType)
never reached the PDF: a Preview & Print or Restricted font looked
unrestricted to anything reading the file later (designcraft#1781).

Patch 18 adds the source font's OS/2 table, byte for byte, to a
TrueType subset that lacks one, rewriting the table directory with its
checksums and head.checkSumAdjustment. CFF programs are still embedded
bare. Ledger, patch file and SOURCE.json updated; craft-pdf's new
font_tables test checks the table, its fsType and the font's checksums.

* krilla: the OS/2 test checks the embedded program is a subset

Its sfnt and re-read checks also pass for the source font embedded whole. A subset has fewer glyphs than the fixture's 11, and a shorter glyf.

---------

Co-authored-by: Hamza <mail.hamzanaveed@gmail.com>
2026-11-12 10:36:53 -05:00
Marc 5a37e3e383 update: offer Save and Update in the prompt when the app can save (#586)
* update: offer Save and Update in the prompt when the app can save (#586)

With unsaved work the prompt only said to save or close first. An app can now pass can_save to show_with_save: the button then reads Save and Update, a click asks the app to save through take_save_request, and the install starts once nothing is unsaved. save_stopped reports a save that did not get there. show and show_if are unchanged, so other apps see no difference.

* update: test that a second click during the save leaves the prompt open

The saving prompt test only read the painted text. This one clicks where Save and Update was, where a Dismiss would catch the second click of a double click.

* update: Save and Update follows the ready update (#586)

A ready update whose close was canceled now offers Save and Update in place of Restart to Update while the app can save. Once the save ends with nothing unsaved, the app is asked to close in that same frame, once. Tests cover both prompts and the close order.

---------

Co-authored-by: Marc <184940552+marcwgls@users.noreply.github.com>
2026-11-12 10:36:47 -05:00
.CooCoo 596694db4d font-store: name four Language pack tests by the behavior they check
Drop the residual_ prefix from four tests in language_packs/tests.rs.
The two cleanup tests become committed_cleanup_*, matching the
committed removal receipts they exercise. Test bodies are unchanged.

---------

Co-authored-by: dotCooCoo <RobertLeeLW@gmail.com>
2026-11-12 10:10:35 -05:00
.CooCoo 3debdb13fc prepress: one wide curve no longer slows region cleanup for every other curve
* prepress: one wide curve no longer slows region cleanup for every other curve

The flattener's region cleanup looks up the input curves whose bounds hold
both ends of an output piece. The lookup searched back from a point by the
width of the widest input curve, so one curve as wide as the page made every
lookup scan nearly every input curve, and the cleanup grew quadratically with
the number of curves.

The curves are now grouped by bounds width, each group within a factor of two,
and each group is searched within its own widest bounds. The lookup returns
the same curves in the same order as before, so the regions are unchanged.

* prepress: region cleanup keeps a curve held just inside its right edge

Far from the origin, `x - width` can round above the left edge of a curve
whose bounds hold `x`, and the width-group search then skipped that curve.
The search's left edge is now rounded outward.

---------

Co-authored-by: dotCooCoo <RobertLeeLW@gmail.com>
2026-11-12 10:08:20 -05:00
.CooCoo 0c07636bbf Fonts build: the non-UTF-8 checkout test passes on macOS
macOS returns EILSEQ when a file name is not UTF-8, so the test panicked while creating its checkout. The test now ends at that step on macOS for that error. Any other error, and the same error on another platform, still fails the test.

---------

Co-authored-by: dotCooCoo <RobertLeeLW@gmail.com>
2026-11-12 10:06:20 -05:00
Helen Kwok 2bfc45c493 web-api: refuse calls beyond an in-flight limit, and say who can call window.<app>
* web-api: refuse calls beyond an in-flight limit, and say who can call window.<app>

At most 64 calls (PageApi::with_max_in_flight) may wait for the app at once. A call made while that many wait rejects at once with a busy error and never reaches the app, so a page script that calls in a loop without awaiting cannot grow the app queue without bound, as the desktop control server already answers busy when its queue is full. The README also says the API is a property of the page global with no key: any script in the page origin has the same command control as the desktop control server.

* web-api: turn a busy call away before converting its params, and say a same-origin parent or opener can call window.<app>

A call over the in-flight limit used to stringify and convert its params before the busy check, so a large payload still cost the work it was being refused for. The params are now built by a closure the call runs only once it holds a slot.

The README said a parent, a child or an opener cannot reach window.<app>. That holds across origins only: a same-origin parent, child or opener with a window reference calls it directly, and the craft-web-host origin and source checks do not apply to that call.

---------

Co-authored-by: Helen Kwok <107752876+helenkwok@users.noreply.github.com>
2026-11-12 10:06:13 -05:00
Ken MantleandKenM76 35b4918481 prepress: composite the plates into a chosen output profile
Plates::composite converts through sRGB, which clips the press gamut
before a print profile sees it. Output converts press CMYK straight into
a gray, RGB or CMYK output profile (CMYK -> PCS -> output) at a chosen
intent and black point compensation, and Plates::composite_to combines
spot inks in that space: a multiply in gray or RGB, a multiply of the
complements in CMYK. CmsConversion::profile exposes the press profile.

The spot ramps move into shared helpers, used by composite and output;
the Lab encoding moves into one used by cms and output.

Part of #838 (item 2). The printer-leg semantics are adapted from pdfcer
(MIT, same author), crates/pdfcer-print/src/icc_proof.rs at 0a249a73;
the code is new, on craft-cms.

---------

Co-authored-by: KenM76 <KenM76>
2026-11-12 10:05:44 -05:00