mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-08 19:42:12 +00:00
* Add craft-doc: read PhotoCraft, VectorCraft and EffectCraft documents without the apps
A new crate, craft-doc, reads the native files of three apps from bytes:
PhotoCraft .pcraft (a ZIP bundle), VectorCraft .vectorcraft (JSON, plain
or gzip, with the version 4 data after the JSON) and EffectCraft .ecproj
(JSON).
- sniff tells which app wrote a file from its first bytes.
- info gives the format version, the writer the file names, and whether
the version is one the crate reads.
- renditions returns VectorCraft's preview and PDF and PhotoCraft's
thumbnail and composite, when the file has them.
- outline returns PhotoCraft's canvas and layer tree, VectorCraft's
artboards and top-level layers, and EffectCraft's compositions with
their layers.
A file whose version is newer than the crate reads is refused with
Error::TooNew. Limits bound the JSON, each entry, the total bytes
decompressed and the JSON nesting, and each is checked before the bytes
it covers are allocated. ZIP entries are read by a small STORE and
DEFLATE reader in the crate. The crate depends on serde, serde_json,
thiserror and flate2 and builds for wasm32-unknown-unknown.
The tests build every document they read: JSON written by hand, PNG,
ZIP and gzip bytes assembled in the test. doc.yml runs fmt, clippy, the
tests and a wasm32 check.
* ci: do not keep the checkout token in the doc workflow
The checkout step sets persist-credentials: false, as jpeg.yml and media.yml do. No later step in the job uses the token.
* craft-doc: read each format's envelope through its format crate
craft-doc no longer keeps its own copies of the envelope types and
constants. It reads them with craft-pcraft-format (`MANIFEST`,
`FORMAT_VERSION`, `THUMB`, `COMPOSITE`, `check_manifest`),
craft-vectorcraft-format (`Head`, `Image`, `sniff`, `check`, `VERSION`,
`BLOB_MAGIC`, base64) and craft-ecproj-format (`Head::read`,
`SCHEMA_VERSION`). Its own base64 module is removed. A `.pcraft`
manifest is still read in one pass without building a
`serde_json::Value`, with the checks `read_manifest` applies. The
outlines are still read with craft-doc's read-only views of each
document, and the public API (`sniff`, `info`, `renditions`, `outline`,
`Limits`) does not change.
These inputs are now read as the format crates read them:
- A `.pcraft` manifest without `generator`, or with a `thumbnail` other
than `thumb.png` or a `composite` other than `composite/preview.png`,
is malformed for `renditions` and `outline`; `info` still reports its
version. A manifest with more than 4,194,304 JSON items is refused
with `LimitExceeded`.
- A `.vectorcraft` preview or PDF member without `mime` gives no
rendition.
- `sniff` takes `format` only from a top-level member, so
`{"meta":{"format":"vectorcraft"}}` is not a VectorCraft file.
tests/envelopes.rs writes a file of each format with the crates' types
and reads it through every call, and checks the inputs above.
doc.yml also runs when one of the three crates changes.
* craft-doc: sniff reads only top-level members of an EffectCraft project
`sniff` took any JSON object whose first member is `savedBy` for an
EffectCraft project when `"schema"` appeared anywhere in its first
bytes, so `{"savedBy":"1.0","metadata":{"schema":1}}` was claimed. It
now checks the top-level member names only, one at a time, without
keeping them.
* craft-doc: bound list elements with max_items; no rendition without a MIME type
Limits gains max_items (default 1,048,576): EffectCraft project items and
composition layers, VectorCraft artboards and layers, and PhotoCraft layers
at every level are counted before each one is read, and a file that lists
more gives Error::LimitExceeded. A list of empty objects within max_json no
longer expands into gigabytes of layers.
A VectorCraft preview or pdf member with an empty mime gives no rendition, as
one without a mime does; a preview may be PNG or JPEG. An EffectCraft
composition without a duration or a frame rate gets no timing instead of a
duration of 0.
97 lines
4.9 KiB
TOML
97 lines
4.9 KiB
TOML
[workspace]
|
|
resolver = "3"
|
|
members = ["crates/*"]
|
|
exclude = ["vendor/krilla", "vendor/hayro", "vendor/hayro-interpret", "vendor/hayro-syntax", "vendor/hayro-jbig2", "vendor/hayro-cmap", "vendor/hayro-postscript"]
|
|
|
|
[workspace.package]
|
|
version = "0.1.0"
|
|
edition = "2024"
|
|
license = "MIT OR Apache-2.0"
|
|
rust-version = "1.95"
|
|
repository = "https://github.com/storytold/craft-libs"
|
|
|
|
[workspace.dependencies]
|
|
# internal: add each crate here as `craft-<name> = { path = "crates/<name>" }`
|
|
craft-platform = { path = "crates/platform" }
|
|
craft-ai-core = { path = "crates/ai-core" }
|
|
craft-pdf = { path = "crates/pdf", version = "=0.1.0" }
|
|
craft-plate-test = { path = "crates/plate-test" }
|
|
krilla = { version = "=0.8.2", default-features = false, features = ["raster-images"] }
|
|
hayro = { version = "=0.7.1", default-features = false }
|
|
hayro-interpret = { version = "=0.7.0", default-features = false, features = ["embed-fonts", "embed-cmaps"] }
|
|
hayro-syntax = { version = "=0.7.2", default-features = false, features = ["std"] }
|
|
bytemuck = "1"
|
|
kurbo = "0.13"
|
|
thiserror = "2"
|
|
craft-atomic = { path = "crates/atomic" }
|
|
craft-cms = { path = "crates/cms" }
|
|
craft-crw = { path = "crates/crw" }
|
|
craft-dialogs = { path = "crates/dialogs" }
|
|
craft-doc = { path = "crates/doc" }
|
|
craft-docpath = { path = "crates/docpath" }
|
|
craft-ecproj-format = { path = "crates/ecproj-format" }
|
|
craft-font-catalog = { path = "crates/font-catalog" }
|
|
craft-font-manager = { path = "crates/font-manager" }
|
|
craft-font-store = { path = "crates/font-store" }
|
|
craft-fonts-build = { path = "crates/fonts-build" }
|
|
craft-i18n = { path = "crates/i18n" }
|
|
craft-jpeg = { path = "crates/jpeg" }
|
|
craft-keymap = { path = "crates/keymap" }
|
|
craft-media = { path = "crates/media" }
|
|
craft-media-testkit = { path = "crates/media-testkit" }
|
|
craft-menu = { path = "crates/menu" }
|
|
craft-pcraft-format = { path = "crates/pcraft-format" }
|
|
craft-prepress = { path = "crates/prepress" }
|
|
craft-print = { path = "crates/print" }
|
|
craft-proof = { path = "crates/proof" }
|
|
craft-quit = { path = "crates/quit" }
|
|
craft-settings = { path = "crates/settings" }
|
|
craft-text = { path = "crates/text" }
|
|
craft-ui = { path = "crates/ui" }
|
|
craft-update = { path = "crates/update" }
|
|
craft-vectorcraft-format = { path = "crates/vectorcraft-format" }
|
|
craft-window = { path = "crates/window" }
|
|
egui = { version = "0.37", default-features = false }
|
|
rayon = "1"
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = "1"
|
|
proptest = "1"
|
|
# 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"] }
|
|
|
|
[workspace.lints.rust]
|
|
unsafe_code = "forbid"
|
|
unexpected_cfgs = { level = "warn", check-cfg = ["cfg(craft_pdf_unicode_fixtures)"] }
|
|
|
|
[workspace.lints.clippy]
|
|
cognitive_complexity = "deny"
|
|
too_many_arguments = "deny"
|
|
wildcard_imports = "deny"
|
|
dbg_macro = "warn"
|
|
todo = "warn"
|
|
|
|
[patch.crates-io]
|
|
krilla = { path = "vendor/krilla" }
|
|
|
|
hayro = { path = "vendor/hayro" }
|
|
hayro-interpret = { path = "vendor/hayro-interpret" }
|
|
hayro-syntax = { path = "vendor/hayro-syntax" }
|
|
hayro-jbig2 = { path = "vendor/hayro-jbig2" }
|
|
hayro-cmap = { path = "vendor/hayro-cmap" }
|
|
hayro-postscript = { path = "vendor/hayro-postscript" }
|
|
ecolor = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
|
emath = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
|
epaint = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
|
epaint_default_fonts = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
|
egui = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
|
egui-winit = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
|
egui-wgpu = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
|
egui_glow = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
|
egui_extras = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
|
egui_kittest = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
|
egui_system_font_provider = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
|
eframe = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
|
egui_demo_lib = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
|
egui_inspection = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
|
winit = { git = "https://github.com/storytold/winit.git", rev = "a517b3846ee8457d84ee4975110bfda6797c44dc" }
|