Aller au contenu principal
Recette numéro 176

Recettes · Chapitre 2 · Caractères et texte

Chiffres elzéviriens, alignés et tabulaires

Les comptes de Walden : figureStyle met des chiffres alignés dans les titres en petites capitales, tabulaires dans les comptes ; la prose reste elzévirienne.

Sur cette page
  • Exemple en anglais : pas encore d’édition française
  • Format 140 × 216 mm
  • 1 colonne
  • Alegreya 10,8/14,5
  • Alegreya SC
  • Alegreya SC@lnum
  • Alegreya Sans
  • Alegreya@tnum
  • 6 pages
  • Niveau
  • Postext 1.65.0
  • Mis en page en 124 ms
  • 160 lignes de code

En bref

Une petite édition des comptes domestiques que Thoreau a publiés dans Walden. Les chiffres du texte sont petits comme des minuscules, les années des titres ont la hauteur des capitales et les sommes des comptes s'alignent en colonne.

Ce que vous allez composer

Une petite édition des comptes que Thoreau a publiés dans le premier chapitre de Walden : ce que lui a coûté sa maison, ce que lui a rapporté le champ de haricots, ce qu'il a mangé en huit mois. Elle est composée sur une page de 140 × 216 mm dans la famille Alegreya, avec la gravure de 1854 de la cabane en ouverture. Les chiffres changent selon leur rôle. Dans la prose, March, 1845 et $13.34 sont en chiffres elzéviriens, qui montent et descendent comme des minuscules, si bien que les dates ne sortent pas de la ligne. Les titres sont en petites capitales, et leurs années ont la hauteur des capitales. Dans les comptes, tous les chiffres ont la même largeur : les cents de treize lignes de planches, de lattes et de clous tombent les uns sous les autres jusqu'au total.

Cette recette répond à

  • Comment composer des chiffres elzéviriens dans le texte, des chiffres alignés dans les titres et des chiffres tabulaires dans les tableaux ?
  • Comment composer des illustrations non numérotées : ornements, vignettes, logos ?

La réponse courte

script.js · lignes 34–55dans le code complet
// Alegreya's own figures are old-style, so the prose asks for none: the cut Fontsource
// serves has no onum, and 'oldstyle' would change nothing (see the write-up).
const bodyText = {
  fontFamily: TEXT, fontSize: pt(10.8), 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,
};
// Small capitals take lining figures (lnum), at the height of the capitals around them.
const section = { level: 2, fontFamily: CAPS, fontSize: pt(12.5), lineHeight: pt(LEAD),
  fontWeight: 400, letterSpacing: pt(0.6), color: col('accent'), figureStyle: 'lining',
  marginTop: pt(LEAD), marginBottom: pt(0) };
// The accounts: every figure on one width (tnum), so the cents stand in columns.
// 'tabular' keeps the font's form, old-style; 'lining-tabular' would set them at cap height.
const tableStyle = {
  figureStyle: 'tabular', bodyFontFamily: TEXT, bodyFontSize: pt(9.8), bodyColor: col('ink'),
  rules: 'booktabs', groupRules: true, borderColor: col('rule'), lightRuleWidth: pt(0.4),
  heavyRuleWidth: pt(0.7), headerBackgroundEnabled: false, headerFontFamily: TEXT,
  headerFontSize: pt(9.8), headerColor: col('ink'), headerBold: false, headerItalic: false,
  cellPadding: mm(0.7),
};

Ingrédients

Typographie
Alegreya, Alegreya SC, Alegreya Sans (SIL OFL 1.1)
Ressources
  • cabin-860.jpg
  • The vignette of Thoreau’s cabin from the title page of the first edition (1854), cropped and levelled to black on white (Ticknor and Fields, Boston, 1854 (engraver unknown), domaine public)

Préparation

#1 · Un style de chiffres par style de texte

Le code de cette étape est la réponse courte, plus haut. figureStyle demande à la police d'un style ses chiffres OpenType (styles de chiffres). Le corps ne demande rien, car les chiffres propres d'Alegreya sont déjà elzéviriens et proportionnels : le 1 fait 342 unités et le 0, 533. Les titres de niveau 2 demandent à Alegreya SC 'lining', ses chiffres lnum, qui ont la hauteur des capitales qui les entourent. Le style de tableau demande 'tabular', le tnum de la police : les chiffres gardent leur forme elzévirienne et prennent tous la même largeur, 450 unités, de sorte qu'une colonne de prix se lit de haut en bas. Avec 'lining-tabular', ils seraient à la hauteur des capitales (voir « Variantes »).

#2 · Les comptes sont des tableaux à cinq colonnes

script.js · lignes 59–122dans le code complet
// A row starting '=' is a total (header cells: a light rule above it), '+' heads a group.
const C = (content, align = 'left', extra = {}) => ({ content, align, ...extra });
function account(tsv) {
  const rows = tsv.replace(/^\n+|\n+$/g, '').split('\n').map((line) => {
    const total = line.startsWith('='), group = line.startsWith('+');
    const [item, dollar, amount, fraction, remark] = line.replace(/^[=+]/, '').split('\t');
    if (group) return [C(item, 'left', { colSpan: 5, isHeader: true }),
      ...[1, 2, 3, 4].map(() => C('', 'left', { hiddenBy: { row: -1, col: 0 } }))];
    const h = { isHeader: total };
    return [C(item, 'left', h), C(dollar ? '\\$' : '', 'right', h), // `$` alone opens maths
      C(amount ?? '', 'right', h), C(fraction ?? '', 'left', h), C(remark ?? '', 'left', h)];
  });
  rows.forEach((row, r) => row.forEach((cell) => cell.hiddenBy && (cell.hiddenBy.row = r)));
  return { rows, headerRowCount: 0, columnWidths: [34, 2.4, 8, 3.6, 35] };
}
const accounts = String.raw`=== house
Boards,	$	8.03	½	mostly shanty boards.
Refuse shingles for roof sides,		4.00
Laths,		1.25
Two second-hand windows with glass,		2.43
One thousand old brick,		4.00
Two casks of lime,		2.40		That was high.
Hair,		0.31		More than I needed.
Mantle-tree iron,		0.15
Nails,		3.90
Hinges and screws,		0.14
Latch,		0.10
Chalk,		0.01
Transportation,		1.40		I carried a good part on my back.
=In all,	$	28.12	½
=== farm
	$	23.44
Deducting the outgoes,		14.72	½
=There are left,	$	8.71	½
=== food
Rice,	$	1.73	½
Molasses,		1.73		Cheapest form of the saccharine.
Rye meal,		1.04	¾
Indian meal,		0.99	¾	Cheaper than rye.
Pork,		0.22
+All experiments which failed:
Flour,		0.88		Costs more than Indian meal, both money and trouble.
Sugar,		0.80
Lard,		0.65
Apples,		0.25
Dried apple,		0.22
Sweet potatoes,		0.10
One pumpkin,		0.06
One watermelon,		0.02
Salt,		0.03
=== outgoes
House,	$	28.12	½
Farm one year,		14.72	½
Food eight months,		8.74
Clothing, etc., eight months,		8.40	¾
Oil, &c., eight months,		2.00
=In all,	$	61.99	¾
=== income
	$	23.44
Earned by day-labor,		13.34
=In all,	$	36.78
`; // content.accounts.<lang>.md: one block per table
const tables = Object.fromEntries(accounts.split(/^=== /m).filter(Boolean)
  .map((block) => [block.slice(0, block.indexOf('\n')).trim(), block.slice(block.indexOf('\n'))]));

Les comptes de Thoreau sont gardés en TSV dans content.accounts.en.md, un bloc par compte, et l'édition espagnole traduit les postes et remplace le point décimal par la virgule sans toucher au code. Chaque ligne porte le poste, le signe du dollar, le montant, une fraction et une remarque. Les demis et les quarts ont leur propre colonne : ½ fait 713 unités et ¾, 689, plus qu'un chiffre tabulaire, et Alegreya n'a pas d'espace de chiffre pour compléter les autres montants. Un total est fait de cellules d'en-tête, si bien que groupRules trace un filet fin au-dessus, comme l'imprimeur de Thoreau traçait une ligne avant In all. Le signe du dollar s'écrit \$, car un $ seul ouvre une formule dans une cellule comme dans le texte. Le type de ressource account n'a ni numéro ni préfixe de légende, et son placement par défaut est 'here' : chaque compte se place là où la phrase de Thoreau l'annonce.

#3 · L'ouverture déclare ses propres chiffres

script.js · lignes 126–144dans le code complet
const at = (to, edge, y, x = 0) => ({ anchor: { to, edge }, offset: { x: mm(x), y: mm(y) } });
// 'below' starts a text at the left edge of what it hangs from: the texts take the measure
// and start half the difference to the left of the vignette, so they centre on the page.
const centred = (to, y) => ({ ...at(to, 'below', y, (VIGNETTE - MEASURE) / 2),
  size: { width: mm(MEASURE) } });
const opener = { enabled: true, minHeight: pt(19 * LEAD), slot: { elements: [
  { kind: 'image', id: 'cabin', resourceId: 'cabin',
    placement: { ...at('container', 'top', 0), size: { width: mm(VIGNETTE) } } },
  { kind: 'text', id: 'kicker', content: '{attr.kicker}', fontFamily: LABEL, fontWeight: 500,
    fontSize: pt(7.5), letterSpacing: pt(1.5), textTransform: 'uppercase', color: col('accent'),
    align: 'center', placement: centred('#cabin', 6) },
  { kind: 'text', id: 'title', content: '{titleText}', fontFamily: CAPS, fontSize: pt(30),
    letterSpacing: pt(1.2), lineHeight: 1, color: col('ink'), align: 'center',
    placement: { ...at('#kicker', 'below', 2.5), size: { width: mm(MEASURE) } } },
  // A design text names its own family, so it states its figures too.
  { kind: 'text', id: 'dates', content: '{attr.dates}', fontFamily: CAPS, fontSize: pt(11),
    letterSpacing: pt(0.8), color: col('accent'), align: 'center', figureStyle: 'lining',
    placement: { ...at('#title', 'below', 2.5), size: { width: mm(MEASURE) } } },
] } };

Un texte de maquette fixe sa propre famille, donc les chiffres du corps ne l'atteignent pas. Concord, 1845–1847 est en Alegreya SC et demande 'lining' lui-même, comme les titres. La vignette est la gravure de la page de titre de 1854, recadrée et ramenée au noir sur blanc ; le surtitre, le titre et les dates prennent la justification et se centrent dessous.

#4 · Des titres courants avec la section sur la belle page

script.js · lignes 148–161dans le code complet
const head = (id, content, parity, edge, x, extra = {}) => ({
  kind: 'text', id, content, parity, pages: 'body', fontFamily: LABEL, fontSize: pt(7.5),
  fontWeight: 500, letterSpacing: pt(1.2), textTransform: 'uppercase', color: col('muted'),
  placement: { anchor: { to: 'page', edge }, offset: { x: mm(x), y: mm(13) } }, ...extra });
const folio = { fontFamily: TEXT, fontSize: pt(9.5), fontWeight: 400, letterSpacing: pt(0),
  color: col('ink') };
const header = { elements: [
  head('verso-folio', '{pageNumber}', 'even', 'top-left', OUTER, folio),
  head('verso-title', '{title}', 'even', 'top-left', OUTER + 8),
  head('recto-title', '{firstMark.h2}', 'odd', 'top-right', -(OUTER + 8)),
  head('recto-folio', '{pageNumber}', 'odd', 'top-right', -OUTER, folio),
] };
const footer = { elements: [head('drop-folio', '{pageNumber}', 'all', 'top', 0, {
  ...folio, pages: 'opener', placement: at('container', 'top', 8) })] };

La fausse page porte le titre du livre, et la belle page la section en cours, {firstMark.h2} : la page 3 indique The Bean-Field, 1845. Les folios sont en Alegreya et gardent ses chiffres elzéviriens.

Page 2 : les chiffres elzéviriens du compte de la maison ont tous la même largeur, et les cents s'alignent jusqu'au total.

La recette complète

Sandbox
// ═══ Postext Cookbook · Nº 176 · Old-style, lining and tabular figures ═══════════════
// https://postext.dev/en/cookbook/old-style-and-tabular-figures
// Code: MIT · Text: H. D. Thoreau, Walden (1854, public domain) · Vignette: 1854 title page (PD)
// Fonts: Alegreya, Alegreya SC, Alegreya Sans (SIL OFL 1.1) · Needs postext ≥ 1.65.0
// Thoreau's accounts from Walden. Each text style asks Alegreya for one kind of figures:
// old-style in the prose, lining in the small-capital headings, tabular in the accounts.
import {
  buildDocumentWithFonts, renderPageToCanvas, registerResourceImage, defaultResourceTypes,
} 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 = 'old-style-and-tabular-figures';

// ─── 1 · Design ─────────────────────────────────────────────────────────────
const palette = {
  ink: '#221e1a', // text: a warm near-black
  accent: '#7d3a1f', // walnut: headings, kicker, rules of the opener
  rule: '#8a7f73', // the light rules of the accounts
  muted: '#6b6158', // running heads, the colophon
  paper: '#ffffff',
};
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' } })),
  { id: 'main-color', name: 'accent (defaults)', value: { hex: palette.accent, model: 'hex' } },
];
const TEXT = 'Alegreya', CAPS = 'Alegreya SC', LABEL = 'Alegreya Sans';
const TRIM = [140, 216], INNER = 19, OUTER = 16; // mm: a trade paperback
const MEASURE = TRIM[0] - INNER - OUTER, VIGNETTE = 50; // mm
const LEAD = 14.5; // pt: the body leading, the grid the headings keep to

// #region answer: one figure style per text style
// Alegreya's own figures are old-style, so the prose asks for none: the cut Fontsource
// serves has no onum, and 'oldstyle' would change nothing (see the write-up).
const bodyText = {
  fontFamily: TEXT, fontSize: pt(10.8), 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,
};
// Small capitals take lining figures (lnum), at the height of the capitals around them.
const section = { level: 2, fontFamily: CAPS, fontSize: pt(12.5), lineHeight: pt(LEAD),
  fontWeight: 400, letterSpacing: pt(0.6), color: col('accent'), figureStyle: 'lining',
  marginTop: pt(LEAD), marginBottom: pt(0) };
// The accounts: every figure on one width (tnum), so the cents stand in columns.
// 'tabular' keeps the font's form, old-style; 'lining-tabular' would set them at cap height.

Le script.js composé s’exécute tel quel : collez-le dans le script de module de n’importe quelle page, ou ouvrez la recette sur CodePen. Dossier de la recette sur GitHub ↗ (s’ouvre dans un nouvel onglet)

Variantes

#Composer les comptes en chiffres alignés

Un livre de comptes ou une liste de prix d'un livre moderne met souvent ses chiffres à la hauteur des capitales.

-  figureStyle: 'tabular', bodyFontFamily: TEXT, bodyFontSize: pt(9.8), bodyColor: col('ink'),
+  figureStyle: 'lining-tabular', bodyFontFamily: TEXT, bodyFontSize: pt(9.8), bodyColor: col('ink'),

#Composer les folios en chiffres alignés

Des folios en chiffres alignés vont avec des titres courants en capitales.

-const folio = { fontFamily: TEXT, fontSize: pt(9.5), fontWeight: 400, letterSpacing: pt(0),
+const folio = { fontFamily: TEXT, fontSize: pt(9.5), fontWeight: 400, letterSpacing: pt(0),
+  figureStyle: 'lining',

Erreurs fréquentes

  • Les fichiers latin que sert Fontsource laissent souvent onum de côté : Lora, EB Garamond, Source Serif 4, Crimson Pro, Newsreader et Literata ne gardent que pnum et tnum. Avec ces fichiers, figureStyle: 'oldstyle' ne change rien et le texte garde des chiffres alignés. Choisissez une police dont les chiffres propres sont elzéviriens, comme Alegreya, Gelasio ou Cormorant Garamond, qui ajoutent lnum pour les titres, ou chargez les fichiers complets de la fonderie.
  • Un $ dans le Markdown ou dans une cellule ouvre une formule en ligne. Écrivez \$14.72½ dans le texte et \$ dans une cellule.

Crédits

Texte
  • The accounts and the passages around them from “Economy,” the first chapter of Walden; or, Life in the Woods (Boston: Ticknor and Fields, 1854) · Henry D. Thoreau · domaine public
  • The headnote, and the Spanish translation of the passages and the accounts · Ignacio Ferro · CC BY 4.0
Images
Polices
Alegreya (SIL OFL 1.1) · Alegreya SC (SIL OFL 1.1) · Alegreya Sans (SIL OFL 1.1)
SandboxPDF