Skip to content

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.

draft: 1e-17     # was false; now true
draft: 0.0       # false, unchanged
draft: 1         # true, unchanged

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