# Exemples musicaux en MEI avec un apparat critique en notes

> Des blocs MEI 5 gravés dans le texte sur une portée de 5 mm, les variantes codées en app, lem et rdg, et l’apparat par mesure et par note en bas de page.

- Version HTML: https://postext.dev/fr/cookbook/musicology-mei-apparatus
- Recette Nº 165 · Encadrés et notes · Niveau 2 (Intermédiaire) · Sorties: Canvas, PDF
- Genres: Articles et publications universitaires
- Nécessite postext ≥ 1.54.0, postext-pdf ≥ 1.54.0 · testée avec 1.54.0, postext-pdf 1.54.0 le 2026-10-22
- Pages: [1](https://postext.dev/cookbook/musicology-mei-apparatus/en/p01.webp?v=cd2d6301), [2](https://postext.dev/cookbook/musicology-mei-apparatus/en/p02.webp?v=cd2d6301), [3](https://postext.dev/cookbook/musicology-mei-apparatus/en/p03.webp?v=cd2d6301), [4](https://postext.dev/cookbook/musicology-mei-apparatus/en/p04.webp?v=cd2d6301), [5](https://postext.dev/cookbook/musicology-mei-apparatus/en/p05.webp?v=cd2d6301)
- PDF: https://postext.dev/cookbook/musicology-mei-apparatus/en/musicology-mei-apparatus.pdf?v=cd2d6301
- Ouvrir dans le Sandbox: https://postext.dev/fr/sandbox#recipe=musicology-mei-apparatus&lang=en (.postext: https://postext.dev/cookbook/musicology-mei-apparatus/en/musicology-mei-apparatus.postext)
- Dernière mise à jour: 2026-10-22
- Autres langues: [en](https://postext.dev/en/cookbook/musicology-mei-apparatus.md), [es](https://postext.dev/es/cookbook/musicology-mei-apparatus.md), [ca](https://postext.dev/ca/cookbook/musicology-mei-apparatus.md), [de](https://postext.dev/de/cookbook/musicology-mei-apparatus.md), [it](https://postext.dev/it/cookbook/musicology-mei-apparatus.md), [pt](https://postext.dev/pt/cookbook/musicology-mei-apparatus.md), [zh](https://postext.dev/zh/cookbook/musicology-mei-apparatus.md), [ja](https://postext.dev/ja/cookbook/musicology-mei-apparatus.md), [ar](https://postext.dev/ar/cookbook/musicology-mei-apparatus.md)

## En bref

Un chapitre d’une étude sur la musique médiévale. De courts passages d’un canon anglais du XIIIe siècle sont imprimés en musique dans le texte, et les notes en bas de page relèvent, mesure par mesure, où les éditions anciennes divergent.

## Ce que vous allez composer

Le chapitre 3 d’une étude sur la manière dont les antiquaires anglais ont lu la musique médiévale : cinq pages de 156 × 234 mm sur *Sumer is icumen in*, la rota de Reading, et sur les éditeurs qui l’ont mise en partition entre 1776 et 1889. Trois passages du canon sont gravés dans le texte, en Leipzig sur une portée de 5 mm, avec la numérotation des mesures de la partition de W. S. Rockstro, et chaque commentaire appelle son apparat en bas de page : « m. 40, n. 2 la ] sol Hw, Bu ». Les notes sont brèves et serrées comme l’est un apparat, la musique occupe la justification du texte, et chaque leçon vient de l’article de Rockstro dans le *Dictionary* de Grove, ce que le chapitre dit dès son deuxième paragraphe.

**Cette recette répond à:**

- Comment donner aux exemples musicaux un apparat critique des variantes des sources en notes de bas de page ?
- Comment faire des notes de bas de page ?

## La réponse courte

````js
// script.js, lignes 36–60
// In the Markdown each example is a ```mei fence. A variant is encoded where it stands,
// <app><lem>…</lem><rdg source="#Hw #Bu">…</rdg></app>: the staff shows the lemma, and
// the file keeps the reading for anyone who opens the encoding. The apparatus entry is a
// footnote cited from the paragraph that discusses the bar: `[^b40]: **b. 40, n. 2** A ] G`.
const scoreStyle = {
  staffSize: mm(5), // running-text examples: 4.5–5.5 mm
  musicFont: 'Leipzig', // lighter than Bravura, close to a 19th-century plate
  textFont: 'Crimson Pro', // the underlay in the book's own face
  barNumbers: 'system', // the apparatus is keyed by bar: number every system
  spacing: 0.95,
  systemGap: mm(4),
  marginTop: pt(4),
  marginBottom: pt(LEAD / 2),
  color: col('ink'),
  minSystemsAtBreak: 2, // never leave one system of an example alone at a page foot
};
const footnotes = {
  placement: 'column', // the foot of the page in one column
  numbering: 'chapter',
  fontSize: pt(8.5),
  lineHeight: pt(10.75),
  hangingIndent: em(1.2), // turnover lines align past the number
  spaceBetween: pt(2),
  separator: { width: 0.2, lineWidth: pt(0.5), color: col('rubric') },
};
````

## Ingrédients

**Enseigne**

- [Notation musicale](https://postext.dev/fr/docs/music.md#une-partition-dans-le-texte): Des partitions écrites en texte dans des blocs ```abc, ```musicxml, ```mei ou ```humdrum, gravées par Verovio à la largeur de la colonne et coupées en systèmes qui se poursuivent sur la colonne ou la page suivante.
- [Notes de bas de page](https://postext.dev/fr/docs/document-format-notes-references.md#notes-de-bas-de-page): Des notes appelées avec [^id] et composées au pied de la colonne qui les cite, ou après le chapitre, numérotées par chapitre ou sur tout le livre.

**Utilise aussi**

- [Style de partition](https://postext.dev/fr/docs/configuration-resources.md#style-de-partition)
- [Ouvertures composées](https://postext.dev/fr/docs/configuration-headings.md#étendue-et-maquette-avancée)
- [Attributs de titre](https://postext.dev/fr/docs/document-format.md#attributs-de-titre)
- [Titres courants et folios](https://postext.dev/fr/docs/configuration-headers-footers.md#en-têtes-et-pieds-de-page)
- [Marges en miroir](https://postext.dev/fr/docs/configuration-page-layout.md#marges-en-miroir)
- [Titres courants selon le rôle de la page](https://postext.dev/fr/docs/configuration-headers-footers.md#éléments-de-texte)
- [Styles de paragraphe](https://postext.dev/fr/docs/configuration-styles.md#styles-de-paragraphe)
- [Palette de couleurs sémantique](https://postext.dev/fr/docs/configuration-fonts-colors-viewers.md#palette-de-couleurs)
- [Couleur du papier](https://postext.dev/fr/docs/configuration-page-layout.md#page)
- [Veuves, orphelines et fins de paragraphe trop courtes](https://postext.dev/fr/docs/configuration-page-flow.md#orphelines-veuves-lignes-creuses-et-règles-de-maintien)
- [Export PDF](https://postext.dev/fr/docs/configuration-pdf-output.md#générer-des-pdf)
- [Listings de code](https://postext.dev/fr/docs/configuration-styles.md#listings-de-code)
- [Équilibrage des colonnes](https://postext.dev/fr/docs/configuration-page-flow.md#équilibrage-des-colonnes)
- [Polices intégrées au PDF](https://postext.dev/fr/docs/configuration-pdf-output.md#pourquoi-un-fournisseur-de-polices)

**La configuration en un coup d’œil**

- [`bodyText`](https://postext.dev/fr/docs/configuration-text.md#texte-courant), [`colorPalette`](https://postext.dev/fr/docs/configuration-fonts-colors-viewers.md#palette-de-couleurs), [`footer`](https://postext.dev/fr/docs/configuration-headers-footers.md#en-têtes-et-pieds-de-page), `footnotes`, [`header`](https://postext.dev/fr/docs/configuration-headers-footers.md#en-têtes-et-pieds-de-page), [`headings`](https://postext.dev/fr/docs/configuration-headings.md#titres), [`layout`](https://postext.dev/fr/docs/configuration-page-layout.md#mise-en-page), [`locale`](https://postext.dev/fr/docs/configuration-language.md#césure), [`page`](https://postext.dev/fr/docs/configuration-page-layout.md#page), [`paragraphStyles`](https://postext.dev/fr/docs/configuration-styles.md#styles-de-paragraphe), [`scoreStyle`](https://postext.dev/fr/docs/configuration-resources.md#style-de-partition)

**API**

- [`buildDocumentWithFonts`](https://postext.dev/fr/docs/configuration-programmatic-usage.md#charger-les-polices-avant-la-mise-en-page), [`decompressWoff2`](https://postext.dev/fr/docs/configuration-pdf-output.md#fournisseur-de-polices-dans-le-navigateur-fontsource--woff2), [`initMusicEngine`](https://postext.dev/fr/docs/music.md#une-partition-dans-le-texte), [`renderPageToCanvas`](https://postext.dev/fr/docs/configuration-programmatic-usage.md#rendre-une-page-en-bitmap), [`renderToPdf`](https://postext.dev/fr/docs/configuration-pdf-output.md#générer-des-pdf)

**Caractères**

- Crimson Pro (OFL-1.1), Cormorant Garamond (OFL-1.1), Source Sans 3 (OFL-1.1)

## Préparation

### 1 · Écrire chaque exemple en MEI, variantes comprises

Les réglages sont dans [la réponse courte](https://postext.dev/fr/cookbook/musicology-mei-apparatus.md#la-réponse-courte) ci-dessus ; la musique est dans le Markdown, un bloc ```` ```mei ```` par exemple. Là où deux sources divergent, la mesure contient un `<app>` avec la leçon retenue dans `<lem>` et l’autre dans `<rdg source="#Hw #Bu">`. Le graveur imprime le lemme : la mesure 40 de l’[exemple 3.3](https://postext.dev/cookbook/musicology-mei-apparatus/en/p05.webp?v=cd2d6301) montre le la, et le sol de Hawkins et Burney reste dans le fichier pour qui ouvre le codage. Donnez à chaque bloc un `alt` dans la langue de la page : c’est ce que lisent de la partition un lecteur d’écran et le PDF balisé.

### 2 · Rattacher l’apparat aux mesures que voit le lecteur

`barNumbers: 'system'` numérote la première mesure de chaque système, et le `<measure n="37">` de chaque bloc fait partir le compte là où commence l’extrait dans l’édition suivie : l’exemple 3.3 s’ouvre donc à 37, et une note qui dit « m. 40 » renvoie à une mesure que le lecteur trouve. L’entrée d’apparat est une note de bas de page ordinaire, appelée depuis le paragraphe qui commente la mesure : lemme, crochet fermant, variante, sigles en italique. Une note ne peut pas être appelée depuis l’intérieur d’une partition ; l’appel se place donc dans la phrase qui nomme la leçon.

### 3 · Dimensionner la musique pour le texte courant

Une portée de 5 mm en Leipzig tient dans une justification de 116 mm avec cinq ou six mesures à 3/2 par système, et les onze mesures de l’exemple 3.3 tiennent en deux systèmes. `textFont` compose les paroles en Crimson Pro, le caractère du texte, et `minSystemsAtBreak: 2` empêche qu’une page se termine sur un seul système d’un exemple dont la suite passe à la page suivante. Le titre de chaque exemple est un intertitre de troisième niveau : il ne reste jamais seul en bas de page sans sa partition.

### 4 · Laisser une page à notes finir court

```js
// script.js, lignes 64–78
const opener = {
  enabled: true,
  minHeight: mm(50), // every chapter's text starts on the same grid line
  slot: { elements: [
    { kind: 'text', id: 'kicker', content: '{attr.kicker}', ...label, fontSize: pt(8.5),
      letterSpacing: pt(1.8), color: col('rubric'), align: 'left',
      placement: at('container', 'top-left', 0, 8) },
    { kind: 'text', id: 'title', content: '{titleText}', fontFamily: 'Cormorant Garamond',
      italic: true, fontWeight: 500, fontSize: pt(29), lineHeight: 1.05, color: col('ink'),
      align: 'left', overflow: 'wrap', // design text ends in an ellipsis by default
      placement: { ...at('#kicker', 'below', 0, 3), size: { width: mm(100), height: 'auto' } } },
    { kind: 'rule', id: 'rule', direction: 'horizontal', thickness: pt(1), color: col('rubric'),
      placement: { ...at('#title', 'below', 0, 5), size: { width: mm(14) } } },
  ] },
};
```

L’ouverture descend le texte de 50 mm sous un titre en Cormorant italique. Plus loin, une page dont la ligne suivante entraînerait une longue note s’arrête quelques lignes plus tôt, et `balancing: { maxLinesPerHeading: 1 }` évite que l’équilibrage des colonnes remonte ce blanc au-dessus de l’intertitre suivant, où il passerait pour une rupture dans le raisonnement.

### 5 · Le livre au verso, le chapitre au recto

```js
// script.js, lignes 82–99
const head = (id, content, parity, edge, x, extra = {}) => ({
  kind: 'text', id, content, parity, pages: 'body', // never on the opener
  ...label, fontSize: pt(7.5), letterSpacing: pt(1.3), color: col('muted'),
  placement: at('page', edge, x, 13), ...extra,
});
const folio = { color: col('rubric'), fontWeight: 700 };
const header = { elements: [
  head('verso-folio', '{pageNumber}', 'even', 'top-left', MARGIN.outer,
    { ...folio, align: 'left' }),
  head('verso-title', '{title}', 'even', 'top-left', MARGIN.outer + 8, { align: 'left' }),
  head('recto-title', '{chapterTitle}', 'odd', 'top-right', -(MARGIN.outer + 8),
    { align: 'right', overflow: 'ellipsis' }),
  head('recto-folio', '{pageNumber}', 'odd', 'top-right', -MARGIN.outer,
    { ...folio, align: 'right' }),
] };
// The opener carries a drop folio instead, centred under the text block.
const footer = { elements: [{ ...head('drop-folio', '{pageNumber}', 'all', 'bottom', 0,
  { ...folio, align: 'center', placement: at('page', 'bottom', 0, -11) }), pages: 'opener' }] };
```

Les folios en rouge de rubrique sur le bord extérieur, le livre au verso et le chapitre au recto ; `pages: 'body'` les retire de l’ouverture, qui reçoit un folio centré en pied.

## La recette complète

Un seul fichier, composé à partir du dossier de la recette avec le texte d’exemple et le kit commun des Recettes déjà inclus ; il construit sa propre page. Pour l’exécuter, placez-le dans un `<script type="module">` d’une page vide ou collez-le dans le panneau JS d’un nouveau pen CodePen (en tant que module). Il importe postext depuis esm.sh : il n’y a rien à installer ni à compiler.

- Dossier de la recette: https://github.com/drnachio/postext/tree/main/cookbook/musicology-mei-apparatus

### script.js

````js
// ═══ Postext Cookbook · Nº 165 · MEI music examples with a critical apparatus ═════
// https://postext.dev/en/cookbook/musicology-mei-apparatus
// Code: MIT · Text: original (CC BY 4.0) · Music: Sumer is icumen in, after Rockstro (PD)
// Fonts: Crimson Pro, Cormorant Garamond, Source Sans 3 (SIL OFL 1.1) · Needs postext ≥ 1.54.0
import {
  buildDocumentWithFonts, renderPageToCanvas, initMusicEngine,
} from 'https://esm.sh/postext';
import { renderToPdf, decompressWoff2 } from 'https://esm.sh/postext-pdf';

const LANG = 'en'; // @lang: the language of the sample document ('en' | 'es')
const RECIPE = 'musicology-mei-apparatus';

// ─── 1 · Design ─────────────────────────────────────────────────────────────
// #region palette: a rubric red for kickers, labels and the notes' rule; ink on cream
const palette = {
  ink: '#211d1a', // text and notation: a warm near-black
  rubric: '#8c2f1f', // the one accent, the red of the manuscript's rubrics
  muted: '#6c645b', // running heads, colophon
  rule: '#cbbfae', // hairlines
  paper: '#fcfaf6',
};
// col(id): a colour linked to its palette entry, with the entry's hex beside the id.
const col = (id) => ({ hex: palette[id], model: 'hex', paletteId: id });
const colorPalette = [
  ...Object.entries(palette).map(([id, hex]) => ({ id, name: id, value: { hex, model: 'hex' } })),
  // The engine's defaults link to main-color: point it at the accent, never the default blue.
  { id: 'main-color', name: 'accent (defaults)', value: { hex: palette.rubric, model: 'hex' } },
];
// #endregion
const MARGIN = { top: 22, bottom: 22, inner: 19, outer: 21 }; // 156 × 234 mm, mirrored
const LEAD = 14.5; // body leading in pt: the baseline grid
const at = (to, edge, x = 0, y = 0) => ({ anchor: { to, edge }, offset: { x: mm(x), y: mm(y) } });
const label = { fontFamily: 'Source Sans 3', fontWeight: 600, textTransform: 'uppercase' };

// #region answer: MEI examples engraved in the text, their apparatus in footnotes
// In the Markdown each example is a ```mei fence. A variant is encoded where it stands,
// <app><lem>…</lem><rdg source="#Hw #Bu">…</rdg></app>: the staff shows the lemma, and
// the file keeps the reading for anyone who opens the encoding. The apparatus entry is a
// footnote cited from the paragraph that discusses the bar: `[^b40]: **b. 40, n. 2** A ] G`.
const scoreStyle = {
  staffSize: mm(5), // running-text examples: 4.5–5.5 mm
  musicFont: 'Leipzig', // lighter than Bravura, close to a 19th-century plate
  textFont: 'Crimson Pro', // the underlay in the book's own face
  barNumbers: 'system', // the apparatus is keyed by bar: number every system
  spacing: 0.95,
  systemGap: mm(4),
  marginTop: pt(4),
  marginBottom: pt(LEAD / 2),
  color: col('ink'),
  minSystemsAtBreak: 2, // never leave one system of an example alone at a page foot
};
const footnotes = {
  placement: 'column', // the foot of the page in one column
  numbering: 'chapter',
  fontSize: pt(8.5),
  lineHeight: pt(10.75),
  hangingIndent: em(1.2), // turnover lines align past the number
  spaceBetween: pt(2),
  separator: { width: 0.2, lineWidth: pt(0.5), color: col('rubric') },
};
// #endregion

// #region opener: kicker, a Cormorant italic title and a short red rule, sunk 50 mm
const opener = {
  enabled: true,
  minHeight: mm(50), // every chapter's text starts on the same grid line
  slot: { elements: [
    { kind: 'text', id: 'kicker', content: '{attr.kicker}', ...label, fontSize: pt(8.5),
      letterSpacing: pt(1.8), color: col('rubric'), align: 'left',
      placement: at('container', 'top-left', 0, 8) },
    { kind: 'text', id: 'title', content: '{titleText}', fontFamily: 'Cormorant Garamond',
      italic: true, fontWeight: 500, fontSize: pt(29), lineHeight: 1.05, color: col('ink'),
      align: 'left', overflow: 'wrap', // design text ends in an ellipsis by default
      placement: { ...at('#kicker', 'below', 0, 3), size: { width: mm(100), height: 'auto' } } },
    { kind: 'rule', id: 'rule', direction: 'horizontal', thickness: pt(1), color: col('rubric'),
      placement: { ...at('#title', 'below', 0, 5), size: { width: mm(14) } } },
  ] },
};
// #endregion

// #region running-heads: book title on the verso, chapter on the recto, folios outside
const head = (id, content, parity, edge, x, extra = {}) => ({
  kind: 'text', id, content, parity, pages: 'body', // never on the opener
  ...label, fontSize: pt(7.5), letterSpacing: pt(1.3), color: col('muted'),
  placement: at('page', edge, x, 13), ...extra,
});
const folio = { color: col('rubric'), fontWeight: 700 };
const header = { elements: [
  head('verso-folio', '{pageNumber}', 'even', 'top-left', MARGIN.outer,
    { ...folio, align: 'left' }),
  head('verso-title', '{title}', 'even', 'top-left', MARGIN.outer + 8, { align: 'left' }),
  head('recto-title', '{chapterTitle}', 'odd', 'top-right', -(MARGIN.outer + 8),
    { align: 'right', overflow: 'ellipsis' }),
  head('recto-folio', '{pageNumber}', 'odd', 'top-right', -MARGIN.outer,
    { ...folio, align: 'right' }),
] };
// The opener carries a drop folio instead, centred under the text block.
const footer = { elements: [{ ...head('drop-folio', '{pageNumber}', 'all', 'bottom', 0,
  { ...folio, align: 'center', placement: at('page', 'bottom', 0, -11) }), pages: 'opener' }] };
// #endregion

const config = {
  locale: t({ en: 'en-gb', es: 'es' }), // the language of the hyphenation patterns
  colorPalette,
  page: { sizePreset: 'custom', width: mm(156), height: mm(234), dpi: 150,
    backgroundColor: col('paper'),
    margins: { top: mm(MARGIN.top), bottom: mm(MARGIN.bottom), left: mm(MARGIN.inner),
      right: mm(MARGIN.outer), mirror: true } }, // left = inner
  layout: { layoutType: 'single' },
  bodyText: { fontFamily: 'Crimson Pro', fontSize: pt(11), lineHeight: pt(LEAD),
    color: col('ink'), boldColor: col('ink'), italicColor: col('ink'), referenceColor: col('ink'),
    textAlign: 'justify', firstLineIndent: mm(4.5), indentAfterHeading: false,
    hyphenation: { enabled: true }, optimalLineBreaking: true,
    avoidWidows: true, avoidOrphans: true, avoidRunts: true },
  headings: { fontFamily: 'Crimson Pro', color: col('ink'),
    // A page whose next line cannot take its footnote ends short: at most one extra grid
    // line goes above a heading, the rest stays at the foot, as in a book with notes.
    balancing: { maxLinesPerHeading: 1 },
    levels: [
    { level: 1, advancedDesign: opener, marginBottom: pt(0),
      breakBefore: { enabled: true, parity: 'odd' } },
    { level: 2, fontFamily: 'Cormorant Garamond', fontSize: pt(14), lineHeight: pt(LEAD),
      fontWeight: 600, italic: true, color: col('ink'), marginTop: pt(LEAD), marginBottom: pt(3) },
    // The example labels: a heading keeps itself with the score under it.
    { level: 3, ...label, fontSize: pt(7.5), lineHeight: pt(LEAD), letterSpacing: pt(1.1),
      color: col('rubric'), marginTop: pt(LEAD / 2), marginBottom: pt(0) },
  ] },
  paragraphStyles: [
    { id: 'colophon', ...label, textTransform: 'none', fontWeight: 400, fontSize: pt(7.5),
      lineHeight: pt(10), color: col('muted'), textAlign: 'left', firstLineIndent: pt(0),
      marginTop: pt(LEAD) },
  ],
  scoreStyle,
  footnotes,
  header,
  footer,
};

// ─── 2 · Content ────────────────────────────────────────────────────────────
const markdown = `---
title: "The Antiquaries’ Middle Ages"
---

# The Reading Rota and Its Editors {kicker="Chapter 3"}

No piece of medieval music was printed, scored and argued over by English antiquaries as often as the six-voice rota *Sumer is icumen in*. It survives on a single leaf, fol. 11v of the miscellany that Humfrey Wanley described for the Earl of Oxford around 1709 and that is now British Library, Harley MS 978. Wanley called it the oldest piece of its kind he had seen. Between Sir John Hawkins’s *General History* of 1776 and the article that William Smyth Rockstro wrote for George Grove’s *Dictionary of Music and Musicians*, the rota went through at least five scores, each claiming to correct the last.

This chapter follows that sequence of editions and tests it at three places where the editors disagreed. The music examples are new transcriptions, and their critical apparatus stands in the notes: each entry gives the bar and the note, the reading adopted, a closing bracket, and the readings of the other witnesses.

## The witnesses and this edition

Four witnesses are cited. **H** is the manuscript. **Hw** is the score in the first volume of Hawkins’s *General History of the Science and Practice of Music* (1776). **Bu** is the score in the second volume of Charles Burney’s *General History of Music* (1782). **Ro** is Rockstro’s solution of the canon, printed with the first part of his article in the third volume of Grove’s *Dictionary*.[^ro]

The examples follow Ro in pitch, clefs and note values, since Rockstro is the only editor of the period who explains every substitution he made. The manuscript, he writes, uses only a C clef, the *B rotundum*, square black notes with tails, one square note without a tail, and black lozenges. He renders a tailed note as a dotted semibreve when it is perfect and as a semibreve when it is imperfect, the untailed square note as a semibreve, and the lozenges as minims, under a signature of perfect time with the lesser prolation, “closely corresponding with the Signature 3-2 in modern Music”.[^values] The readings of H, Hw and Bu below are those that Rockstro reports. This chapter has not collated the two eighteenth-century prints afresh, and where Rockstro is silent, the apparatus is silent too.

## The first phrase and a corrected note

### Example 3.1 · Cantus I, bars 1–7, with the English and Latin texts

\`\`\`mei {alt="Sumer is icumen in, the first voice, bars 1 to 7, with the English words and the Latin text Perspice Christicola"}
<mei xmlns="http://www.music-encoding.org/ns/mei" meiversion="5.1">
<meiHead><fileDesc><titleStmt><title>Sumer is icumen in, Cantus I, bars 1–7</title></titleStmt><pubStmt/><sourceDesc><source xml:id="H"><bibl>BL Harley 978, f. 11v</bibl></source><source xml:id="Ro"><bibl>Rockstro, Grove iii</bibl></source></sourceDesc></fileDesc></meiHead>
<music><body><mdiv><score>
<scoreDef meter.count="3" meter.unit="2" keysig="1f"><staffGrp><staffDef n="1" lines="5" clef.shape="G" clef.line="2"/></staffGrp></scoreDef>
<section>
<measure n="1"><staff n="1"><layer n="1"><note pname="f" oct="5" dur="1"><verse n="1"><syl wordpos="i" con="d">Sum</syl></verse><verse n="2"><syl wordpos="i" con="d">Per</syl></verse></note><app><lem><note pname="e" oct="5" dur="2"><verse n="1"><syl wordpos="t">er</syl></verse><verse n="2"><syl wordpos="m" con="d">spi</syl></verse></note></lem><rdg source="#H"><note pname="f" oct="5" dur="2"><verse n="1"><syl wordpos="t">er</syl></verse><verse n="2"><syl wordpos="m" con="d">spi</syl></verse></note></rdg></app></layer></staff></measure>
<measure n="2"><staff n="1"><layer n="1"><note pname="d" oct="5" dur="1"><verse n="1"><syl>is</syl></verse><verse n="2"><syl wordpos="t">ce</syl></verse></note><note pname="e" oct="5" dur="2"><verse n="1"><syl wordpos="i" con="d">i</syl></verse><verse n="2"><syl wordpos="i" con="d">Chri</syl></verse></note></layer></staff></measure>
<measure n="3"><staff n="1"><layer n="1"><note pname="f" oct="5" dur="1"><verse n="1"><syl wordpos="m" con="d">cum</syl></verse><verse n="2"><syl wordpos="m" con="d">sti</syl></verse></note><note pname="f" oct="5" dur="2"><verse n="1"><syl wordpos="t">en</syl></verse><verse n="2"><syl wordpos="m" con="d">co</syl></verse></note></layer></staff></measure>
<measure n="4"><staff n="1"><layer n="1"><note pname="e" oct="5" dur="2" dots="1"><verse n="1"><syl>in,</syl></verse><verse n="2"><syl wordpos="t">la,</syl></verse></note><note pname="d" oct="5" dur="4"/><note pname="c" oct="5" dur="2"/></layer></staff></measure>
<measure n="5"><staff n="1"><layer n="1"><note pname="a" oct="4" dur="1"><verse n="1"><syl wordpos="i" con="d">Lhud</syl></verse><verse n="2"><syl>que</syl></verse></note><note pname="a" oct="4" dur="2"><verse n="1"><syl wordpos="t">e</syl></verse><verse n="2"><syl wordpos="i" con="d">dig</syl></verse></note></layer></staff></measure>
<measure n="6"><staff n="1"><layer n="1"><note pname="b" oct="4" dur="1"><verse n="1"><syl>sing</syl></verse><verse n="2"><syl wordpos="m" con="d">na</syl></verse></note><note pname="g" oct="4" dur="2"><verse n="1"><syl wordpos="i" con="d">cuc</syl></verse><verse n="2"><syl wordpos="m" con="d">ci</syl></verse></note></layer></staff></measure>
<measure n="7"><staff n="1"><layer n="1"><note pname="a" oct="4" dur="1" dots="1"><verse n="1"><syl wordpos="t">cu.</syl></verse><verse n="2"><syl wordpos="t">o.</syl></verse></note></layer></staff></measure>
</section></score></mdiv></body></music></mei>
\`\`\`

The opening is the best-documented phrase of the rota, because Rockstro returned to the leaf itself in the second part of his article and listed the erasures he could see. The scribe corrected his own work in the same ink, and the first correction falls on the second note.[^b1] Rockstro took the erasures as improvements made while the piece was being written down rather than as repairs of a faulty copy, and used them to support his view that the canon grew out of a popular tune that a trained musician adjusted until its phrases fitted against one another.

Two further decisions are visible in bars 2 and 4. The Latin text is printed in full where the manuscript abbreviates the name of Christ,[^b2] and the single tailed lozenge under the word *in* becomes a dotted minim and a crotchet.[^b4] The flat in the signature stands for the *B rotundum* of the manuscript, which Rockstro reads as a B flat throughout.[^bflat]

## The pes

### Example 3.2 · Pes, bars 1–8, as scored by Rockstro

\`\`\`mei {alt="Sumer is icumen in, the two lower voices of the pes, bars 1 to 8"}
<mei xmlns="http://www.music-encoding.org/ns/mei" meiversion="5.1">
<meiHead><fileDesc><titleStmt><title>Sumer is icumen in, the pes, bars 1–8</title></titleStmt><pubStmt/><sourceDesc><source xml:id="Ro"><bibl>Rockstro, Grove iii</bibl></source></sourceDesc></fileDesc></meiHead>
<music><body><mdiv><score>
<scoreDef meter.count="3" meter.unit="2" keysig="1f"><staffGrp symbol="bracket" bar.thru="false"><staffDef n="1" lines="5" clef.shape="F" clef.line="4"/><staffDef n="2" lines="5" clef.shape="F" clef.line="4"/></staffGrp></scoreDef>
<section>
<measure n="1"><staff n="1"><layer n="1"><note pname="f" oct="3" dur="1" dots="1"><verse n="1"><syl>Sing</syl></verse></note></layer></staff><staff n="2"><layer n="1"><note pname="c" oct="4" dur="1" dots="1"><verse n="1"><syl>Sing</syl></verse></note></layer></staff></measure>
<measure n="2"><staff n="1"><layer n="1"><note pname="g" oct="3" dur="1" dots="1"><verse n="1"><syl wordpos="i" con="d">cuc</syl></verse></note></layer></staff><staff n="2"><layer n="1"><note pname="b" oct="3" dur="1" dots="1"><verse n="1"><syl wordpos="i" con="d">cuc</syl></verse></note></layer></staff></measure>
<measure n="3"><staff n="1"><layer n="1"><note pname="f" oct="3" dur="1" dots="1"><verse n="1"><syl wordpos="t">cu</syl></verse></note></layer></staff><staff n="2"><layer n="1"><note pname="c" oct="4" dur="1" dots="1"><verse n="1"><syl wordpos="t">cu.</syl></verse></note></layer></staff></measure>
<measure n="4"><staff n="1"><layer n="1"><note pname="g" oct="3" dur="1"><verse n="1"><syl>nu.</syl></verse></note><note pname="a" oct="3" dur="2"/></layer></staff><staff n="2"><layer n="1"><mRest/></layer></staff></measure>
<measure n="5"><staff n="1"><layer n="1"><note pname="c" oct="4" dur="1" dots="1"><verse n="1"><syl>Sing</syl></verse></note></layer></staff><staff n="2"><layer n="1"><note pname="f" oct="3" dur="1" dots="1"><verse n="1"><syl>Sing</syl></verse></note></layer></staff></measure>
<measure n="6"><staff n="1"><layer n="1"><note pname="b" oct="3" dur="1" dots="1"><verse n="1"><syl wordpos="i" con="d">cuc</syl></verse></note></layer></staff><staff n="2"><layer n="1"><note pname="g" oct="3" dur="1" dots="1"><verse n="1"><syl wordpos="i" con="d">cuc</syl></verse></note></layer></staff></measure>
<measure n="7"><staff n="1"><layer n="1"><note pname="c" oct="4" dur="1" dots="1"><verse n="1"><syl wordpos="t">cu.</syl></verse></note></layer></staff><staff n="2"><layer n="1"><note pname="f" oct="3" dur="1" dots="1"><verse n="1"><syl wordpos="t">cu</syl></verse></note></layer></staff></measure>
<measure n="8"><staff n="1"><layer n="1"><mRest/></layer></staff><staff n="2"><layer n="1"><note pname="g" oct="3" dur="1"><verse n="1"><syl>nu.</syl></verse></note><note pname="a" oct="3" dur="2"/></layer></staff></measure>
</section></score></mdiv></body></music></mei>
\`\`\`

Below the four voices of the canon the manuscript writes two short parts to be sung as a ground, each to the words *Sing cuccu nu*. The Latin rubric opens *Hanc rotam cantare possunt quatuor socii*: four companions can sing the round, not counting those who hold the pes. Each new voice begins “at the first note after the cross”, which Ro marks over bar 5. The first pes is repeated *faciens pausacionem in fine*, the second *pausans in medio et non in fine*.[^rubric] Ro follows the rubric exactly: the first pes rests in bar 8, the second in bar 4, so that one of the two lower voices is always sounding.

The pes is also where the eighteenth-century scores went most visibly wrong. According to Rockstro, Hawkins misread both of its ligatures and printed the passage incorrectly at every repetition; Burney corrected the mistake.[^lig] Neither ligature can be shown in a modern score, and Example 3.2 resolves both into separate notes, as Ro does.

Rockstro’s own analysis of the part-writing begins from this score. The fourth, sixth, seventh, eighth and twenty-fourth bars, he observes, are in strict two-part counterpoint “of irreproachable purity”, while the passage from bar 9 to bar 10 gives consecutive fifths between the first and third voices of the canon, and bars 37 to 39 three consecutive fifths between the first cantus and the second voice of the pes.[^fifths] For Rockstro the mixture of styles was the strongest argument for a melody older than its harmony.

## The question of date

The dates proposed for the leaf say as much about the editors as about the manuscript. Hawkins placed it in the middle of the fifteenth century, arguing that its notation was the *cantus figuratus* he credited to John Dunstable, who, Hawkins wrote, died in 1455; Rockstro called the reasoning an anachronism “absolutely worthless”.[^hawkins] Burney thought the piece not much later than the thirteenth or fourteenth century. Joseph Ritson referred the manuscript to the middle of the thirteenth, and Thomas Busby, reprinting Burney’s score note for note in 1819, returned it to the fifteenth.

The decisive arguments came from palaeography. In 1855 William Chappell published a coloured facsimile in *Popular Music of the Olden Time* and later argued that the leaf was written at Reading Abbey by a monk named John of Fornsete around 1226. In April 1862 Sir Frederick Madden noted on a flyleaf of the volume that the whole was of the thirteenth century and that its earlier part had been written at Reading about 1240, a date the Palaeographical Society adopted for its facsimile of 1878. On the Continent, Johann Nikolaus Forkel (1788), August Wilhelm Ambros (1862 and 1868) and Edmond de Coussemaker (1865) all wrote on the rota without seeing the manuscript, a point Rockstro makes against each of them in turn.[^continent] A more recent study puts the copying between 1261 and 1264.[^wulstan]

## “Cuccu” in bar 40

### Example 3.3 · Cantus I, bars 37–47

\`\`\`mei {alt="Sumer is icumen in, the first voice, bars 37 to 47, from Cuccu, cuccu to ne swik thu nauer nu"}
<mei xmlns="http://www.music-encoding.org/ns/mei" meiversion="5.1">
<meiHead><fileDesc><titleStmt><title>Sumer is icumen in, Cantus I, bars 37–47</title></titleStmt><pubStmt/><sourceDesc><source xml:id="Hw"><bibl>Hawkins 1776</bibl></source><source xml:id="Bu"><bibl>Burney 1782</bibl></source><source xml:id="Ro"><bibl>Rockstro, Grove iii</bibl></source></sourceDesc></fileDesc></meiHead>
<music><body><mdiv><score>
<scoreDef meter.count="3" meter.unit="2" keysig="1f"><staffGrp><staffDef n="1" lines="5" clef.shape="G" clef.line="2"/></staffGrp></scoreDef>
<section>
<measure n="37"><staff n="1"><layer n="1"><note pname="c" oct="5" dur="1" dots="1"><verse n="1"><syl wordpos="i" con="d">Cuc</syl></verse></note></layer></staff></measure>
<measure n="38"><staff n="1"><layer n="1"><note pname="d" oct="5" dur="1" dots="1"><verse n="1"><syl wordpos="t">cu,</syl></verse></note></layer></staff></measure>
<measure n="39"><staff n="1"><layer n="1"><note pname="c" oct="5" dur="1" dots="1"><verse n="1"><syl wordpos="i" con="d">cuc</syl></verse></note></layer></staff></measure>
<measure n="40"><staff n="1"><layer n="1"><note pname="b" oct="4" dur="1"><verse n="1"><syl wordpos="t">cu.</syl></verse></note><app><lem><note pname="a" oct="4" dur="2"/></lem><rdg source="#Hw #Bu"><note pname="g" oct="4" dur="2"/></rdg></app></layer></staff></measure>
<measure n="41"><staff n="1"><layer n="1"><note pname="f" oct="4" dur="1"><verse n="1"><syl>Wel</syl></verse></note><note pname="a" oct="4" dur="2"><verse n="1"><syl wordpos="i" con="d">sing</syl></verse></note></layer></staff></measure>
<measure n="42"><staff n="1"><layer n="1"><note pname="b" oct="4" dur="1"><verse n="1"><syl wordpos="t">es</syl></verse></note><note pname="g" oct="4" dur="2"><verse n="1"><syl>thu</syl></verse></note></layer></staff></measure>
<measure n="43"><staff n="1"><layer n="1"><note pname="a" oct="4" dur="1" dots="1"><verse n="1"><syl wordpos="i" con="d">cuc</syl></verse></note></layer></staff></measure>
<measure n="44"><staff n="1"><layer n="1"><note pname="b" oct="4" dur="1"><verse n="1"><syl wordpos="t">cu,</syl></verse></note><note pname="c" oct="5" dur="2"><verse n="1"><syl>ne</syl></verse></note></layer></staff></measure>
<measure n="45"><staff n="1"><layer n="1"><note pname="a" oct="4" dur="1"><verse n="1"><syl>swik</syl></verse></note><note pname="c" oct="5" dur="2"><verse n="1"><syl>thu</syl></verse></note></layer></staff></measure>
<measure n="46"><staff n="1"><layer n="1"><note pname="g" oct="4" dur="1"><verse n="1"><syl wordpos="i" con="d">nau</syl></verse></note><note pname="e" oct="4" dur="2"><verse n="1"><syl wordpos="t">er</syl></verse></note></layer></staff></measure>
<measure n="47"><staff n="1"><layer n="1"><note pname="f" oct="4" dur="1" dots="1"><verse n="1"><syl>nu.</syl></verse></note></layer></staff></measure>
</section></score></mdiv></body></music></mei>
\`\`\`

The last passage of the tune shows how an error could travel. Hawkins and Burney, Rockstro reports, both print a G where the manuscript has an A in bar 40, on the second syllable of *cuccu*, and they do so each time the phrase returns in a new voice.[^b40] Against Hawkins’s faulty pes the G passes; against Burney’s corrected pes it makes what Rockstro calls “a horrible discord”. The variant is encoded in the source of Example 3.3 as a reading of Hw and Bu, while the engraved staff shows the lemma.

Both historians also fitted the words to the notes differently from the manuscript from bar 41 onwards, at *Wel singes thu cuccu, ne swik thu nauer nu*.[^b41] Rockstro gives no details of their underlay, and the example therefore records only the fact. The one modern copy Rockstro knew went further and left out the pes, reducing the rota to four parts.

:::paragraphs{style="colophon"}
Set in Crimson Pro, Cormorant Garamond and Source Sans 3 (SIL OFL); music in Leipzig, engraved by Verovio · Text: original, CC BY 4.0 · Music: *Sumer is icumen in* (13th century), after W. S. Rockstro’s score in Grove’s *Dictionary of Music and Musicians*, iii, 766–767 (public domain)
:::

[^ro]: W. S. Rockstro, “Sumer is icumen in”, in G. Grove (ed.), *A Dictionary of Music and Musicians*, iii, 765–768, continued in iv, 1–3 (London: Macmillan, 1900 printing). The score, iii, 766–767, is cited from the Wikisource transcription of that printing.

[^values]: Ro, iii, 765. The tailed lozenge on the first note of *in* is the one exception (see the note on bar 4).

[^b1]: **b. 1, n. 2** E ] F *H* before correction (“The second note on Stave 1 was originally an F”, Ro, iv, 1). The reading before correction is encoded as a variant in Example 3.1.

[^b2]: **b. 2, n. 2 – b. 4, n. 1** *Chri-sti-co-la* ] abbreviated with the Greek letters *chi* and *rho* *H*, Ro. Expanded as Rockstro glosses it, *Christicola* (iii, 765).

[^b4]: **b. 4, nn. 1–2** dotted minim, crotchet ] one lozenge with a tail *H*, the only tailed lozenge in the manuscript, which Rockstro could “scarcely conceive to be accidental” (iii, 765).

[^bflat]: **bb. 1–7, signature** B flat ] *B rotundum* *H*. The flat is printed in the signature, as in Ro.

[^rubric]: The rubric is quoted from the transcription and translation in the Wikipedia article “Sumer is icumen in”, which also gives the folio. Rockstro scores the canon “in exact accordance with the Latin directions” (iii, 765) but does not print them.

[^lig]: **Pes, bb. 1–8** both ligatures misread at every statement *Hw*; corrected *Bu* (Ro, iii, 765). Rockstro does not say how Hawkins read them, so no variant is encoded.

[^fifths]: Ro, iv, 2. The bar numbers refer to Rockstro’s score and to the examples of this chapter.

[^hawkins]: Ro, iii, 765. Rockstro dates Hawkins’s first volume 1770; the *General History* appeared in 1776.

[^continent]: Ro, iii, 768, and iv, 1, citing Forkel, *Allgemeine Geschichte der Musik*, ii, 490–500; Ambros, *Geschichte der Musik*, ii, 473–475; Coussemaker, *L’Art harmonique*, 144.

[^wulstan]: D. Wulstan, “‘Sumer is icumen in’: A Perpetual Puzzle-Canon?”, *Plainsong and Medieval Music* 9 (2000), 1–17, at 8. Wulstan proposes W. de Wycombe as the scribe.

[^b40]: **b. 40, n. 2** A ] G *Hw*, *Bu* (and Busby 1819, after *Bu*), at every entry of the phrase (Ro, iii, 765). Encoded as a variant in Example 3.3.

[^b41]: **bb. 41–47, underlay** as *H* ] text placed otherwise under the notes *Hw*, *Bu* (Ro, iii, 765).
`; // content.<lang>.md, inlined by the Cookbook

// ─── 3 · Fonts ──────────────────────────────────────────────────────────────
// Every face the design uses, loaded before the first build with the ones the config names.
const FONTS = {
  'Crimson Pro': ['400', '400i', '600', '600i', '700'],
  'Cormorant Garamond': ['500i', '600i'],
  'Source Sans 3': ['400', '600', '700'],
};

// ─── 4 · Build & show ───────────────────────────────────────────────────────
await initMusicEngine(); // Verovio, loaded only by books that hold a score
const doc = await buildDocumentWithFonts({ markdown }, config, kitFonts(FONTS));
showPages(doc, { title: t({ en: 'Music examples with a critical apparatus',
  es: 'Ejemplos musicales con aparato crítico' }) });
offerPdf(() => renderToPdf(doc, { fontProvider: fontsourceProvider }), `${RECIPE}.pdf`);

// ─── Kit ── helpers shared by every Cookbook recipe · postext.dev/cookbook ─────

// ─── Kit · core v1 ── the same in every recipe · postext.dev/cookbook
function mm(value) { return { value, unit: 'mm' }; }
function pt(value) { return { value, unit: 'pt' }; }
function em(value) { return { value, unit: 'em' }; }
/** The sample language's string: t({ en: 'Figure', es: 'Figura' }). */
function t(strings) { return strings[LANG] ?? Object.values(strings)[0]; }
/** A file in this recipe's assets folder, served from the Postext repo by jsDelivr. */
function asset(file) { return `https://cdn.jsdelivr.net/gh/drnachio/postext@main/cookbook/${RECIPE}/assets/${file}`; }

// ─── Kit · fonts v3 ── the same in every recipe · postext.dev/cookbook
// Postext loads the faces a config asks for before layout (prepareFonts,
// buildDocumentWithFonts) and asks the kit for the files: Fontsource's, the
// files the PDF embeds too.

/** The options of buildDocumentWithFonts / prepareFonts / withLoadedFonts:
 *  the Fontsource resolver, and the faces of FONTS loaded with the ones the
 *  config names. FONTS = { 'Family Name': ['400', '400i', '700'] }. */
function kitFonts(FONTS = {}) {
  const faces = Object.entries(FONTS).flatMap(([family, specs]) => specs.map((spec) =>
    ({ family, weight: parseInt(spec, 10), style: spec.endsWith('i') ? 'italic' : 'normal' })));
  return { resolve: fontsourceResolver, faces };
}

/** A face's Fontsource files, in a fixed order: latin, then latin-ext and greek
 *  when `text` needs them and the family ships them (kitSubsetsFor), each with
 *  its unicode-range. Null for a weight or slant Fontsource does not ship. */
async function fontsourceResolver(family, weight, style, { text = '' } = {}) {
  const id = fontsourceId(family);
  const meta = await fontsourceMeta(family);
  if (meta && !(meta.weights.includes(weight) && meta.styles.includes(style))) return null;
  kitStatus('Loading fonts…');
  const at = await fontsourceAt(id);
  return ['latin', ...kitSubsetsFor(text, meta)].map((subset) => ({
    source: `${at}files/${id}-${subset}-${weight}-${style}.woff2`,
    unicodeRange: kitRanges()[subset],
  }));
}

/** The unicode-range of each Fontsource file the kit loads (a function: the
 *  kit is inlined after the pen's code, which runs before a const would be set). */
function kitRanges() {
  return {
    latin: 'U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,'
      + 'U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD',
    'latin-ext': 'U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,'
      + 'U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF',
    greek: 'U+0370-03FF',
  };
}

/** For the cjk, arabic and comics blocks, which add their own files: the
 *  faces of `faces` = { 'Family Name': ['400', '400i', '700'] }. `text` is the sample:
 *  č ł † α χ also load latin-ext and greek files (kitSubsetsFor). With
 *  `optional`, a face Fontsource does not ship is skipped instead of failing.
 *  The files are added once all have loaded, in the order asked (latin, then
 *  latin-ext, then greek), whatever order they arrive in: the last one added
 *  wins a character two files hold. Resolves to the number of faces added. */
async function loadFonts(faces, text = '', { optional = false } = {}) {
  kitStatus('Loading fonts…');
  const ranges = kitRanges();
  const jobs = [];
  for (const [family, specs] of Object.entries(faces)) {
    const id = fontsourceId(family);
    const todo = [...new Set(specs)].map((spec) => [parseInt(spec, 10), spec.endsWith('i') ? 'italic' : 'normal'])
      .filter(([weight, style]) => !hasFace(family, weight, style)); // before any await
    const at = fontsourceAt(id);
    const meta = optional || /[^\0-ÿ]/u.test(text) ? await fontsourceMeta(family) : null;
    const subsets = ['latin', ...kitSubsetsFor(text, meta)];
    for (const [weight, style] of todo) {
      if (optional && !(meta?.weights.includes(weight) && meta.styles.includes(style))) continue;
      for (const subset of subsets) {
        const url = `${await at}files/${id}-${subset}-${weight}-${style}.woff2`;
        const face = new FontFace(family, `url(${url}) format('woff2')`,
          { weight: String(weight), style, unicodeRange: ranges[subset] });
        jobs.push(face.load().catch(() => {
          if (subset === 'latin' && !optional) throw new Error(`Fontsource has no ${family} ${weight} ${style}`);
        }));
      }
    }
  }
  const ready = await Promise.all(jobs).catch((error) => { kitFail(error); throw error; });
  return ready.filter((face) => face && document.fonts.add(face)).length;
}

/** A loaded FontFace covers this family, weight and style (fonts.check() would
 *  also say yes for families nobody declared). */
function hasFace(family, weight, style) {
  for (const face of document.fonts) {
    if (face.status !== 'loaded' || face.style !== style) continue;
    if (face.family.replace(/^["']|["']$/g, '') !== family) continue;
    const [low, high = low] = face.weight.split(' ').map(Number);
    if (weight >= low && weight <= high) return true;
  }
  return false;
}

/** The files beyond latin `text` needs that `meta`'s family ships. */
function kitSubsetsFor(text, meta) {
  return [[/[Ā-˿ᴀ-ᶿḀ-ỿ†ℓⱠ-Ɀ꜠-ꟿ]/u, 'latin-ext'], [/[Ͱ-Ͽ]/u, 'greek']]
    .filter(([re, x]) => re.test(text) && meta?.subsets?.includes(x)).map(([, x]) => x);
}

/** Fontsource's id for a family: 'Source Serif 4' → 'source-serif-4'. */
function fontsourceId(family) { return family.toLowerCase().replace(/\s+/g, '-'); }

/** A package's folder at the one version @5 names now (#766). */
function fontsourceAt(id, u = `https://cdn.jsdelivr.net/npm/@fontsource/${id}@`) {
  return (fontsourceAt.c ??= {})[id] ??= fetch(u + '5/package.json').then((r) => r.json())
    .then((p) => p.version || 5, () => 5).then((v) => `${u}${v}/`);
}

/** The family's Fontsource metadata (weights, styles, subsets), or null. */
function fontsourceMeta(family) {
  fontsourceMeta.cache ??= new Map();
  const id = fontsourceId(family);
  if (!fontsourceMeta.cache.has(id)) {
    fontsourceMeta.cache.set(id, fetch(`https://api.fontsource.org/v1/fonts/${id}`)
      .then((res) => (res.ok ? res.json() : null), () => null));
  }
  return fontsourceMeta.cache.get(id);
}

// ─── Kit · viewer v2 ── the same in every recipe · postext.dev/cookbook
/** The pages as spreads on a dark desk, page 1 alone, then verso | recto,
 *  each painted when it scrolls near. Other options go to renderPageToCanvas. */
function showPages(docs, { title, width = 460, ...render } = {}) {
  const root = viewer(title);
  const pages = [docs].flat().flatMap((doc) =>
    doc.pages.map((page) => ({ doc, page, n: (doc.pageIndexOffset ?? 0) + page.index })));
  const spreads = [];
  let verso = null;
  for (const p of pages) {
    if (p.n % 2 === 1) { if (verso) spreads.push([verso, null]); verso = p; }
    else { spreads.push([verso, p]); verso = null; }
  }
  if (verso) spreads.push([verso, null]);
  const density = Math.min(window.devicePixelRatio || 1, 2);
  showPages.painter?.disconnect();
  const painter = new IntersectionObserver((entries) => {
    for (const { isIntersecting, target } of entries) {
      if (!isIntersecting) continue;
      painter.unobserve(target);
      const { doc, page } = target.postext;
      renderPageToCanvas(page, doc, target, { ...render, scale: (width * density) / page.width });
    }
  }, { rootMargin: '800px' });
  showPages.painter = painter;
  root.replaceChildren(...spreads.map((pair) => {
    const spread = document.createElement('div');
    spread.className = 'pt-spread';
    for (const p of pair) {
      const figure = document.createElement('figure');
      if (p) {
        const label = p.page.pageLabel || String(p.n + 1);
        const canvas = document.createElement('canvas');
        canvas.postext = p;
        canvas.style.aspectRatio = `${p.page.width} / ${p.page.height}`;
        canvas.setAttribute('role', 'img');
        canvas.setAttribute('aria-label', `Page ${label}`);
        const folio = document.createElement('figcaption');
        folio.textContent = label;
        figure.append(canvas, folio);
        painter.observe(canvas);
      } else figure.className = 'pt-blank';
      spread.append(figure);
    }
    return spread;
  }));
  kitStatus(`${pages.length} ${pages.length === 1 ? 'page' : 'pages'}`);
  document.documentElement.dataset.postext = 'ready';
  return pages.length;
}

/** The desk, the bar and the error reporting, created once. */
function viewer(title) {
  if (!document.getElementById('pt-kit')) {
    document.head.insertAdjacentHTML('beforeend', `<style id="pt-kit">
      :root { color-scheme: dark; }
      body { margin: 0; background: #0e1014; color: #b9bcc4; font: 13px/1.45 system-ui, sans-serif; }
      #pt-bar { position: sticky; top: 0; z-index: 1; display: flex; flex-wrap: wrap; align-items: center;
        gap: 6px 16px; padding: 10px 16px; background: rgb(14 16 20 / .92); backdrop-filter: blur(6px);
        border-bottom: 1px solid #23262d; }
      #pt-bar strong { color: #f4f1ea; font-weight: 600; }
      #pt-actions { display: flex; gap: 12px; margin-left: auto; }
      #pt-actions a, #pt-actions button { color: #d8a21a; font: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
      #pages { display: grid; justify-items: center; gap: 48px; padding: 32px 16px 72px; }
      .pt-spread { display: flex; }
      .pt-spread figure { margin: 0; width: min(460px, 44vw); }
      .pt-spread canvas { display: block; width: 100%; background: #fff;
        box-shadow: 0 1px 2px rgb(0 0 0 / .5), 0 22px 44px -16px rgb(0 0 0 / .8); }
      .pt-spread figure:first-child canvas { box-shadow: inset -14px 0 14px -14px rgb(0 0 0 / .18), 0 1px 2px rgb(0 0 0 / .5), 0 22px 44px -16px rgb(0 0 0 / .8); }
      .pt-spread figcaption { margin-top: 10px; text-align: center; font: 600 10px/1 system-ui, sans-serif;
        letter-spacing: .18em; text-transform: uppercase; color: #6c7079; }
      .pt-blank { visibility: hidden; }
      @media (max-width: 760px) {
        .pt-spread { flex-direction: column; gap: 32px; }
        .pt-spread figure { width: min(460px, 92vw); }
        .pt-blank { display: none; }
      }
    </style>`);
    document.body.insertAdjacentHTML('afterbegin',
      '<header id="pt-bar"><strong id="pt-title"></strong><span id="pt-status" role="status"></span><span id="pt-actions"></span></header>');
    document.getElementById('pt-title').textContent = document.title || 'Postext';
    addEventListener('error', (event) => kitFail(event.error ?? event.message));
    addEventListener('unhandledrejection', (event) => kitFail(event.reason));
  }
  if (title) document.getElementById('pt-title').textContent = title;
  return document.getElementById('pages')
    ?? document.body.appendChild(Object.assign(document.createElement('main'), { id: 'pages' }));
}

function kitStatus(text) {
  viewer();
  document.getElementById('pt-status').textContent = text;
}

function kitFail(error) {
  document.documentElement.dataset.postext = 'error';
  kitStatus(`Error: ${error?.message ?? error}`);
}

// ─── Kit · pdf v3 ── the same in every recipe that exports a PDF
/** The Fontsource files the screen used, as TrueType, with their range and
 *  place: the nearest weight the family ships, upright with no italic;
 *  latin, then what its letters need (kitSubsetsFor). */
async function fontsourceProvider(family, weight, style, request) {
  const id = fontsourceId(family);
  const meta = await fontsourceMeta(family);
  const weights = meta?.weights?.length ? meta.weights : [400, 700];
  const w = weights.reduce((a, b) => (Math.abs(b - weight) < Math.abs(a - weight) ? b : a));
  const s = style === 'italic' && meta && !meta.styles.includes('italic') ? 'normal' : style;
  const text = String.fromCodePoint(...(request?.codePoints ?? []));
  const files = await Promise.all(['latin', ...kitSubsetsFor(text, meta)].map(async (subset, order) => {
    const res = await fetch(`${await fontsourceAt(id)}files/${id}-${subset}-${w}-${s}.woff2`);
    if (!res.ok) throw new Error(`Fontsource has no ${family} ${w} ${s} ${subset}`);
    return Object.assign(await decompressWoff2(new Uint8Array(await res.arrayBuffer())),
      { unicodeRange: kitRanges()[subset], order });
  }));
  return files.length === 1 ? files[0] : files;
}

/** A "Build the PDF" button; then "Open the PDF" (a new tab: CodePen's frame
 *  shows no PDFs) and a download link. */
function offerPdf(makePdf, filename) {
  viewer();
  const button = Object.assign(document.createElement('button'), { type: 'button', textContent: 'Build the PDF' });
  button.dataset.postextPdf = filename;
  button.addEventListener('click', async () => {
    button.disabled = true;
    button.textContent = 'Building the PDF…';
    try {
      const bytes = await makePdf();
      const url = URL.createObjectURL(new Blob([bytes], { type: 'application/pdf' }));
      const size = `${Math.max(1, Math.round(bytes.length / 1024))} KB`;
      button.replaceWith(
        Object.assign(document.createElement('a'), { href: url, target: '_blank', rel: 'noopener', textContent: 'Open the PDF ↗' }),
        Object.assign(document.createElement('a'), { href: url, download: filename, textContent: `Download ${filename} · ${size}` }));
    } catch (error) {
      button.disabled = false;
      button.textContent = 'Build the PDF';
      kitFail(error);
    }
  });
  document.getElementById('pt-actions').append(button);
}

// ─── /Kit ───────────────────────────────────────────────────────────────────────
````

## Variantes

### Numéroter toutes les mesures

Un apparat chargé se suit mieux avec un numéro sur chaque mesure.

```diff
-  barNumbers: 'system', // the apparatus is keyed by bar: number every system
+  barNumbers: 'every',
```

### Graver les exemples en Bravura

Les têtes de note plus épaisses de Bravura s’accordent à un style de gravure sur cuivre.

```diff
-  musicFont: 'Leipzig', // lighter than Bravura, close to a 19th-century plate
+  musicFont: 'Bravura',
```

## Erreurs fréquentes

- MEI 5 écrit l’armure `keysig="1f"` sur `scoreDef` ou `staffDef`. L’attribut `key.sig` de MEI 4 est ignoré sans message, et le bémol disparaît.
- Donnez à chaque `staffDef` son `lines="5"` : sans lui, notes et clés sont gravées sans portée.
- Le graveur imprime le `<lem>` d’un `<app>`, pas le `<rdg>`. Une variante que le lecteur doit voir sur la page va dans la note d’apparat ou dans un second exemple.

## Crédits

- Recette: Ignacio Ferro ([@drnachio](https://github.com/drnachio))
- Texte: The chapter text and the apparatus notes, in English and Spanish: Ignacio Ferro, CC-BY-4.0
- Texte: Sumer is icumen in (13th century), the music examples transcribed from W. S. Rockstro's score and the readings his article reports, in A Dictionary of Music and Musicians, ed. George Grove, vol. 3, pp. 765–768, and vol. 4, pp. 1–3 (Macmillan, 1900 printing), as transcribed on Wikisource: Anonymous; William Smyth Rockstro ([source](https://en.wikisource.org/wiki/A_Dictionary_of_Music_and_Musicians/Sumer_is_icumen_in)), domaine public
- Texte: The Latin rubric of Harley MS 978, f. 11v, and the folio, as quoted in the Wikipedia article (fact check): Wikipedia contributors ([source](https://en.wikipedia.org/wiki/Sumer_is_icumen_in)), CC-BY-SA-4.0
- Caractères: Crimson Pro (OFL-1.1), Cormorant Garamond (OFL-1.1), Source Sans 3 (OFL-1.1)
- Code: MIT · Contenu d’exemple: CC-BY-4.0

## Recettes liées

- [Nº 071 · Des notes au pied de la colonne qui les appelle](https://postext.dev/fr/cookbook/novel-footnotes-column-foot.md): Les appels Markdown [^id] deviennent des notes numérotées au pied de la colonne qui les cite ; options : une colonne, fin de chapitre, numéros continus. · Niveau 2 (Intermédiaire) · Fiction, théâtre et prose littéraire
- [Nº 044 · Édition critique : numéros de vers et notes associées](https://postext.dev/fr/cookbook/critical-edition-line-numbers.md): Le Lycidas de Milton (texte de 1645) : lineNumbers numérote un vers sur cinq dans la colonne latérale, et chaque note s’ouvre sur ce numéro en pastille. · Niveau 3 (Avancé) · Poésie
- [Nº 163 · Un manuel d’harmonie aux exemples musicaux en ABC](https://postext.dev/fr/cookbook/harmony-textbook-abc-examples.md): Un chapitre sur les cadences dont les exemples sont des blocs ABC dans le Markdown, gravés à la largeur de la colonne ; l’un d’eux continue sur deux pages. · Niveau 2 (Intermédiaire) · Manuels scolaires
