Pular para o conteúdo principal
Receita número 175

Receitas · Capítulo 5 · Estrutura do livro

Um guia de museu com os links impressos como códigos QR

Um passeio de bolso por um museu com os links impressos como notas numeradas no rodapé, com endereço curto e código QR (links.print em notes).

Nesta página
Saída
Canvas · PDF
Postext
Testada com o Postext 1.65.0
Requer ≥ 1.65.0 · postext-pdf ≥ 1.65.0
Licença
Atualizada em 31 de out. de 2026
Código MIT · Texto CC BY 4.0
  • Amostra em inglês: ainda sem edição em português
  • Refile 160 × 240 mm
  • 1 coluna
  • Newsreader 10,2/14
  • Archivo
  • Instrument Serif
  • 7 páginas
  • Nível
  • Postext 1.65.0
  • Diagramado em 167 ms
  • 177 linhas de código

Em poucas palavras

Um pequeno guia impresso de cinco quadros. Onde o texto cita uma página web, um número pequeno leva ao pé da página, onde um código quadrado abre essa página no celular.

O que você vai compor

Um guia de bolso das salas holandesas do Metropolitan Museum of Art: cinco quadros, uma hora a pé, um quadro por página. A capa é a jovem de Vermeer junto à janela, cortada pela borda superior. Cada parada abre com o número da sala, o título e o pintor, depois o quadro com a etiqueta ao lado e depois três parágrafos curtos. O texto aponta para o catálogo do museu como qualquer página web faria. No papel, cada link vira um número pequeno, e no pé da página a nota traz o endereço curto e um código QR que o visitante aponta com o celular diante do quadro.

Esta receita responde a

  • Como imprimo os endereços web dos links de um livro, como notas numeradas ou códigos QR que o leitor possa escanear?

A resposta curta

script.js · linhas 39–50no código 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') } };

Ingredientes

Tipografia
Newsreader, Instrument Serif, Archivo (SIL OFL 1.1)
Materiais
  • 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)

Preparo

O código é a resposta curta acima. O Markdown mantém links comuns, [A ficha do catálogo](https://www.metmuseum.org/…/436616), e links.print: 'notes' transforma cada um numa nota numerada com o endereço e o código (links na edição impressa). As notas são notas de rodapé: numbering: 'document' as conta ao longo do passeio, então o leitor encontra de 1 a 7, e repeat: 'first' deixa a última parada citar o Hals pela nota 3 em vez de imprimir o mesmo código duas vezes. No PDF o código é desenhado em vetores e é ele mesmo um link.

O pé da parada 1: a nota 3, a ficha do quadro como endereço curto ao lado de um código de 16 mm.

#2 · Uma parada por página

script.js · linhas 74–82no código 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) },
  ] } };

Cada parada é um título de segundo nível cujo breakBefore abre uma página, par ou ímpar, e assim o visitante vira uma folha por quadro. O número da sala e o pintor são atributos do título; o design os põe acima e abaixo do título, e minHeight deixa o quadro na mesma altura em todas as páginas.

#3 · A etiqueta ao lado do quadro

script.js · linhas 219–259no código 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' põe a legenda e a nota de crédito ao lado do quadro, como um museu pendura a etiqueta, e width dá a cada quadro sua parte da medida: menos ao Hals, que é alto, mais ao Ruisdael, que é horizontal, para que as cinco páginas tenham uma altura de texto parecida. A etiqueta junta a técnica, as medidas em centímetros com espaços inseparáveis e a linha de crédito do museu, com vírgula decimal na edição espanhola.

#4 · A parada no cabeçalho

script.js · linhas 86–96no código 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} imprime o título da parada que começa na página: a página ímpar nomeia o quadro, a par, o guia. Nenhum cabeçalho chega à capa, uma página com papel de abertura.

A receita 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),

O script.js montado funciona como está: cole-o como script de módulo em qualquer página ou abra a receita no CodePen. Pasta da receita no GitHub ↗ (abre em uma nova aba)

Variações

#Imprima o endereço na linha

Para um guia lido em casa, 'inline' acrescenta o endereço entre parênteses depois do texto do link, sem nota nem código.

-  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

#Códigos sem endereço

show: 'qr' imprime só o código ao lado do número da nota, para um cartaz ou uma etiqueta em que o endereço só ocuparia espaço.

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

Erros comuns

Uma nota com código tem pelo menos a altura do código. Quando a linha que leva o link não tem espaço embaixo para a nota na página, a linha passa para a seguinte com a nota; deixe os links fora das últimas linhas de uma página que precisa terminar onde termina, como o primeiro parágrafo da capa deste guia.

Créditos

Texto
Texto original, CC BY 4.0
Imagens
Fontes
Newsreader (SIL OFL 1.1) · Instrument Serif (SIL OFL 1.1) · Archivo (SIL OFL 1.1)
SandboxPDF