Skip to content

1.4

Choose a tag to compare

@sbeitzel sbeitzel released this 04 Sep 16:03
· 91 commits to main since this release
971a809

Pages you can number, and hand on.

Four fixes from a downstream consumer that assembles binders out of separately
rendered tunes. Page breaks are now something the source can ask for, page numbers
are something the source can set and a consumer can override, and a footer
placeholder that never worked is gone along with the silence that hid it.

Highlights

%%newpage starts a page, and can renumber it (#140). The directive is listed in
ABC v2.2 §11.4.7 and implemented by abcm2ps and abc2svg, so it lands under its
established unprefixed name — a file that page-breaks in abcm2ps does not need editing
to page-break here. Where it is written decides what it moves: the file header moves
the first tune, a gap between tunes moves the tune below it, a tune header moves that
tune, and inside the body it moves the stave it stands above. A break part-way through
a stave snaps back to the start of it — a page break is a system break — with a
pageBreakSnappedToStave warning; one past the last tune is dropped with
pageBreakAfterLastTune. %%newpage N renumbers the page it opens, and the count
carries on from there.

%%ceolkit:pagenumber reaches the page it numbers (#138). It was parsed,
validated, and then dropped: $P came from pageIndex + 1 and nothing else, so every
document numbered from 1 — including the "start numbering from page 3" example in
EXTENSIONS.md, which printed "Page 1". The renderer now reads the last .pageNumber
directive out of the first tune and numbers from it. It composes with %%newpage N
rather than competing: this directive names the document's opening page, and each
%%newpage N renames the one it opens. The base reaches the footer, $P,
${pagenumber} and the ceolkit-meta scroll-sync comment alike, so they cannot
disagree.

A footer span can be marked for a downstream consumer (#137). Outlining is a
one-way door: under the default .outlines a footer reading "Page 1" leaves the
renderer as six <use> elements and no text anywhere in the document, so a tool
assembling pages from several files has nothing left to rewrite. ${name} in a
%%footer template now marks a span as the consumer's to fill in. CeolKit still draws
its own value there, but wraps the span in a group carrying the name and the position,
size, family and anchor it drew at:

<g id="ceolkit-tag-pagenumber" class="ceolkit-tag" data-ceolkit-tag="pagenumber"
   data-x="306" data-y="780" data-font-size="12"
   data-font-family="Libertinus Serif" data-text-anchor="middle">…</g>

A consumer empties the group and stamps its own text without owning the face. The
wrapper is emitted identically in every text-rendering mode, and a consumer that
ignores it gets exactly the output it always got. ${pagenumber}, ${pagecount},
${title} and ${date} have CeolKit values; any other name draws nothing and leaves a
positioned, empty group — a binder name is not CeolKit's to invent.

Unrecognised footer placeholders are diagnosed, and $Q is gone (#141).
EXTENSIONS.md documented a $Q "non-resetting page counter" that no commit on any
branch ever implemented; %%footer "$Q" engraved the literal characters, and under
.outlines engraved them as artwork, so the only way to find the gap was to find $Q
printed on a finished page. Removed rather than implemented. The class is covered too:
the parser now warns on every $-token a template uses that CeolKit does not
substitute, so an unimplemented abcm2ps placeholder ($F, $V, $P0) and a typo
($p) are both caught where they are written. A $ not followed by a letter is
ordinary text, and ${name} is a substitutable span — neither is a token, neither is
diagnosed.

New in the model

Type / member Purpose
PageBreak a %%newpage, as a stave index plus an optional restart number
Tune.pageBreaks: [PageBreak] the breaks the tune asks for, in source order
Diagnostic.Code.unknownFooterPlaceholder a $-token CeolKit does not substitute
Diagnostic.Code.pageBreakSnappedToStave a break mid-stave, moved to the stave's start
Diagnostic.Code.pageBreakAfterLastTune a break with nothing left to move

PageBreak is positional and deliberately not also a flattened CeolKitDirective:
a page break has no meaning apart from where it falls, which is how %%score is
modelled too.

New in the renderer

Type / member Purpose
ForcedPageBreak a break the layout engine honours, by group index
ResolvedPage.pageNumber the number the layout stamped on the page
VerticalLayoutEngine.layout(_:firstPageNumber:) firstPageNumber defaults to 1

The layout engine now stamps each page as it decides where the page falls, because
only the walk that breaks the pages can honour a directive that breaks and renames at
once. The footer, $P, ${pagenumber} and the scroll-sync comment all read that
stamp.

ckprobe reports a tune's page breaks alongside its staff plans.

Documentation

  • EXTENSIONS.md — the ${…} substitution mark; the list of the four $
    placeholders CeolKit supports, and a plain statement that the set is narrower than
    abcm2ps's; %%ceolkit:pagenumber's "Interaction with %%newpage" section is true
    for the first time.
  • CONFORMANCE.md — a new §11.4.7 section on how CeolKit reads %%newpage.

What's Changed

Full Changelog: v1.3.0...v1.4.0

Upgrading

Source-compatible with 1.3.0. The only changed signature is
VerticalLayoutEngine.layout(_:), which gains a defaulted firstPageNumber:
parameter; existing calls compile unchanged.

Two behaviour changes worth knowing about:

  • A document with %%ceolkit:pagenumber N in its preamble now actually numbers from
    N. If you worked around the old behaviour by offsetting page numbers downstream,
    remove the workaround.
  • %%footer templates using a $-token CeolKit does not substitute now produce a
    warning. The engraved output is unchanged — the token is still drawn literally.