En poques paraules
Una edició breu dels comptes domèstics que Thoreau va publicar a Walden. Les xifres del text són petites com les minúscules, els anys dels títols fan l'alçada de les majúscules i les sumes dels comptes s'alineen en columna.
Què compondràs
Una edició breu dels comptes que Thoreau va publicar al primer capítol de Walden: el que li va costar la casa, el que li va donar el camp de mongetes, el que va menjar en vuit mesos. Va en una pàgina de 140 × 216 mm amb la família Alegreya, i l'obertura porta el gravat de 1854 de la cabana. Les xifres canvien segons la feina que fan. A la prosa, març de 1845 i $13,34 van en xifres elzevirianes, que pugen i baixen com les minúscules, i les dates no destaquen a la línia. Els títols van en versaletes, i els seus anys fan l'alçada de les majúscules. Als comptes totes les xifres tenen la mateixa amplada, de manera que els cèntims de tretze línies de taules, llistons i claus queden l'un sota l'altre fins al total.
Aquesta recepta respon a
- Com componc xifres elzevirianes al text, xifres de caixa alta als títols i xifres tabulars a les taules?
- Com componc il·lustracions sense numerar: ornaments, vinyetes, logotips?
La resposta curta
// 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),
};
Ingredients
- Funcions
- Estils de xifresEstil de taulesTaules a partir de dadesTipus de recurs propisCol·locació de figuresObertures dissenyadesImatges als dissenys de pàginaAtributs de títolCapçaleres per seccióCapçaleres i folisMarges simètricsEstils de paràgrafPaleta de color semànticaExportació a PDFFonts incrustades al PDF
- També fa servir
- Figures just aquíFigura i Taula en la teva llenguaCapçaleres segons el tipus de pàginaFigures i taules com a recursos
- Tipografia
- Alegreya, Alegreya SC, Alegreya Sans (SIL OFL 1.1)
- Recursos
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), domini públic)
Elaboració
#1 · Un estil de xifres per estil de text
El codi d'aquest pas és la resposta curta, més amunt. figureStyle demana a la font d'un estil les seves xifres OpenType (estils de xifres). El cos no demana res, perquè les xifres pròpies d'Alegreya ja són elzevirianes i proporcionals: l'1 fa 342 unitats i el 0, 533. Els títols de segon nivell demanen a Alegreya SC 'lining', les seves xifres lnum, que fan l'alçada de les majúscules que les envolten. L'estil de taula demana 'tabular', el tnum de la font: les xifres conserven la forma elzeviriana i prenen totes la mateixa amplada, 450 unitats, així que una columna de preus es llegeix de dalt a baix. Amb 'lining-tabular' anirien a l'alçada de les majúscules (mira «Variants»).
#2 · Els comptes són taules de cinc columnes
// 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
Tablas, $ 8,03 ½ casi todas de chabolas.
Tablillas de desecho para tejado y paredes, 4,00
Listones, 1,25
Dos ventanas de segunda mano con cristal, 2,43
Mil ladrillos viejos, 4,00
Dos barriles de cal, 2,40 Eso fue caro.
Pelo, 0,31 Más del que necesitaba.
Hierro para la repisa, 0,15
Clavos, 3,90
Bisagras y tornillos, 0,14
Pestillo, 0,10
Tiza, 0,01
Transporte, 1,40 Buena parte la llevé a cuestas.
=En total, $ 28,12 ½
=== farm
$ 23,44
Descontando los gastos, 14,72 ½
=Quedan, $ 8,71 ½
=== food
Arroz, $ 1,73 ½
Melaza, 1,73 La forma más barata del azúcar.
Harina de centeno, 1,04 ¾
Harina de maíz, 0,99 ¾ Más barata que la de centeno.
Cerdo, 0,22
+Todos los experimentos que fallaron:
Harina de trigo, 0,88 Cuesta más que la de maíz, en dinero y en trabajo.
Azúcar, 0,80
Manteca, 0,65
Manzanas, 0,25
Manzana seca, 0,22
Boniatos, 0,10
Una calabaza, 0,06
Una sandía, 0,02
Sal, 0,03
=== outgoes
Casa, $ 28,12 ½
Granja, un año, 14,72 ½
Comida, ocho meses, 8,74
Ropa, etc., ocho meses, 8,40 ¾
Aceite, etc., ocho meses, 2,00
=En total, $ 61,99 ¾
=== income
$ 23,44
Ganado como jornalero, 13,34
=En total, $ 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'))]));
Els comptes de Thoreau es guarden com a TSV a content.accounts.es.md, un bloc per compte, de manera que l'edició castellana tradueix les partides i canvia el punt decimal per la coma sense tocar el codi. Cada fila porta la partida, el signe del dòlar, l'import, una fracció i una observació. Els mitjos i els quarts tenen columna pròpia: ½ fa 713 unitats i ¾, 689, més que una xifra tabular, i Alegreya no té espai de xifra per omplir els altres imports. Un total està fet de cel·les de capçalera, així que groupRules hi traça un filet fi a sobre, com l'impressor de Thoreau traçava una línia abans d'In all. El signe del dòlar s'escriu \$, perquè un $ sol obre una fórmula en una cel·la, igual que al text. El tipus de recurs account no porta número ni prefix de peu, i la seva col·locació per defecte és 'here', així que cada compte va on el presenta la frase de Thoreau.
#3 · L'obertura declara les seves xifres
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 text de disseny fixa la seva pròpia família, així que les xifres del cos no hi arriben. Concord, 1845–1847 va en Alegreya SC i demana 'lining' pel seu compte, com els títols. La vinyeta és el gravat de la portada de 1854, retallat i ajustat a negre sobre blanc; l'avanttítol, el títol i les dates prenen la mesura de la caixa i se centren a sota.
#4 · Capçaleres amb la secció a la pàgina senar
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 pàgina parella porta el títol del llibre, i la senar, la secció en curs, {firstMark.h2}, de manera que la pàgina 3 diu El campo de judías, 1845. Els folis van en Alegreya i conserven les xifres elzevirianes.

La recepta completa
// ═══ 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 = 'es'; // @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.
L'script.js compost funciona tal com és: enganxa'l com a script de mòdul en qualsevol pàgina o obre la recepta a CodePen. Carpeta de la recepta a GitHub ↗ (s'obre en una pestanya nova)
Variants
#Compon els comptes en xifres de caixa alta
Un llibre de comptes o una llista de preus d'un llibre modern sol portar les xifres a l'alçada de les majúscules.
- figureStyle: 'tabular', bodyFontFamily: TEXT, bodyFontSize: pt(9.8), bodyColor: col('ink'),
+ figureStyle: 'lining-tabular', bodyFontFamily: TEXT, bodyFontSize: pt(9.8), bodyColor: col('ink'),#Compon els folis en xifres de caixa alta
Els folis en xifres de caixa alta casen amb capçaleres en majúscules.
-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',Errors freqüents
- Els fitxers latin que serveix Fontsource sovint deixen fora
onum: Lora, EB Garamond, Source Serif 4, Crimson Pro, Newsreader i Literata només conservenpnumitnum. Amb aquests fitxers,figureStyle: 'oldstyle'no canvia res i el text continua amb xifres de caixa alta. Tria una lletra amb xifres pròpies elzevirianes, com Alegreya, Gelasio o Cormorant Garamond, que afegeixenlnumper als títols, o carrega els fitxers complets de la foneria. - Un
$al Markdown o en una cel·la obre una fórmula en línia. Escriu\$14,72½al text i\$en una cel·la.
Crèdits
- Recepta
- Ignacio Ferro
- Text
- 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 · domini públic
- The headnote, and the Spanish translation of the passages and the accounts · Ignacio Ferro · CC BY 4.0
- Imatges
- 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) · domini públic
- Fonts
- Alegreya (SIL OFL 1.1) · Alegreya SC (SIL OFL 1.1) · Alegreya Sans (SIL OFL 1.1)
- Codi
- MIT, com Postext
Editar aquest text ↗ (s'obre en una pestanya nova)Carpeta de la recepta a GitHub ↗ (s'obre en una pestanya nova)


