0.122 → 0.123 — a number under a boolean field renders by whether it is non-zero, a form.pdf whose page /Annots hold a widget, or whose dictionary names a key twice, fails to open, a YAML comment drops the whitespace ending it, a comment under a tagged empty value, an alias key or a merge keeps its slot, a WASM options object must be a plain object and refuses a non-enumerable key it does not read, and an open matrix refuses an added item whose title is blank¶
The first break nothing reports: a float under a boolean field whose
magnitude is at most 2.220446049250313e-16 renders true, where it rendered
false. The render's boolean coercion reads a number by whether it is
non-zero. It read a float by whether its magnitude exceeded f64::EPSILON, the
spacing of floats near 1.0, so 1e-17 and -2e-16 read false. 0 and
-0.0 still render false, and every integer reads as before.
The value is valid under both readings, so no diagnostic fires. A matrix member's bare tick reads through the same coercion, so a tick spelled as such a float is held where it was not.
To find one, search your documents for a float under a boolean field or a
matrix member. Write false where the float meant off.
The second break nothing reports: a YAML comment in a card-yaml block drops
the whitespace ending its line, so to_markdown writes # note where it wrote
# note. 0.122 kept the spaces and tabs after a comment's last word and
wrote them back. A block's text is trimmed before it parses, so a comment that
came to end the block, as one above a ... end marker does, read back without
them, and the emission was no fixed point. Every value and every comment's
slot are unchanged, and no diagnostic fires. The canonical markdown of a block
holding such a comment changes once, so a hash over it recomputes. A stored row
and a CardWire read a comment's text the same way: a document loaded from a
0.122 row holding such a comment emits markdown that parses back to it, and the
row's bytes change once it is saved again. To find one, search the card-yaml
blocks for a # comment, on its own line or trailing a value, ending in
whitespace.
The third break nothing reports: a comment re-emits at the slot the YAML
gives it, where 0.122 dropped or moved it. 0.122 dropped a comment indented
under a tagged empty value (k: !!str, k: !), one inside the value of an
alias key (*x :), and one inside a merge's value (<<:). It bound a comment
at a lesser column below a tagged or anchored empty value to that entry, where
the collection around it holds the slot. It placed a comment after a <<
entry a key too late, by the source's children, where the value holds a
mapping's own keys first and then the keys its merges bring. Each now keeps
its slot among the value's keys. A top-level alias key keeps its place in the
field order, where 0.122 moved its field to the block's end and gave its
trailer to the field before it, and a parse::unsupported_yaml_tag inside its
value names the key's text (main.o.foo.s, where it named main.o..s). Every
value is unchanged and no diagnostic fires; the canonical markdown of a block
holding such a comment moves, as do Payload::nested_comments and the DTO a
parse stores, so a hash over any of them recomputes.
The first loud break: an acroform quill whose form.pdf holds a widget annotation
in a page's /Annots fails to open as pdf::existing_acroform, whether or not
its catalog names an /AcroForm. 0.122 refused a background by its catalog
entry alone. One whose catalog /AcroForm was deleted, or nulled as PyMuPDF's
xref_set_key leaves it, stamped the fresh widgets beside its own in the page
/Annots. MuPDF listed the old widgets as live, with their old values, and the
canvas drew them; no diagnostic fired. The refusal reads every page's
/Annots, each annotation written in the array or referenced from it, and its
message names the first page holding a widget.
Strip the widget annotations from every page's /Annots, the quillification
layer's job: nulling or deleting the catalog's /AcroForm strips the catalog
entry alone. A caller of quillmark_pdf::stamp meets the same refusal on such a
base whenever it stamps a field.
The second loud break: a form.pdf whose trailer, catalog or page-tree node
names a key twice, or holds a token where a key belongs, fails to open as
pdf::parse, and one whose /Info object does fails as pdf::write. A
dictionary names each key once (ISO 32000-1 §7.3.7), and readers part on which
of two entries holds. 0.122 read the first and rewrote only it, so
/AcroForm null /AcroForm 7 0 R stamped a catalog whose second entry still
named the base's form. A key named twice with one value, such as
/Type /Page /Type /Page, stamped right, and fails too. A token where a key
belongs, such as the 2 of /A 1 2, ended 0.122's scan, and a key after it
read as absent. { and } end a token (ISO 32000-1 §7.2.2), so
/Lang /en{US} holds the stray token {US} and fails too, where 0.122 read
one name. A key with no value fails the same way: it takes the next token as
its own, so 0.122, appending /AcroForm 7 0 R to a catalog ending in /Lang,
wrote /Lang /AcroForm and a stray 7 0 R. A trailer's direct /Info
dictionary of any such shape stamps a fresh /Info carrying none of its
entries, where 0.122 carried them, and nothing reports that one.
Write each key once, with its value, and drop the stray token. A caller of
quillmark_pdf::stamp meets the catalog's refusal as pdf::stamp_parse
whenever it stamps a field, and quillmark_pdf::page_canvas_boxes refuses the
trailer, catalog and page-tree node as pdf::parse.
The third loud break: a WASM options object that is not a plain object
throws, and so does a non-enumerable key its verb does not read. The render
options, new Engine options, an Addr and a CardInput take an object whose
prototype is null or Object.prototype of any realm. Their reader takes a
key up the prototype chain and a non-enumerable one, where 0.122's refusal
walked the own enumerable keys alone: Object.create({ today }) rendered the
local date, and a Map, whose entries are no properties, rendered every option
at its default. A class instance, or an object inheriting the keys it means,
rendered as asked; it throws too.
class Options { format = "svg"; }
await engine.render(quill, doc, new Options()); // 0.122: SVG. 0.123: rejects.
await engine.render(quill, doc, { ...new Options() }); // Both: SVG.
The refusal is the uncoded QuillmarkError an unknown key throws, and its
message names the key or what was passed. Spread the object into a literal,
with each inherited key it means: { ...defaults, ...options }. A spread drops
a non-enumerable key too, such as the __ob__ Vue 2 defines on reactive data.
The fourth loud break: an open matrix's added item whose title is blank,
"" or whitespace alone, fails validation as validation::enum_violation, and
the render with it, where it printed a held row with nothing beside the
tick. The title is the label the page prints for the item, so a blank one
leaves the key untitled, as a missing or null title does. The diagnostic
anchors at the item's key and carries the open arg, as an untitled key's
does. An editor that stores an emptied title box as "" writes one.
qualifications:
wing_ig: { title: Wing IG } # an added item, unchanged
aide: { title: "" } # 0.122: a bare tick. 0.123: enum_violation at main.qualifications.aide
Write the title, or remove the key to drop the item. A quill whose default:
holds such an item fails to load as quill::default_not_in_enum, and a card
kind's seed: as quill::seed_enum_violation.
| Break | Surface | Action |
|---|---|---|
A float of magnitude at most 2.220446049250313e-16 under a boolean field, or as a matrix member's tick, renders true, where it rendered false |
rendered output, plate JSON | Write false where the float meant off. Nothing reports this one: find it as above |
A YAML comment's text drops the whitespace ending its line, so to_markdown writes the comment without it |
comment text as parsed, loaded from a stored row or read from a CardWire; to_markdown output; hashes over either |
None |
A comment under a tagged empty value, inside an alias key's value or a merge's, below a tagged or anchored empty value at a lesser column, or after a << entry re-emits at the slot the YAML gives it, where it dropped or moved; a top-level alias key keeps its place in the field order |
to_markdown output, Payload::items and Payload::nested_comments, the DTO a parse stores, parse::unsupported_yaml_tag paths, hashes over any |
None |
A base PDF whose page /Annots hold a /Subtype /Widget fails as pdf::existing_acroform, naming the page, whatever its catalog /AcroForm holds, where it stamped beside the old widgets |
acroform quills' form.pdf, quillmark_pdf::stamp |
Strip the widgets from every page's /Annots. Nulling the catalog's /AcroForm, as PyMuPDF's xref_set_key does, is not enough |
A base PDF whose trailer, catalog or page-tree node names a key twice or with no value, or holds a token where a key belongs, fails as pdf::parse, and one whose /Info object does as pdf::write, where it stamped; a trailer's direct /Info dictionary of any such shape stamps a fresh /Info without its entries |
acroform quills' form.pdf, quillmark_pdf::stamp (pdf::stamp_parse for the catalog), quillmark_pdf::page_canvas_boxes |
Write each key once, with its value, and drop the stray token |
An options object, Addr or CardInput that is not a plain object throws, such as a class instance or Object.create(defaults), where 0.122 read its keys, own or inherited |
WASM | Pass a plain object: { ...defaults, ...options } |
A Map as an options object, Addr or CardInput throws, where 0.122 read none of its entries |
WASM | Pass Object.fromEntries(map) |
| A non-enumerable key outside the ones its verb reads throws, where 0.122 dropped it | WASM | Drop the key, or spread the object: { ...options } |
A new Engine argument other than an object, null or undefined throws, where a number or a boolean read as no options |
WASM new Engine |
Pass { backends }, or nothing |
An open matrix's added item whose title is "" or whitespace alone fails as validation::enum_violation at its key, and the render with it, where it printed a bare tick; in a default: or seed: the quill fails to load |
document YAML, diagnostics, Quill.yaml |
Write the title, or remove the key |