* Add craft-figma-rest: a typed model of the Figma REST file JSON
The neutral model for design files: the .fig and .sketch readers map onto
it and an application's importer takes it. Typed records for the document
tree, paints, effects, text styles, layout grids, constraints, export
settings and the component and style metadata; every record keeps the
fields it has no name for, so a file read and written loses nothing.
The vectorNetwork extension is typed and documented with the others.
The layer tree is converted level by level in both directions, and the
depth and node limits are checked on the JSON before any typed value is
built.
* craft-figma-rest: Warnings and style_name for the readers that build this shape
Moved from FigCraft's REST importer and .fig reader (crates/format/src/rest.rs
Warnings, fig.rs style_name), which the .fig and Sketch mappings share.
* craft-fig: map the message onto the REST file JSON
FigCraft's .fig mapping (crates/format/src/fig/map and fig/text_path.rs
at e8b53e33) moved onto craft-figma-rest: rest::to_rest turns a decoded
message into the REST API file JSON, with the vector networks layers name
by vectorNetworkRef handed over beside it.
Connector routing and the text path measure check are the caller's
geometry (rest::Geometry; rest::Straight when it has none). The plugin
data key under which a writer keeps an outlined stroke is an option.
The geometry blob is read with craft_fig::path instead of a second
decoder.
* craft-fig: an empty overriddenSymbolID is no swap
The empty GUID (4294967295:4294967295) in overriddenSymbolID means "no
swap". The mapping took it as the main, found no node and made the
instance an empty frame; it now keeps the main its symbolData names, in
the instance mapping and in the list of mains a file's instances use.
Ported from storytold/figcraft#2208.
* craft-fig: a swapped nested instance takes its new main's look
A nested instance swapped to another main stores the previous main's
look on its own node, so the mapping drew that. Each group of the look
(fills, strokes, stroke settings, effects, corners) whose stored values
are a copy of the previous main's now takes the new main's; a group the
node or a row above it changes is kept, and a style link is decided by
the topmost layer that names it.
Ported from storytold/figcraft#2204 (the fig/map part; the warning's
locale entry stays in the app).
* craft-fig: a vector point without a corner radius of its own takes the layer's
A point whose style sets no cornerRadius, or that has no style, takes the
layer's cornerRadius, as the file draws it; a point's own value (an
explicit 0 included) still wins. The layer's radius is part of the
network cache key, so an instance overriding the radius doesn't share its
main's network.
Ported from storytold/figcraft#2115 (the fig/map part; its export change
stays in the app).
* craft-fig: map deep files within a 1 MiB stack
The mapping recursed once per level of layer nesting in a frame of about
2 KB (an unoptimised build): 300 nested frames, or a chain of instances
of mains holding each other, overflowed a 1 MiB stack before the 256
level limit could stop them, a crash on hostile input.
The work on one layer now happens in layer_head and layer_tail, which
return before and after its children are mapped, and what they hand over
is boxed, so the recursive frame is about 800 bytes. An unoptimised build
now maps past the limit on 512 KiB; the limit stays 256. The test at the
limit runs on a 1 MiB thread, for frames and for instances, and
overflowed before this change.
---------
Co-authored-by: Trevor Brown <trevorbrown.dev@gmail.com>
Co-authored-by: Giovanni Almeida <gioalmeida07@gmail.com>
* Add craft-kiwi: the kiwi binary schema format, decoded and encoded
The generic kiwi decoder and encoder from FigCraft's .fig import and export
(crates/format/src/fig/kiwi.rs at 3b9bf8c9), moved as it is: binary schema
decode and encode, the compact text form of a schema, message decode into a
tree keyed by field name, and message encode checked against the schema.
The value budget that follows a .fig file's compressed size stays with the
.fig reader; here decode_message_within takes the budget and
DEFAULT_MAX_VALUES is the floor FigCraft used.
* Add craft-fig: the .fig container, read and written
The app-independent half of FigCraft's .fig import and export
(crates/format/src/fig at 3b9bf8c9): the ZIP archive with its image
budget, the chunked canvas (deflate, zlib and zstd chunks), the value
budget that follows the compressed canvas, images carried in the
message's blobs, and the vector network and geometry blob encodings.
The message stays a craft-kiwi tree read by field name; mapping it onto
a document stays in the app.
* craft-kiwi: no scan per field in schema decode; the text form keeps the limits
Schema::decode compared each message field id with every earlier one: 20
definitions of 10 000 fields, within every cap, took a billion
comparisons. A set per definition now.
Schema::parse applies the definition and field caps of the binary form,
looks type names up in an index instead of scanning the lines per field,
accepts a struct or message without fields, and refuses two fields of one
name and a message field id that is 0 (it ends a message) or repeated.