Skip to content

v0.45.0 — rotation composes, and its angle is readable

Choose a tag to compare

@KenM76 KenM76 released this 07 Sep 20:25
· 135 commits to main since this release

Rotation composes now, and an annotation's angle can be read

Two fixes to annotation rotation, shipped together because they interlock.

Turning something repeatedly no longer grows it

Rotating an annotation four times by 15° drew it 1.93× wider and 1.42×
taller
than rotating it once by 60°. Every additional turn multiplied it
again.

The rectangle bounding an annotation was derived from the previous
rectangle. That is correct for one rotation and wrong for every one after it:
the second turn bounded an already-enlarged box while the artwork's own matrix
had only accumulated the angle, and ISO 32000-1 §12.5.5's placement step then
scaled the artwork up to fill the surplus.

The rectangle is now derived from the artwork, through one of three rules,
and rotate-annotation reports which one it used (rect_derived=):

rule when composes?
artwork the annotation has an appearance stream yes
geometry no appearance, but /L /Vertices /QuadPoints /CL /InkList yes
previous-rect neither no — see below

N turns totalling θ now produce the same drawn size as one turn of θ,
asserted to a placement scale of exactly 1.0 after 1, 2, 4, 8 and 24 turns.

One case is still not composable and now says so. An annotation with
neither an appearance stream nor rotatable geometry — a /Square or /Circle
with no /APis its rectangle, and §12.5.2 requires that rectangle
upright, so there is nowhere in the file its orientation could be recorded. It
genuinely does still grow on each turn. The CLI warns, and the API reports it,
so a caller can refuse rather than damage the object.

An annotation's rotation angle can now be read, and set directly

Nothing exposed an annotation's orientation, so it could be written and never
read back — a properties field would have had to display a guess, and a
selection outline could only be an upright box visibly larger than a turned
object.

  • Annotation::appearance_matrix — the appearance's /Matrix, raw.
  • Annotation::appearance_rotation_degrees() — the angle, anticlockwise.
    Returns nothing for a shear or a mirror: those are not angles, and reporting
    the nearest one would seed a field the operator is about to commit.
  • EditSession::set_annotation_rotation() and rotate-annotation --absolute
    — set the angle absolutely rather than by a delta, which is what a typed
    properties field needs. Idempotent. Refuses rather than assuming zero
    when the current angle cannot be read.
  • pdfcer_render::annot::appearance_placement() — §12.5.5's placement
    quadrilateral, made public, so a shell can draw a selection outline in the
    object's own orientation instead of re-implementing a normative algorithm.

Also

The verb's own documentation claimed "the artwork does not grow; only the
rectangle that bounds it does"
. That was true of one rotation and false of
the next. All three copies are corrected, with the superseded wording kept
legible rather than quietly deleted.


Verified: fmt, clippy --all-targets --all-features -D warnings, the
wasm32 target check, the no-GUI/no-network dependency check, and all
document/CI gates green; CI green on the tagged commit; the portable folder
copied to a fresh path and the fix reproduced there.

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