Zum Hauptinhalt springen
Rezept Nummer 5

Rezepte · Kapitel 4 · Kolumnentitel und Paginas

Kolumnentitel nach Seitenparität in einem Essayband

Kopfzeilenelemente nach Parität und Seitenrolle: Buchtitel links, gekürzter Essaytitel rechts, Paginamarken am Rand, Pagina am Fuß der Auftaktseiten.

Auf dieser Seite
Ausgabe
Canvas
Stufe
Mittel
Postext
Getestet mit Postext 1.31.0
Erfordert ≥ 1.25.0
Lizenz
Aktualisiert am 10. Okt. 2026
Code MIT · Text MIT

S. 30–31 · 2–3 von 5

  • Englisches Beispiel: noch keine deutsche Ausgabe
  • Endformat 132 × 198 mm
  • 1 Spalte
  • Baskervville 10,2/14
  • Alegreya SC
  • Libre Caslon Display
  • 5 Seiten
  • Stufe
  • Postext 1.31.0
  • In 128 ms gesetzt
  • 174 Zeilen Code

Kurz gesagt

Fünf Seiten aus einem kleinen Essayband. Sie zeigen, wie man den Titel des Buchs oben auf die linken Seiten und den Titel des Essays auf die rechten Seiten druckt.

Was Sie setzen

Die Seiten 29 bis 33 einer Taschenausgabe von Montaignes Essays in der englischen Übersetzung von Charles Cotton: das ganze Kapitel IV und der Anfang von Kapitel V. Die linken Seiten tragen den Titel des Buchs in gesperrten Versalien. Die rechten Seiten tragen den Titel des Essays kursiv, mit Auslassungspunkten gekürzt, da der Titel von Kapitel IV 81 Zeichen lang ist. Jede Pagina sitzt in einer sepiafarbenen Marke am Außensteg, 3 mm von einer Linie von 0,5 pt an der Kante des Satzspiegels. Eine Auftaktseite hat keinen Kolumnentitel, nur eine Pagina am Fuß. Der Text steht in Baskervville auf einer warm gebrochen weißen Seite, die Titel in Libre Caslon Display, die Paginas und der Buchtitel in Alegreya SC. Jeder Kolumnentitel ist in der Konfiguration festgelegt, sodass der eingebaute Kolumnentitel der Engine, blaue Open Sans zu 8 pt, nie gedruckt wird.

Dieses Rezept beantwortet

  • Wie setze ich Kolumnentitel: Buchtitel auf der linken Seite, Kapiteltitel auf der rechten, Seitenzahl außen?
  • Wie blende ich Kolumnentitel auf Auftakten und Vakatseiten aus oder färbe eine leere linke Seite in der Teilfarbe?
  • Wie verhindere ich, dass Überschriften, fette Wörter und Aufzählungszeichen blau erscheinen?

Die kurze Antwort

script.js · Zeilen 67–95im vollständigen Code
const GAP = 3; // mm from the tab to the divider, which lands on the text block's edge
const HEAD = 12; // mm from the top edge of the page to the top of the tab
const CAP = t({ en: 60.5, es: 66.8 }); // mm: the head's widest line, set per edition so
// essay IV's is cut after 'false' and 'objetos'; a short running title suits any title
const page = (edge) => ({ to: 'page', edge }); // the trim box (gotcha: negative-offsets)
const header = { elements: [
  // Verso (even): tab | divider | THE BOOK'S TITLE from the frontmatter
  folio({ id: 'folio-even', parity: 'even', pages: 'body', anchor: page('top-left'),
    x: OUTER - GAP - TAB, y: HEAD }),
  divider({ id: 'rule-even', parity: 'even', from: 'folio-even', edge: 'right-of', x: GAP }),
  { kind: 'text', id: 'book', content: '{title}', parity: 'even', pages: 'body',
    fontFamily: 'Alegreya SC', fontSize: pt(FOLIO_PT), lineHeight: LINE / FOLIO_PT,
    color: col('muted'), textTransform: 'uppercase', letterSpacing: pt(1.3),
    placement: { anchor: { to: '#rule-even', edge: 'right-of' }, // top: the tab's top
      offset: { x: mm(2.5), y: pt(PAD.top) } } }, // PAD.top down: on the folio's baseline
  // Recto (odd): the essay's title, cut short with an ellipsis | divider | tab.
  folio({ id: 'folio-odd', parity: 'odd', pages: 'body', anchor: page('top-right'),
    x: -(OUTER - GAP - TAB), y: HEAD }), // from the right edge, a negative x runs inwards
  divider({ id: 'rule-odd', parity: 'odd', from: 'folio-odd', edge: 'left-of', x: -GAP }),
  { kind: 'text', id: 'essay', content: '{chapterTitle}', parity: 'odd', pages: 'body',
    fontFamily: 'Baskervville', italic: true, fontSize: pt(TITLE_PT),
    lineHeight: LINE / TITLE_PT, color: col('muted'),
    overflow: 'ellipsis-end', // stated, though a running head's default
    placement: { anchor: { to: '#rule-odd', edge: 'left-of' },
      offset: { x: mm(-2.5), y: pt(PAD.top) }, size: { maxWidth: mm(CAP) } } },
] };
// Openers: no head, only a drop folio HEAD above the page's foot, centred under the text block.
const footer = { elements: [folio({ id: 'drop-folio', parity: 'all', pages: 'opener',
  anchor: { to: 'container', edge: 'bottom' }, y: -HEAD })] };

Zutaten

Schrift
Baskervville, Libre Caslon Display, Alegreya SC (SIL OFL 1.1)
Dateien
Keine: Jedes Bild wird im Code gezeichnet

Zubereitung

#1 · Den eingebauten blauen Kolumnentitel ersetzen

script.js · Zeilen 14–26im vollständigen Code
const palette = {
  ink: '#1f1b16', // text: a warm near-black, never #000
  sepia: '#8a5a2b', // the one accent: folio tabs, dividers, opener plates (5.3:1 on paper)
  muted: '#6f6558', // running heads, verse glosses, the colophon (5.2:1 on paper)
  paper: '#f8f4ec', // a warm off-white page; also the type reversed out of the sepia
};
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' } })),
  // Text-style defaults (headings, bold, italic, lists, boxes) link to 'main-color':
  // pointed at the sepia, none of them prints in the engine's blue.
  { id: 'main-color', name: 'sepia (defaults)', value: { hex: palette.sepia, model: 'hex' } },
];

Jede Farbe der Konfiguration ist mit einem dieser Einträge verknüpft. Lassen Sie header undefiniert, zeichnet die Engine ihren eingebauten Kolumnentitel: {title} auf rechten und {chapterTitle} auf linken Seiten über einer Linie von 1 pt, in Open Sans 600 zu 8 pt und im Blau #295AA3 (Kopf- und Fußzeilen). Richtet man main-color auf das Sepia, färbt das die Vorgaben des Texts und mit ihnen diesen Kolumnentitel um, doch Schrift, Linie und die Seite, auf der jeder Titel steht, bleiben die der Engine. Deshalb ist jeder Kolumnentitel hier vollständig ausgeschrieben, mit jeder Farbe an einen Paletteneintrag geknüpft. Auch Fett, Kursiv und Verweise nehmen standardmäßig main-color an, und bodyText setzt sie auf die Textfarbe zurück.

#2 · Jeden Kolumnentitel aus einer einzigen Marke bauen

script.js · Zeilen 31–48im vollständigen Code
const FOLIO_PT = 7.5; // pt: the folio, and the book's title in capitals beside it
const TITLE_PT = 8.8; // pt: the essay's title, whose italic lowercase reads small at 7.5
const LINE = FOLIO_PT * 1.2; // pt: the folio and both titles share this line box and baseline
const PAD = { top: 1, bottom: 1.4 }; // pt: 0.4 pt more below for the descending 3, 5, 7, 9
const TAB = 7.5; // mm: the tab's width
const TAB_H = LINE + PAD.top + PAD.bottom; // pt: the tab's height, which the divider matches
const folio = ({ id, parity, pages, anchor, x = 0, y }) => ({
  kind: 'text', id, content: '{pageNumber}', parity, pages, fontFamily: 'Alegreya SC',
  fontSize: pt(FOLIO_PT), fontWeight: 700, lineHeight: LINE / FOLIO_PT, color: col('paper'),
  box: { backgroundColor: col('sepia'), borderRadius: mm(2), // a pill; the number is centred
    padding: { top: pt(PAD.top), bottom: pt(PAD.bottom) } }, // by default in both directions
  placement: { anchor, offset: { x: mm(x), y: mm(y) }, size: { width: mm(TAB) } },
});
const divider = ({ id, parity, from, edge, x }) => ({
  kind: 'rule', id, parity, pages: 'body', direction: 'vertical', thickness: pt(0.5),
  color: col('sepia'), placement: { anchor: { to: `#${from}`, edge }, offset: { x: mm(x) },
    size: { height: pt(TAB_H) } },
});

Die kurze Antwort verankert die Marke jedes Kolumnentitels an 'page', sodass ihre Versätze ab dem Endformat gemessen werden. Von der Ecke top-right aus läuft ein positives x aus der Seite hinaus, deshalb nimmt die Marke der rechten Seite ein negatives. Die Trennlinie hängt an der Marke und der Titel an der Trennlinie (Platzierung von Elementen). Die Maße der Marke sind benannte Konstanten, weil die übrigen Teile von ihnen aus gemessen werden. Die Trennlinie ist TAB_H hoch. Pagina und beide Titel teilen sich eine Zeilenhöhe von 9 pt, und die Grundlinie eines Designtexts liegt bei 80 % seiner Zeilenhöhe; so stehen alle drei bei jedem Schriftgrad auf einer Grundlinie.

Seite 30, eine linke Seite: Marke, Trennlinie und Titel des Buchs, vom Außensteg nach innen gelesen.
Seite 31, eine rechte Seite: der Titel des Essays, gekürzt vor der Trennlinie und der Marke am Außensteg.

#3 · Die Ränder spiegeln und ganze Zeilen zählen

script.js · Zeilen 52–63im vollständigen Code
const TRIM = { width: 132, height: 198 }; // mm
const TOP = 22; // mm: the top margin, which holds the heads (gotcha: header-paints-over-text)
const INNER = 15; // mm
const OUTER = 21; // mm: the outer margin, where the folio tabs hang
const LINES = 31; // whole lines of LEAD in the text block, so full pages end level
const MEASURE = TRIM.width - INNER - OUTER; // the text block's width: 96 mm
const geometry = { // left is the inner margin on a recto; mirror swaps it on the versos
  width: mm(TRIM.width), height: mm(TRIM.height), dpi: 150, // 150 dpi is for the screen
  backgroundColor: col('paper'),
  margins: { top: mm(TOP), bottom: mm(TRIM.height - TOP - (LINES * LEAD * 25.4) / 72),
    left: mm(INNER), right: mm(OUTER), mirror: true },
};

Mit mirror: true ist der Rand right auf einer rechten Seite der äußere und wandert auf einer linken Seite nach links; OUTER ist also immer der Außensteg, an dem die Marken hängen. Der Fußsteg wird aus Endformat, Kopfsteg und LINES abgeleitet, was den Satzspiegel bei genau LINES Zeilen zu LEAD hält (hier 31 × 14 pt), welches Endformat und welchen Kopfsteg Sie auch wählen. Jede volle Seite endet auf der 31. Zeile, sodass gegenüberliegende Seiten wie 30 und 31 auf gleicher Höhe enden.

#4 · Die Auftaktseite den ganzen Titel zeigen lassen

script.js · Zeilen 99–121im vollständigen Code
const PLATE = 26; // mm: a square plate at the head of the text block
const opener = {
  enabled: true,
  minHeight: mm(58), // the text of every essay starts on the same line
  slot: { elements: [
    { kind: 'box', id: 'plate', style: { backgroundColor: col('sepia') },
      placement: { anchor: { to: 'container', edge: 'top-left' },
        size: { width: mm(PLATE), height: mm(PLATE) } } },
    { kind: 'text', id: 'numeral', content: '{attr.num}', fontFamily: 'Libre Caslon Display',
      fontSize: pt(48), lineHeight: 1, color: col('paper'),
      placement: { anchor: { to: '#plate', edge: 'align-top' }, // the plate's own box, where
        size: { width: mm(PLATE), height: mm(PLATE) } } }, // the text centres by default
    { kind: 'text', id: 'book', content: '{subtitle}', fontFamily: 'Alegreya SC',
      fontSize: pt(8.5), letterSpacing: pt(1.4), color: col('sepia'), align: 'left',
      placement: { anchor: { to: 'container', edge: 'top-left' },
        offset: { x: mm(PLATE + 4.5), y: mm(PLATE - 2.9) } } }, // its baseline on the plate's foot
    { kind: 'text', id: 'title', content: '{titleText}', fontFamily: 'Libre Caslon Display',
      fontSize: pt(20), lineHeight: 1.12, color: col('ink'), align: 'left',
      overflow: 'wrap', // an opener shows the whole title; the running head cuts it
      placement: { anchor: { to: '#plate', edge: 'below' }, offset: { y: mm(6) },
        size: { width: mm(MEASURE) } } },
  ] },
};

Die Auftaktseite setzt den Titel mit overflow: 'wrap' vollständig und bricht ihn an jedem \\ der Überschriftenzeile um. Nur ein Auftakt über die ganze Seitenbreite beachtet diese Umbrüche, deshalb hat die Ebene span: 'page' (Zeilenumbrüche in Titeln). Im Kolumnentitel liefert {chapterTitle} den Titel immer in einer Zeile, und die Auslassungspunkte kürzen diese Zeile. Die Ziffer stammt aus dem Attribut {num="IV"} der Überschrift und die Kapitälchen neben dem Schild aus subtitle im Frontmatter.

#5 · Jeden Essay auf einer rechten Seite beginnen

script.js · Zeilen 139–147im vollständigen Code
  headings: { // 400, the face's only weight: the default 700 would ask for one that is missing
    fontFamily: 'Libre Caslon Display', fontWeight: 400, color: col('ink'),
    levels: [
      // 'odd' puts every opener on a recto, adding a blank verso when one is needed;
      // span: 'page' makes the design an opener, where a \\ in the title breaks the line.
      { level: 1, span: 'page', breakBefore: { enabled: true, parity: 'odd' },
        marginTop: pt(0), marginBottom: pt(0), advancedDesign: opener },
    ],
  },

Mit 'odd' folgt auf einen Essay, der auf einer rechten Seite endet, eine leere linke Seite. Die spanische Ausgabe hat eine, weil ihr Kapitel IV auf Seite 31 endet; hier endet Kapitel IV auf Seite 32, einer linken Seite, und Kapitel V beginnt auf Seite 33. pages: 'body' der Kolumnentitel hält sie von Vakatseiten und von Auftaktseiten fern (den Seiten, deren erster Block diese Überschrift ist). pages: 'opener' der Fußzeile druckt die Pagina am Fuß nur auf Auftaktseiten (Textelemente).

#6 · Die Paginas bei 29 beginnen

script.js · Zeilen 251–255im vollständigen Code
// 28 pages come before this one, so recto and verso, the mirrored margins and the odd/even
// heads follow the book page (gotcha: parity-page1-recto); the folios start at 29.
const continuation = { pageIndexOffset: 28, pageNumbering: { startAt: 29 } };
const doc = await buildDocumentWithFonts({ markdown, continuation }, config(), kitFonts(FONTS));
showPages(doc, { title: t({ en: 'Running heads by parity', es: 'Cabeceras según la paridad' }) });

parity richtet sich nach der Stellung der Seite im Buch und ignoriert ihre Pagina. pageIndexOffset: 28 addiert 28 zum Index jeder Seite, sodass rechte und linke Seite, gespiegelte Ränder und Kolumnentitel für gerade und ungerade Seiten der Buchseite folgen. pageNumbering.startAt nummeriert diese Seiten dann von 29 bis 33. Ohne den Versatz ist die erste Seite des Builds eine rechte Seite, welche Pagina sie auch druckt. Das passt zufällig zu Seite 29, doch mit startAt: 30 käme der Titel des Buchs auf Seite 31 und der des Essays auf Seite 32, jeweils auf der falschen Seite der Doppelseite.

Das ganze Rezept

Sandbox
// ═══ Postext Cookbook · Nº 005 · Running heads by parity ═══════════════════════════════
// https://postext.dev/en/cookbook/running-heads-by-parity
// Code: MIT · Text: Montaigne, tr. Cotton (PD, Gutenberg #3600); es: new translation (MIT)
// Fonts: Baskervville, Libre Caslon Display, Alegreya SC (SIL OFL 1.1) · Needs postext ≥ 1.25.0
// Pages 29 to 33 of a pocket Montaigne. Versos carry the book's title and rectos the essay's,
// the folios sit in tabs in the outer margin, and an opener prints only a folio at the foot.
import { buildDocumentWithFonts, renderPageToCanvas } from 'https://esm.sh/postext';

const LANG = 'en'; // @lang: the language of the sample document ('en' | 'es')
const RECIPE = 'running-heads-by-parity';

// ─── 1 · Design ─────────────────────────────────────────────────────────────
// #region palette: four colours with semantic ids; main-color points at the sepia
const palette = {
  ink: '#1f1b16', // text: a warm near-black, never #000
  sepia: '#8a5a2b', // the one accent: folio tabs, dividers, opener plates (5.3:1 on paper)
  muted: '#6f6558', // running heads, verse glosses, the colophon (5.2:1 on paper)
  paper: '#f8f4ec', // a warm off-white page; also the type reversed out of the sepia
};
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' } })),
  // Text-style defaults (headings, bold, italic, lists, boxes) link to 'main-color':
  // pointed at the sepia, none of them prints in the engine's blue.
  { id: 'main-color', name: 'sepia (defaults)', value: { hex: palette.sepia, model: 'hex' } },
];
// #endregion
const LEAD = 14; // pt: the body leading, the pitch of the baseline grid

// #region tab: the folio tab and its divider, the two pieces every head is built from
const FOLIO_PT = 7.5; // pt: the folio, and the book's title in capitals beside it
const TITLE_PT = 8.8; // pt: the essay's title, whose italic lowercase reads small at 7.5
const LINE = FOLIO_PT * 1.2; // pt: the folio and both titles share this line box and baseline
const PAD = { top: 1, bottom: 1.4 }; // pt: 0.4 pt more below for the descending 3, 5, 7, 9
const TAB = 7.5; // mm: the tab's width
const TAB_H = LINE + PAD.top + PAD.bottom; // pt: the tab's height, which the divider matches
const folio = ({ id, parity, pages, anchor, x = 0, y }) => ({
  kind: 'text', id, content: '{pageNumber}', parity, pages, fontFamily: 'Alegreya SC',
  fontSize: pt(FOLIO_PT), fontWeight: 700, lineHeight: LINE / FOLIO_PT, color: col('paper'),
  box: { backgroundColor: col('sepia'), borderRadius: mm(2), // a pill; the number is centred
    padding: { top: pt(PAD.top), bottom: pt(PAD.bottom) } }, // by default in both directions
  placement: { anchor, offset: { x: mm(x), y: mm(y) }, size: { width: mm(TAB) } },
});
const divider = ({ id, parity, from, edge, x }) => ({
  kind: 'rule', id, parity, pages: 'body', direction: 'vertical', thickness: pt(0.5),
  color: col('sepia'), placement: { anchor: { to: `#${from}`, edge }, offset: { x: mm(x) },
    size: { height: pt(TAB_H) } },
});
// #endregion

// #region page: a pocket trim; the margins mirror, the text block holds 31 whole lines
const TRIM = { width: 132, height: 198 }; // mm
const TOP = 22; // mm: the top margin, which holds the heads (gotcha: header-paints-over-text)
const INNER = 15; // mm
const OUTER = 21; // mm: the outer margin, where the folio tabs hang
const LINES = 31; // whole lines of LEAD in the text block, so full pages end level
const MEASURE = TRIM.width - INNER - OUTER; // the text block's width: 96 mm
const geometry = { // left is the inner margin on a recto; mirror swaps it on the versos
  width: mm(TRIM.width), height: mm(TRIM.height), dpi: 150, // 150 dpi is for the screen
  backgroundColor: col('paper'),
  margins: { top: mm(TOP), bottom: mm(TRIM.height - TOP - (LINES * LEAD * 25.4) / 72),
    left: mm(INNER), right: mm(OUTER), mirror: true },
};
// #endregion

// #region answer: running heads by parity: book on the verso, essay on the recto, folios outside
const GAP = 3; // mm from the tab to the divider, which lands on the text block's edge
const HEAD = 12; // mm from the top edge of the page to the top of the tab
const CAP = t({ en: 60.5, es: 66.8 }); // mm: the head's widest line, set per edition so
// essay IV's is cut after 'false' and 'objetos'; a short running title suits any title
const page = (edge) => ({ to: 'page', edge }); // the trim box (gotcha: negative-offsets)
const header = { elements: [
  // Verso (even): tab | divider | THE BOOK'S TITLE from the frontmatter
  folio({ id: 'folio-even', parity: 'even', pages: 'body', anchor: page('top-left'),
    x: OUTER - GAP - TAB, y: HEAD }),
  divider({ id: 'rule-even', parity: 'even', from: 'folio-even', edge: 'right-of', x: GAP }),
  { kind: 'text', id: 'book', content: '{title}', parity: 'even', pages: 'body',
    fontFamily: 'Alegreya SC', fontSize: pt(FOLIO_PT), lineHeight: LINE / FOLIO_PT,
    color: col('muted'), textTransform: 'uppercase', letterSpacing: pt(1.3),
    placement: { anchor: { to: '#rule-even', edge: 'right-of' }, // top: the tab's top
      offset: { x: mm(2.5), y: pt(PAD.top) } } }, // PAD.top down: on the folio's baseline
  // Recto (odd): the essay's title, cut short with an ellipsis | divider | tab.
  folio({ id: 'folio-odd', parity: 'odd', pages: 'body', anchor: page('top-right'),
    x: -(OUTER - GAP - TAB), y: HEAD }), // from the right edge, a negative x runs inwards
  divider({ id: 'rule-odd', parity: 'odd', from: 'folio-odd', edge: 'left-of', x: -GAP }),
  { kind: 'text', id: 'essay', content: '{chapterTitle}', parity: 'odd', pages: 'body',
    fontFamily: 'Baskervville', italic: true, fontSize: pt(TITLE_PT),
    lineHeight: LINE / TITLE_PT, color: col('muted'),
    overflow: 'ellipsis-end', // stated, though a running head's default
    placement: { anchor: { to: '#rule-odd', edge: 'left-of' },
      offset: { x: mm(-2.5), y: pt(PAD.top) }, size: { maxWidth: mm(CAP) } } },
] };
// Openers: no head, only a drop folio HEAD above the page's foot, centred under the text block.
const footer = { elements: [folio({ id: 'drop-folio', parity: 'all', pages: 'opener',
  anchor: { to: 'container', edge: 'bottom' }, y: -HEAD })] };
// #endregion

// #region opener: the essay's numeral on a sepia plate; the title below it, set in full
const PLATE = 26; // mm: a square plate at the head of the text block
const opener = {
  enabled: true,
  minHeight: mm(58), // the text of every essay starts on the same line
  slot: { elements: [
    { kind: 'box', id: 'plate', style: { backgroundColor: col('sepia') },
      placement: { anchor: { to: 'container', edge: 'top-left' },
        size: { width: mm(PLATE), height: mm(PLATE) } } },
    { kind: 'text', id: 'numeral', content: '{attr.num}', fontFamily: 'Libre Caslon Display',
      fontSize: pt(48), lineHeight: 1, color: col('paper'),
      placement: { anchor: { to: '#plate', edge: 'align-top' }, // the plate's own box, where
        size: { width: mm(PLATE), height: mm(PLATE) } } }, // the text centres by default
    { kind: 'text', id: 'book', content: '{subtitle}', fontFamily: 'Alegreya SC',
      fontSize: pt(8.5), letterSpacing: pt(1.4), color: col('sepia'), align: 'left',
      placement: { anchor: { to: 'container', edge: 'top-left' },
        offset: { x: mm(PLATE + 4.5), y: mm(PLATE - 2.9) } } }, // its baseline on the plate's foot
    { kind: 'text', id: 'title', content: '{titleText}', fontFamily: 'Libre Caslon Display',
      fontSize: pt(20), lineHeight: 1.12, color: col('ink'), align: 'left',
      overflow: 'wrap', // an opener shows the whole title; the running head cuts it
      placement: { anchor: { to: '#plate', edge: 'below' }, offset: { y: mm(6) },
        size: { width: mm(MEASURE) } } },
  ] },
};
// #endregion

const config = () => ({
  locale: t({ en: 'en-us', es: 'es' }), // the language of the hyphenation patterns
  colorPalette,
  page: geometry,
  layout: { layoutType: 'single' },
  bodyText: { // bold, italic and references default to main-color (the sepia): set to the ink
    fontFamily: 'Baskervville', fontSize: pt(10.2), lineHeight: pt(LEAD), color: col('ink'),
    boldColor: col('ink'), italicColor: col('ink'), referenceColor: col('ink'),
    textAlign: 'justify', firstLineIndent: mm(4), indentAfterHeading: false,
    // Hyphenation, optimal breaking and widow, orphan and runt control are on by default; a band
    // of 0.7–1.65 (default 0.6–2) evens the grey from line to line. At the default runt length,
    // 20 space widths, English essay IV ends on 'minds.' alone; at 16 it ends on 'our minds.'.
    minWordSpacing: 0.7, maxWordSpacing: 1.65, runtMinCharacters: 16,
  },
  // #region levels: every essay opens on a recto; the heading draws the opener
  headings: { // 400, the face's only weight: the default 700 would ask for one that is missing
    fontFamily: 'Libre Caslon Display', fontWeight: 400, color: col('ink'),
    levels: [
      // 'odd' puts every opener on a recto, adding a blank verso when one is needed;
      // span: 'page' makes the design an opener, where a \\ in the title breaks the line.
      { level: 1, span: 'page', breakBefore: { enabled: true, parity: 'odd' },
        marginTop: pt(0), marginBottom: pt(0), advancedDesign: opener },
    ],
  },
  // #endregion
  // Montaigne's quotations: a box with no fill keeps verse and gloss together. Its two 10.5 pt
  // margins and a two-line gloss (2 × 10.5 pt) make three whole lines, so the grid adds no space.
  calloutStyles: [{ id: 'quote', backgroundEnabled: false,
    padding: { top: pt(0), right: mm(8), bottom: pt(0), left: mm(8) },
    marginTop: pt(LEAD * 0.75), marginBottom: pt(LEAD * 0.75) }],
  paragraphStyles: [
    { id: 'verse', fontSize: pt(9.6), textAlign: 'center', firstLineIndent: pt(0) },
    { id: 'gloss', fontSize: pt(7.6), lineHeight: pt(LEAD * 0.75), color: col('muted'),
      textAlign: 'center', firstLineIndent: pt(0) },
    // Markdown has no horizontal rule (--- prints as text; gap: markdown-extras), so the rule
    // over the colophon is eight em dashes of Alegreya SC that overlap into a 0.5 pt line.
    { id: 'end', fontFamily: 'Alegreya SC', fontSize: pt(8), color: col('sepia'),
      textAlign: 'left', firstLineIndent: pt(0), marginTop: pt(LEAD) },
    { id: 'colophon', fontSize: pt(7), lineHeight: pt(9.5), color: col('muted'),
      textAlign: 'left', firstLineIndent: pt(0) },
  ],
  header,
  footer,
});

// ─── 2 · Content ────────────────────────────────────────────────────────────
const markdown = String.raw`---
Markdown-Beispiel · 70 Zeilen · content.en.mdtitle: "The Essays of Montaigne" subtitle: "Book the First" author: "Michel de Montaigne" --- # That the soul expends its passions \\ upon false objects, \\ where the true are wanting {num="IV"} A gentleman of my country, marvellously tormented with the gout, being importuned by his physicians totally to abstain from all manner of salt meats, was wont pleasantly to reply, that in the extremity of his fits he must needs have something to quarrel with, and that railing at and cursing, one while the Bologna sausages, and another the dried tongues and the hams, was some mitigation to his pain. But, in good earnest, as the arm when it is advanced to strike, if it miss the blow, and goes by the wind, it pains us; and as also, that, to make a pleasant prospect, the sight should not be lost and dilated in vague air, but have some bound and object to limit and circumscribe it at a reasonable distance. :::callout{type="quote"} :::paragraphs{style="verse"} *Ventus ut amittit vires, nisi robore densae* *Occurrant sylvae, spatio diffusus inani.* ::: :::paragraphs{style="gloss"} As the wind loses its force diffused in void space, unless it in its strength encounters the thick wood. — Lucan, III, 362 ::: ::: So it seems that the soul, being transported and discomposed, turns its violence upon itself, if not supplied with something to oppose it, and therefore always requires an object at which to aim, and whereon to act. Plutarch says of those who are delighted with little dogs and monkeys, that the amorous part that is in us, for want of a legitimate object, rather than lie idle, does after that manner forge and create one false and frivolous. And we see that the soul, in its passions, inclines rather to deceive itself, by creating a false and fantastical subject, even contrary to its own belief, than not to have something to work upon. After this manner brute beasts direct their fury to fall upon the stone or weapon that has hurt them, and with their teeth even execute revenge upon themselves for the injury they have received from another: :::callout{type="quote"} :::paragraphs{style="verse"} *Pannonis haud aliter, post ictum saevior ursa,* *Cui jaculum parva Lybis amentavit habena,* *Se rotat in vulnus, telumque irata receptum* *Impetit, et secum fugientem circuit hastam.* ::: :::paragraphs{style="gloss"} So the she-bear, fiercer for the Libyan’s dart, turns upon the wound and, attacking the spear, twists it as she flies. — Lucan, VI, 220 ::: ::: What causes of the misadventures that befall us do we not invent? what is it that we do not lay the fault to, right or wrong, that we may have something to quarrel with? It is not those beautiful tresses you tear, nor is it the white bosom that in your anger you so unmercifully beat, that with an unlucky bullet have slain your beloved brother; quarrel with something else. Livy, speaking of the Roman army in Spain, says that for the loss of the two brothers, their great captains, *flere omnes repente, et offensare capita*, “all at once wept and tore their hair.” ’Tis a common practice. And the philosopher Bion said pleasantly of the king, who by handfuls pulled his hair off his head for sorrow, “Does this man think that baldness is a remedy for grief?” Who has not seen peevish gamesters chew and swallow the cards, and swallow the dice, in revenge for the loss of their money? Xerxes whipped the sea, and wrote a challenge to Mount Athos; Cyrus employed a whole army several days at work, to revenge himself of the river Gyndas, for the fright it had put him into in passing over it; and Caligula demolished a very beautiful palace for the pleasure his mother had once enjoyed there. I remember there was a story current, when I was a boy, that one of our neighbouring kings, having received a blow from the hand of God, swore he would be revenged, and in order to it, made proclamation that for ten years to come no one should pray to Him, or so much as mention Him throughout his dominions, or, so far as his authority went, believe in Him; by which they meant to paint not so much the folly as the vainglory of the nation of which this tale was told. They are vices that always go together, but in truth such actions as these have in them still more of presumption than want of wit. Augustus Caesar, having been tossed with a tempest at sea, fell to defying Neptune, and in the pomp of the Circensian games, to be revenged, deposed his statue from the place it had amongst the other deities. Wherein he was still less excusable than the former, and less than he was afterwards when, having lost a battle under Quintilius Varus in Germany, in rage and despair he went running his head against the wall, crying out, “O Varus! give me back my legions!” for these exceed all folly, forasmuch as impiety is joined therewith, invading God Himself, or at least Fortune, as if she had ears that were subject to our batteries; like the Thracians, who when it thunders or lightens, fall to shooting against heaven with Titanian vengeance, as if by flights of arrows they intended to bring God to reason. Though the ancient poet in Plutarch tells us: :::callout{type="quote"} :::paragraphs{style="verse"} *Point ne se faut couroucer aux affaires,* *Il ne leur chault de toutes nos choleres.* ::: :::paragraphs{style="gloss"} We must not trouble the gods with our affairs; they take no heed of our angers and disputes. — Plutarch ::: ::: But we can never enough decry the disorderly sallies of our minds. # Whether the governor of a place \\ besieged ought himself \\ to go out to parley {num="V"} Quintus Marcius, the Roman legate in the war against Perseus, King of Macedon, to gain time wherein to reinforce his army, set on foot some overtures of accommodation, with which the king being lulled asleep, concluded a truce for some days, by this means giving his enemy opportunity and leisure to recruit his forces, which was afterwards the occasion of the king’s final ruin. Yet the elder senators, mindful of their forefathers’ manners, condemned this proceeding as degenerating from their ancient practice, which, they said, was to fight by valour, and not by artifice, surprises, and night-encounters; neither by pretended flight nor unexpected rallies to overcome their enemies; never making war till having first proclaimed it, and very often assigned both the hour and place of battle. :::paragraphs{style="end"} ———————— ::: :::paragraphs{style="colophon"} Chapters IV and V of Book the First of Montaigne’s essays, in Charles Cotton’s translation as revised by W. C. Hazlitt (1877), Project Gutenberg eBook #3600; notes shortened or omitted, misprints corrected. Set in Baskervville, Libre Caslon Display and Alegreya SC (SIL Open Font License). :::
`; // content.<lang>.md, inlined by the Cookbook // ─── 3 · Fonts ────────────────────────────────────────────────────────────── // Every face the design uses, loaded before the first build. const FONTS = { // text, display and label faces (Libre Caslon Display has no italic) Baskervville: ['400', '400i'], 'Libre Caslon Display': ['400'], 'Alegreya SC': ['400', '700'], }; // ─── 4 · Build & show ─────────────────────────────────────────────────────── // #region build: pages 29 to 33 of the book // 28 pages come before this one, so recto and verso, the mirrored margins and the odd/even // heads follow the book page (gotcha: parity-page1-recto); the folios start at 29. const continuation = { pageIndexOffset: 28, pageNumbering: { startAt: 29 } }; const doc = await buildDocumentWithFonts({ markdown, continuation }, config(), kitFonts(FONTS)); showPages(doc, { title: t({ en: 'Running heads by parity', es: 'Cabeceras según la paridad' }) }); // #endregion
Kit · core, fonts, viewer: in jedem Rezept gleich · 224 Zeilen// ─── 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 ───────────────────────────────────────────────────────────────────────

Das zusammengesetzte script.js läuft unverändert: Fügen Sie es in das Modulskript einer beliebigen Seite ein, oder öffnen Sie das Rezept auf CodePen. Ordner des Rezepts auf GitHub ↗ (öffnet in neuem Tab)

Varianten

#Langen Titeln einen kurzen Kolumnentitel geben

Schreiben Sie den Kolumnentitel in die Überschriftenzeile, {num="IV" short="Of false objects"}, und drucken Sie das Attribut statt des ganzen Titels; ein Kapitel ohne short druckt dort nichts.

-  { kind: 'text', id: 'essay', content: '{chapterTitle}', parity: 'odd', pages: 'body',
+  { kind: 'text', id: 'essay', content: '{attr.short}', parity: 'odd', pages: 'body',

#Essays auf beiden Seiten beginnen lassen

Mit 'any' beginnt ein Essay auf der nächsten Seite, rechts oder links, und es wird nie eine leere Seite eingefügt. Das Schild hängt an der linken oberen Ecke des Satzspiegels und die Pagina am Fuß ist darunter zentriert, sodass beide auf einer linken Seite genauso stehen wie auf einer rechten.

-      { level: 1, span: 'page', breakBefore: { enabled: true, parity: 'odd' },
+      { level: 1, span: 'page', breakBefore: { enabled: true, parity: 'any' },

#Die leere linke Seite in der Akzentfarbe färben

Ein Kasten mit pages: 'blank' druckt nur auf den Seiten, die der Umbruch auf eine rechte Seite hinzufügt: In der spanischen Ausgabe füllt er Seite 32 mit Sepia, und die englische Ausgabe, die keine leere Seite hat, bleibt unverändert.

 const footer = { elements: [folio({ id: 'drop-folio', parity: 'all', pages: 'opener',
-  anchor: { to: 'container', edge: 'bottom' }, y: -HEAD })] };
+  anchor: { to: 'container', edge: 'bottom' }, y: -HEAD }),
+  { kind: 'box', id: 'blank-leaf', pages: 'blank', style: { backgroundColor: col('sepia') },
+    placement: { anchor: page('top-left'),
+      size: { width: mm(TRIM.width), height: mm(TRIM.height) } } }] };

Häufige Fehler

Häufiger Fehler

Negative Versätze relativ zum Container rendern nichts

Gestalteter Text mit automatischer Breite wird auf seinen Container begrenzt, ein negativer Versatz vom Container schiebt ihn also hinaus, und nichts wird gerendert. Verankern Sie solche Elemente mit expliziten mm-Versätzen an der Seite oder am Anschnitt, oder geben Sie ihnen eine feste Breite. Gestaltungselemente verankern →

Häufiger Fehler

Elemente von Kopf- und Fußzeile malen über den Text

Elemente von Kopf- und Fußzeile werden über die Seite gemalt, und der Textbereich macht ihnen keinen Platz. Halten Sie sie innerhalb der Ränder, denn die Ränder reservieren ihren Raum. Kolumnentitel und Seitenzahlen →

Häufiger Fehler

Seite 1 ist eine rechte Seite: Seiten mit physischen Nummern planen

Seite 1 ist eine rechte Seite und Seite 2 die erste linke, planen Sie Doppelseiten also mit physischen Seitenzahlen: ein Auftakt auf einer geraden Seite steht der ungeraden Seite danach gegenüber. Seiten- und Spaltenumbrüche →

Sandbox-Prüfung · headerFooterUnknownPlaceholder

Unbekannter Platzhalter

Ursache. Ein Gestaltungstext verwendet einen Platzhalter {name}, den es nicht gibt, er wird daher wörtlich gedruckt.

Lösung. Verwenden Sie einen unterstützten Platzhalter ({pageNumber}, {chapterTitle}, {title}, {attr.x} …) oder schreiben Sie {{ und }} für wörtliche geschweifte Klammern. Doku →

Sandbox-Prüfung · headerFooterMetadataMissing

Metadaten für Platzhalter fehlen

Ursache. Ein Platzhalter wie {author} oder {title} hat in den Metadaten des Dokuments keinen Wert und wird daher leer gedruckt.

Lösung. Ergänzen Sie den Wert im Frontmatter, in Anführungszeichen, oder entfernen Sie den Platzhalter. Doku →

  • CAP ist in jeder Ausgabe auf den Titel von Essay IV eingestellt, den einzigen, den hier ein Kolumnentitel zeigt. Die Auslassungspunkte folgen auf das letzte ganze Wort, das passt, und dieses Wort kann eine Präposition sein: Bei 64 mm würde der spanische Kolumnentitel auf „…sus pasiones sobre…“ enden. Ein Buch mit langen Titeln braucht den kurzen Kolumnentitel aus den Varianten.
  • In der englischen Ausgabe lässt eine Erhöhung von minWordSpacing von 0.7 auf 0.75 der ersten Zeile von Essay V („Quintus Marcius, the Roman legate…“) keinen Umbruch innerhalb des Bereichs, und ihre Wortzwischenräume dehnen sich auf das 2,4-Fache ihrer normalen Breite. Die spanische Ausgabe hat keine solche Zeile. Prüfen Sie nach jeder Änderung die lockersten Zeilen, die die Aufnahme auflistet.

Nachweise

Text
Schriften
Baskervville (SIL OFL 1.1) · Libre Caslon Display (SIL OFL 1.1) · Alegreya SC (SIL OFL 1.1)
Sandbox