باختصار
دليل مطبوع صغير لخمس لوحات. حيث يذكر النص صفحة ويب، يقود رقم صغير إلى أسفل الصفحة، وهناك رمز مربع يفتح تلك الصفحة على الهاتف.
ما الذي ستنضده
دليل جيب إلى القاعات الهولندية في متحف المتروبوليتان للفنون: خمس لوحات، وساعة سيرًا على الأقدام، ولوحة واحدة في كل صفحة. الغلاف هو فتاة فيرمير عند النافذة، مقطوعة عند الحافة العليا. تبدأ كل محطة برقم القاعة والعنوان والرسام، ثم اللوحة وبطاقتها إلى جانبها، ثم ثلاث فقرات قصيرة. يحيل النص إلى فهرس المتحف كما تفعل أي صفحة ويب. وعلى الورق يصير كل رابط رقمًا صغيرًا، وفي أسفل الصفحة تعطي الحاشية العنوان القصير ورمز QR يوجّه إليه الزائر هاتفه وهو واقف أمام اللوحة.
تجيب هذه الوصفة عن
- كيف أطبع عناوين الويب لروابط الكتاب حواشيَ مرقّمة أو رموز QR يمسحها القارئ؟
الجواب المختصر
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') } };
المكونات
- الميزات
- عناوين الروابط في النسخة المطبوعةالحواشي السفليةصفحات افتتاح مصمَّمةسمات العنوانالصور في تصاميم الصفحةكلمات الدليل في الترويساتالترويسات وأرقام الصفحاتالترويسات بحسب دور الصفحةأشكال في هذا الموضع بالضبطتعليقات جانبيةأنواع موارد مخصّصةسطور المصدر والحقوقلوحة ألوان دلاليةالتصدير إلى PDF
- الخطوط
- Newsreader, Instrument Serif, Archivo (SIL OFL 1.1)
- الأصول
hals-1200.jpgrembrandt-1200.jpgruisdael-1400.jpgsteen-1200.jpgvermeer-1600.jpgvermeer-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)
طريقة التحضير
#١ · اكتب روابط، واطبع حواشي
الشيفرة هي الجواب المختصر في الأعلى. يحتفظ Markdown بروابط عادية، [The catalogue entry](https://www.metmuseum.org/…/436616)، ويحوّل links.print: 'notes' كلًّا منها إلى حاشية مرقّمة فيها العنوان والرمز (الروابط في النسخة المطبوعة). هذه الحواشي حواشٍ سفلية: يرقّمها numbering: 'document' على امتداد الجولة كلها، فيرى القارئ الأرقام من ١ إلى ٧، ويدع repeat: 'first' المحطة الأخيرة تحيل إلى لوحة هالس برقم حاشيتها ٣ بدل أن تطبع الرمز نفسه مرتين. وفي ملف PDF يُرسم الرمز رسمًا متجهيًّا، وهو نفسه رابط.

#٢ · محطة واحدة في كل صفحة
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) },
] } };
كل محطة عنوان من المستوى الثاني يفتح breakBefore الخاص به صفحة جديدة، زوجية أو فردية، فيقلب الزائر ورقة واحدة لكل لوحة. رقم القاعة والرسام سمتان للعنوان؛ يضعهما التصميم فوق العنوان وتحته، ويبقي minHeight اللوحة على الارتفاع نفسه في كل صفحة.
#٣ · البطاقة بجانب اللوحة
// 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' التعليق وحاشية المصدر بجانب اللوحة، كما يعلّق المتحف بطاقته على الجدار، ويعطي width كل لوحة حصتها من عرض السطر: أقل للوحة هالس الطويلة، وأكثر للوحة رويسدال العريضة، فتبقى للصفحات الخمس كمية نص متقاربة. تتألف البطاقة من الخامة، والمقاس بالسنتيمترات مع مسافات غير فاصلة، وسطر الاقتناء الخاص بالمتحف، مع فاصلة عشرية في الطبعة الإسبانية.
#٤ · المحطة في الترويسة
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} عنوان المحطة التي تبدأ في الصفحة، فتسمّي الصفحة الفردية اللوحة والزوجية الدليل. ولا تصل أي ترويسة إلى الغلاف، لأن دوره دور صفحة افتتاحية.
الوصفة كاملة
// ═══ 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),
يعمل ملف script.js المجمّع كما هو: الصقه في سكربت الوحدة (module) لأي صفحة، أو افتح الوصفة على CodePen. مجلد الوصفة على GitHub ↗ (يفتح في تبويب جديد)
تنويعات
#اطبع العنوان داخل السطر
لدليل يُقرأ في البيت، يضيف 'inline' العنوان بين قوسين بعد نص الرابط، بلا حاشية ولا رمز.
- 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#رموز بلا عناوين
يطبع show: 'qr' الرمز وحده بجانب رقم الحاشية، لملصق أو بطاقة لا يفعل فيها العنوان سوى أخذ مساحة.
- show: 'both', // the short address for reading, the code for the phone
+ show: 'qr', // the code aloneأخطاء شائعة
الحاشية التي فيها رمز لا يقل ارتفاعها عن ارتفاع الرمز. فإذا لم يجد السطر الذي يحمل الرابط مكانًا تحته لحاشيته في الصفحة، انتقل السطر مع حاشيته إلى الصفحة التالية. لذلك أبقِ الروابط خارج الأسطر الأخيرة من صفحة يجب أن تنتهي حيث تنتهي، كما في الفقرة الأولى من غلاف هذا الدليل.
الحقوق
- الوصفة
- Ignacio Ferro
- النص
- نص أصلي, CC BY 4.0
- الصور
- 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
- الخطوط
- Newsreader (SIL OFL 1.1) · Instrument Serif (SIL OFL 1.1) · Archivo (SIL OFL 1.1)
- الشيفرة
- MIT، مثل Postext
حرّر هذا الشرح ↗ (يفتح في تبويب جديد)مجلد الوصفة على GitHub ↗ (يفتح في تبويب جديد)


