Skip to content

v0.46.0 — six check-box tick styles, and two annotation gates enforced at last

Choose a tag to compare

@KenM76 KenM76 released this 08 Sep 05:17
· 103 commits to main since this release

Six check-box tick styles, and two annotation gates that were documented but not enforced

Check boxes and radio buttons get Acrobat's six styles

check · cross · star · circle · square · diamond

pdfcer add-check-box in.pdf --name agree --page 1 --rect 40,40,60,60 \
    --check-style star --no-tooltip -o out.pdf

pdfcer draws each as vector artwork and Acrobat does not, and that is a
deliberate divergence rather than an implementation shortcut. Acrobat's own
appearance stream selects a ZapfDingbats font and shows a glyph, so its tick
depends on resolving that font at display time — and Acrobat and Reader have a
long-standing, recurring bug failing exactly that, which leaves the box blank.
Paths need no font, no /Resources entry and no substitution.

The style is still recorded in /MK /CA as the single ZapfDingbats
character Acrobat stores, so any other editor reads the operator's choice back
into its own picker. Those character codes were derived from Adobe's own
ZapfDingbats.afm metrics and the Adobe Glyph List, and independently
reproduced by a second method before being accepted.

The style survives a resize. The appearance is rebuilt from geometry, so
the rebuild reads /MK /CA back — without that, a star would silently
become a tick on the first drag while the file still said star.

An annotation's /F flags can be set at last

pdfcer set-annotation-flags in.pdf --page 1 --index 0 --print --locked -o out.pdf

AnnotFlags had eight read accessors and no writer. An operator could see
that a markup was hidden and not un-hide it, could see it would not print and
not make it print, and could not lock anything — which made pdfcer's own
Locked gate unreachable from pdfcer.

Takes the whole flag word rather than per-bit toggles: Table 165's bits
interact (--no-view with --print means prints but is not on screen, a
combination reached deliberately), so a toggle-one-bit interface lets a
sequence of individually-sensible calls build a state nobody chose. Refuses a
form widget by name — that is edit-widget --visibility's key.

Two defects, both found by audit, both fixed as classes

The Locked flag was inverted in effect. ISO 32000-1 §12.5.3 Table 165 bit
8 says a locked annotation's properties "including position and size" shall
not be modified. move, resize, rotate and set_annotation_rotation all
ignored it while set_markup_style, reshape and the deletion guards
honoured it — so a locked markup could not be recoloured and could be
dragged anywhere
. LockedContents (bit 10) deliberately still does not
block a transform; it guards the text.

Five verbs documented an encryption refusal none of them performed.
rotate_annotation, set_annotation_rotation, resize_annotation,
move_annotation and set_markup_note each listed
EditError::DocumentEncrypted in their # Errors section with no code path
able to produce it. They enforce it now, and the guard runs before subtype
routing so an encrypted document is named as such rather than being answered
with "use rotate_widget instead".

Widget fill and border colours round-trip

/MK /BG and /BC had read and write on opposite keys of one
dictionary
: pdfcer wrote /BC (hard-coded black, never settable) and never
read it; read /BG and never wrote it. Neither survived a round trip.

pdfcer edit-widget in.pdf --name agree --background 1,0,0 --border-color none -o out.pdf
pdfcer list-fields out.pdf --widgets        # background=1,0,0 border_color=none

none writes Table 189's empty array, which states no colour and is not the
same as the key being absent. CMYK is passed through as CMYK, never converted.
⚠️ pdfcer's own renderer does not paint /MK colours (they are the
named-not-painted case); the values are in the file for viewers that honour
them.


Verified: fmt, clippy -D warnings, and nineteen document/CI gates
green; CI green on the tagged commit; the portable folder copied to a fresh
path and all six styles, the colour round trip, the widget refusal and the
lock-then-refuse sequence reproduced there in the shipped binary.

sha256 of the zip is in the accompanying .sha256 file.