Vai al contenuto principale
Ricetta numero 175

Ricettario · Capitolo 5 · Struttura del libro

Una guida museale con i link stampati come codici QR

Una passeggiata tascabile in un museo con i link web stampati come note numerate a piè di pagina, con indirizzo breve e codice QR (links.print su notes).

In questa pagina
Output
Canvas · PDF
Livello
Intermedio
Postext
Provata con Postext 1.65.0
Richiede ≥ 1.65.0 · postext-pdf ≥ 1.65.0
Licenza
Aggiornata il 31 ott 2026
Codice MIT · Testo CC BY 4.0
  • Esempio in inglese: non c’è ancora un’edizione italiana
  • Formato 160 × 240 mm
  • 1 colonna
  • Newsreader 10,2/14
  • Archivo
  • Instrument Serif
  • 7 pagine
  • Livello
  • Postext 1.65.0
  • Impaginato in 167 ms
  • 177 righe di codice

In breve

Una piccola guida stampata a cinque dipinti. Dove il testo cita una pagina web, un numero piccolo rimanda al piede della pagina, dove un codice quadrato apre quella pagina sul telefono.

Che cosa comporrai

Una guida tascabile alle sale olandesi del Metropolitan Museum of Art: cinque dipinti, un’ora a piedi, un dipinto per pagina. La copertina è la giovane donna di Vermeer alla finestra, tagliata dal margine superiore. Ogni tappa si apre con il numero di sala, il titolo e il pittore, poi il dipinto con la didascalia da parete accanto, poi tre brevi paragrafi. Il testo rimanda al catalogo del museo come farebbe qualunque pagina web. Sulla carta ogni link diventa un numero piccolo, e al piede della pagina la nota dà l’indirizzo breve e un codice QR che il visitatore inquadra con il telefono davanti al quadro.

Questa ricetta risponde a

  • Come stampo gli indirizzi web dei link di un libro, come note numerate o codici QR che il lettore può inquadrare?

La risposta breve

script.js · righe 39–50nel codice completo
const links = {
  print: 'notes', // a note marker after the link text, the address in a note at the foot
  show: 'both', // the short address for reading, the code for the phone
  urlDisplay: 'short', // metmuseum.org/art/collection/search/436616: no https://, no www.
  repeat: 'first', // an address noted once is cited again by its number, never noted twice
  qr: { size: mm(16), side: 'start', gap: em(1.2), color: col('ink') },
};
// Link notes are footnotes: they take the note style and the numbering below. 'document'
// numbers them through the walk, so the five stops print notes 3 to 7, not five notes 1.
const footnotes = { placement: 'column', numbering: 'document', fontSize: pt(8),
  lineHeight: pt(10.5), color: col('muted'), spaceAbove: pt(LEAD), spaceBetween: pt(5),
  separator: { enabled: true, width: 0.18, lineWidth: pt(0.5), color: col('rule') } };

Ingredienti

Tipo
Newsreader, Instrument Serif, Archivo (SIL OFL 1.1)
Risorse
  • hals-1200.jpg
  • rembrandt-1200.jpg
  • ruisdael-1400.jpg
  • steen-1200.jpg
  • vermeer-1600.jpg
  • vermeer-cover-1800.jpg
  • Johannes Vermeer, Young Woman with a Water Pitcher, about 1662 (the cover, cropped) (The Metropolitan Museum of Art, CC0 1.0)
  • Frans Hals, Young Man and Woman in an Inn, 1623 (stop 1) (The Metropolitan Museum of Art, CC0 1.0)
  • Jan Steen, Merry Company on a Terrace, about 1670 (stop 2) (The Metropolitan Museum of Art, CC0 1.0)
  • Rembrandt, Aristotle with a Bust of Homer, 1653 (stop 3) (The Metropolitan Museum of Art, CC0 1.0)
  • Johannes Vermeer, Young Woman with a Water Pitcher, about 1662 (stop 4) (The Metropolitan Museum of Art, CC0 1.0)
  • Jacob van Ruisdael, Wheat Fields, about 1670 (stop 5) (The Metropolitan Museum of Art, CC0 1.0)

Preparazione

Il codice è la risposta breve qui sopra. Il Markdown conserva link normali, [La scheda del catalogo](https://www.metmuseum.org/…/436616), e links.print: 'notes' trasforma ciascuno in una nota numerata con l’indirizzo e il codice (link su carta). Le note sono note a piè di pagina: numbering: 'document' le conta lungo tutta la passeggiata, quindi il lettore incontra le note da 1 a 7, e repeat: 'first' lascia che l’ultima tappa citi il Hals con la sua nota 3 invece di stampare due volte lo stesso codice. Nel PDF il codice è disegnato in vettoriale ed è esso stesso un link.

Il piede della tappa 1: la nota 3, la scheda del dipinto come indirizzo breve accanto a un codice di 16 mm.

#2 · Una tappa per pagina

script.js · righe 74–82nel codice completo
const stop = { enabled: true, minHeight: pt(4 * LEAD), slot: { elements: [
    { kind: 'text', id: 'stop', content: '{attr.stop}', ...caps(7.5), color: col('delft'),
      align: 'left', placement: at('container', 'top-left', 0, 0) },
    { kind: 'text', id: 'title', content: '{titleText}', fontFamily: DISPLAY, fontSize: pt(22),
      lineHeight: 1.05, color: col('ink'), align: 'left', placement: at('#stop', 'below', 0, 1.6) },
    { kind: 'text', id: 'artist', content: '{attr.artist}', fontFamily: TEXT, italic: true,
      fontSize: pt(10.5), color: col('muted'), align: 'left',
      placement: at('#title', 'below', 0, 0.8) },
  ] } };

Ogni tappa è un titolo di secondo livello il cui breakBefore apre una pagina, pari o dispari, così il visitatore gira un foglio per dipinto. Il numero di sala e il pittore sono attributi del titolo; il disegno li mette sopra e sotto il titolo, e minHeight tiene il dipinto alla stessa altezza in ogni pagina.

#3 · La didascalia accanto al dipinto

script.js · righe 219–259nel codice completo
// Paintings are not numbered: their own type, with no caption prefix and no number.
const paintingType = { id: 'painting', name: t({ en: 'Painting', es: 'Cuadro' }),
  shortLabel: t({ en: 'painting', es: 'cuadro' }), captionPrefix: '', numberingTemplate: '',
  resetOn: 'never', counterFormat: 'decimal' };
// Set where the ::resource line stands, under the stop's heading; caption and note beside it.
const HERE = { position: 'here', captionSide: 'inside' };
const cm = (n) => (LANG === 'es' ? String(n).replace('.', ',') : String(n)); // decimal comma
// The wall label: medium, size in cm (no-break spaces) and the museum's credit line.
const painting = (id, w, h, [cmH, cmW], credit, alt, width = 0.5) => ({
  id, typeId: 'painting', kind: 'bitmap', createdAt: 0, updatedAt: 0, placement: { ...HERE, width },
  bitmap: { fileId: `${id}-${Math.max(w, h)}.jpg`, format: 'jpeg', width: w, height: h },
  caption: `${OIL}, ${cm(cmH)}\u00a0×\u00a0${cm(cmW)}\u00a0cm. ${credit}`, altText: alt,
  note: 'The Metropolitan Museum of Art, Open Access (CC0)' });
const OIL = t({ en: 'Oil on canvas', es: 'Óleo sobre lienzo' });
const ALTMAN = t({ en: 'Bequest of Benjamin Altman, 1913', es: 'Legado de Benjamin Altman, 1913' });
const resources = [
  painting('hals', 908, 1200, [105.4, 79.4], `${ALTMAN} (14.40.602)`, t({
    en: 'A laughing young man raises a glass; a young woman leans on his shoulder.',
    es: 'Un joven ríe y alza una copa; una joven se apoya en su hombro.' }), 0.4), // portrait
  painting('steen', 1115, 1200, [141, 131.4], 'Fletcher Fund, 1958 (58.89)', t({
    en: 'A crowded party under a vine, with a lute player, a child and a dog.',
    es: 'Una fiesta concurrida bajo una parra, con un laudista, un niño y un perro.' })),
  painting('rembrandt', 1139, 1200, [143.5, 136.5], t({
    en: 'Purchase, special contributions and funds given or bequeathed by friends of the Museum',
    es: 'Compra con aportaciones y fondos donados o legados por amigos del Museo' })
    + ', 1961 (61.198)', t({ en: 'A bearded man in a black hat rests his hand on a marble bust.',
    es: 'Un hombre barbado con sombrero negro apoya la mano en un busto de mármol.' })),
  painting('vermeer', 1422, 1600, [45.7, 40.6], t({ en: 'Marquand Collection, gift of',
    es: 'Colección Marquand, donación de' }) + ' Henry G. Marquand, 1889 (89.15.21)', t({
    en: 'A young woman at an open window holds a gilt pitcher over a basin.',
    es: 'Una joven junto a una ventana abierta sujeta una jarra dorada sobre una jofaina.' })),
  painting('ruisdael', 1400, 1084, [100, 130.2], `${ALTMAN} (14.40.623)`, t({
    en: 'A sandy road between wheat fields under a tall bank of cloud.',
    es: 'Un camino de arena entre campos de trigo bajo un alto banco de nubes.' }), 0.6),
  // The cover: the Vermeer cut to the box's 4 : 3 (160 × 120 mm), since the opener fits a
  // picture inside its box. Drawn by the cover design, never cited.
  { ...painting('vermeer-cover', 1800, 1350, [], '', t({
    en: 'Detail of Vermeer’s young woman at the window, in blue and white.',
    es: 'Detalle de la joven de Vermeer junto a la ventana, en azul y blanco.' })),
  caption: '', note: '' },
];

captionSide: 'inside' mette la didascalia e la nota di credito accanto al dipinto, come un museo appende il suo cartellino, e width dà a ogni dipinto la sua parte di giustezza: meno al Hals, che è alto, di più al Ruisdael, che è orizzontale, così le cinque pagine hanno un’altezza di testo simile. La didascalia unisce la tecnica, le misure in centimetri con spazi indivisibili e la riga di provenienza del museo, con la virgola decimale nell’edizione spagnola.

#4 · La tappa nella testatina

script.js · righe 86–96nel codice completo
const head = (id, content, parity, edge, x, extra = {}) => ({
  kind: 'text', id, content, parity, pages: 'body', ...caps(7, 600), color: col('muted'),
  placement: at('page', edge, x, 13), ...extra,
});
const folio = { ...caps(8, 700), color: col('ink') };
const header = { elements: [
  head('verso-folio', '{pageNumber}', 'even', 'top-left', OUTER, folio),
  head('verso-title', '{title}', 'even', 'top-left', OUTER + 9),
  head('recto-stop', '{firstMark.h2}', 'odd', 'top-right', -(OUTER + 9)),
  head('recto-folio', '{pageNumber}', 'odd', 'top-right', -OUTER, folio),
] };

{firstMark.h2} stampa il titolo della tappa che comincia nella pagina: la pagina dispari nomina il dipinto, quella pari la guida. Nessuna testatina arriva alla copertina, una pagina che ha il ruolo di apertura.

La ricetta completa

Sandbox
// ═══ Postext Cookbook · Nº 175 · A museum walk whose links print as QR notes ═════
// https://postext.dev/en/cookbook/museum-guide-qr-link-notes
// Code: MIT · Text: original (CC BY 4.0) · Paintings: The Metropolitan Museum of Art (CC0)
// Fonts: Newsreader, Instrument Serif, Archivo (SIL OFL 1.1) · Needs postext ≥ 1.65.0
// A pocket gallery guide: five paintings, one to a page. The Markdown writes the museum's
// catalogue pages as ordinary links; the print edition turns each one into a numbered note at
// the foot of the page, with the short address and a code a phone can scan.
import {
  buildDocumentWithFonts, renderPageToCanvas, registerResourceImage,
} 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 = 'museum-guide-qr-link-notes';

// ─── 1 · Design ─────────────────────────────────────────────────────────────
// #region palette: Delft blue on warm paper, every colour linked by id
const palette = {
  ink: '#1f1d1a', // text: a warm near-black
  delft: '#2b4a7a', // the one accent: kickers, stop numbers, rules (8.6:1 on paper)
  rule: '#c6c9cf', // hairlines
  muted: '#5f6168', // running heads, notes, credits (6.1:1 on paper)
  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' } })),
  // The engine's defaults link to main-color: point it at the accent, so nothing prints blue.
  { id: 'main-color', name: 'delft (defaults)', value: { hex: palette.delft, model: 'hex' } },
];
// #endregion
const TRIM = { w: 160, h: 240 }; // a large pocket guide, in mm
const TOP = 22; // top margin in mm: the cover's container starts here
const OUTER = 22; // outer margin in mm: folios and running heads align to it
const LEAD = 14; // body leading in pt: the baseline grid
const [TEXT, DISPLAY, LABEL] = ['Newsreader', 'Instrument Serif', 'Archivo']; // three voices

// #region answer: every web link of the guide prints as a numbered note with a code to scan
const links = {
  print: 'notes', // a note marker after the link text, the address in a note at the foot
  show: 'both', // the short address for reading, the code for the phone
  urlDisplay: 'short', // metmuseum.org/art/collection/search/436616: no https://, no www.
  repeat: 'first', // an address noted once is cited again by its number, never noted twice
  qr: { size: mm(16), side: 'start', gap: em(1.2), color: col('ink') },
};
// Link notes are footnotes: they take the note style and the numbering below. 'document'
// numbers them through the walk, so the five stops print notes 3 to 7, not five notes 1.
const footnotes = { placement: 'column', numbering: 'document', fontSize: pt(8),

Lo script.js composto funziona così com’è: incollalo nello script modulo di qualsiasi pagina, oppure apri la ricetta su CodePen. Cartella della ricetta su GitHub ↗ (si apre in una nuova scheda)

Varianti

#Stampare l’indirizzo nella riga

Per una guida da leggere a casa, 'inline' aggiunge l’indirizzo tra parentesi dopo il testo del link, senza nota né codice.

-  print: 'notes', // a note marker after the link text, the address in a note at the foot
-  show: 'both', // the short address for reading, the code for the phone
+  print: 'inline', // (metmuseum.org/…) after the link text

#Codici senza indirizzo

show: 'qr' stampa solo il codice accanto al numero della nota, per un manifesto o un cartellino in cui l’indirizzo occuperebbe solo spazio.

-  show: 'both', // the short address for reading, the code for the phone
+  show: 'qr', // the code alone

Errori frequenti

Una nota con codice è alta almeno quanto il codice. Quando la riga che porta il link non ha spazio sotto di sé per la sua nota nella pagina, la riga passa alla pagina seguente con la nota: tieni i link fuori dalle ultime righe di una pagina che deve finire dove finisce, come il primo paragrafo della copertina di questa guida.

Crediti

Testo
Testo originale, CC BY 4.0
Immagini
Font
Newsreader (SIL OFL 1.1) · Instrument Serif (SIL OFL 1.1) · Archivo (SIL OFL 1.1)
SandboxPDF