انتقل إلى المحتوى الرئيسي
الوصفة رقم 176

دليل الوصفات · الفصل 2 · الحروف والنص

أرقام قديمة الطراز ومصطفّة وجدولية

حسابات ثورو من «والدن»: يعطي figureStyle العناوين بالحروف الكبيرة الصغيرة أرقامًا مصطفّة والحسابات أرقامًا جدولية، ويبقي المتن على أرقامه قديمة الطراز.

في هذه الصفحة
المُخرَج
Canvas · PDF
المستوى
متوسط
Postext
اختُبرت مع Postext 1.65.0
تتطلب ≥ 1.65.0 · postext-pdf ≥ 1.65.0
الترخيص
حُدّثت في 31 أكتوبر 2026
الشيفرة MIT · النص CC BY 4.0
  • نموذج باللغة الإنجليزية: لا توجد طبعة عربية بعد
  • مقاس القص 140 × 216 مم
  • عمود واحد
  • Alegreya 10.8/14.5
  • Alegreya SC
  • Alegreya SC@lnum
  • Alegreya Sans
  • Alegreya@tnum
  • 6 صفحات
  • المستوى
  • Postext 1.65.0
  • أُخرجت في 124 ms
  • أسطر الشيفرة: 160

باختصار

طبعة صغيرة من حسابات البيت التي نشرها ثورو في «والدن». أرقام المتن صغيرة كالحروف الصغيرة، وسنوات العناوين بارتفاع الحروف الكبيرة، ومبالغ الحسابات تصطفّ في أعمدة.

ما الذي ستنضده

طبعة صغيرة من الحسابات التي نشرها ثورو في الفصل الأول من «والدن»: كم كلّفه بيته، وكم درّ عليه حقل الفاصولياء، وماذا أكل في ثمانية أشهر. تُنضَّد على صفحة مقاسها 140 × 216 مم بعائلة Alegreya، وفي الافتتاح حفر الكوخ من طبعة 1854. تتغيّر الأرقام بحسب عملها. في المتن تأتي March, 1845 و*$13.34* بأرقام قديمة الطراز تصعد وتنزل كالحروف الصغيرة، فلا تبرز التواريخ من السطر. العناوين بالحروف الكبيرة الصغيرة، وسنواتها بارتفاع الحروف الكبيرة. وفي الحسابات لكل رقم العرض نفسه، فتقع سنتات ثلاثة عشر سطرًا من الألواح والعوارض والمسامير بعضها تحت بعض حتى المجموع.

تجيب هذه الوصفة عن

  • كيف أستعمل الأرقام قديمة الطراز في المتن والأرقام المصطفّة في العناوين والأرقام الجدولية في الجداول؟
  • كيف أنضد رسومًا غير مرقّمة: زخارف، وحليات، وشعارات؟

الجواب المختصر

script.js · الأسطر 34–55في الشيفرة الكاملة
// 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),
};

المكونات

الخطوط
Alegreya, Alegreya SC, Alegreya Sans (SIL OFL 1.1)
الأصول
  • 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), ملك عام)

طريقة التحضير

#1 · نمط أرقام لكل نمط نصّي

شيفرة هذه الخطوة هي الجواب المختصر أعلاه. يطلب figureStyle من خطّ النمط أرقامه في OpenType (أنماط الأرقام). المتن لا يطلب شيئًا، لأن أرقام Alegreya الأصلية قديمة الطراز ونسبية العرض: عرض 1 هو 342 وحدة وعرض 0 هو 533. وتطلب عناوين المستوى الثاني من Alegreya SC القيمة 'lining'، أي أرقام lnum، فتأتي بارتفاع الحروف الكبيرة حولها. ويطلب نمط الجدول 'tabular'، أي tnum في الخط: تحتفظ الأرقام بشكلها القديم وتأخذ كلها العرض نفسه، 450 وحدة، فيُقرأ عمود الأسعار من أعلى إلى أسفل. ومع 'lining-tabular' تصير بارتفاع الحروف الكبيرة (انظر «تنويعات»).

#2 · الحسابات جداول من خمسة أعمدة

script.js · الأسطر 59–122في الشيفرة الكاملة
// 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
Boards,	$	8.03	½	mostly shanty boards.
Refuse shingles for roof sides,		4.00
Laths,		1.25
Two second-hand windows with glass,		2.43
One thousand old brick,		4.00
Two casks of lime,		2.40		That was high.
Hair,		0.31		More than I needed.
Mantle-tree iron,		0.15
Nails,		3.90
Hinges and screws,		0.14
Latch,		0.10
Chalk,		0.01
Transportation,		1.40		I carried a good part on my back.
=In all,	$	28.12	½
=== farm
	$	23.44
Deducting the outgoes,		14.72	½
=There are left,	$	8.71	½
=== food
Rice,	$	1.73	½
Molasses,		1.73		Cheapest form of the saccharine.
Rye meal,		1.04	¾
Indian meal,		0.99	¾	Cheaper than rye.
Pork,		0.22
+All experiments which failed:
Flour,		0.88		Costs more than Indian meal, both money and trouble.
Sugar,		0.80
Lard,		0.65
Apples,		0.25
Dried apple,		0.22
Sweet potatoes,		0.10
One pumpkin,		0.06
One watermelon,		0.02
Salt,		0.03
=== outgoes
House,	$	28.12	½
Farm one year,		14.72	½
Food eight months,		8.74
Clothing, etc., eight months,		8.40	¾
Oil, &c., eight months,		2.00
=In all,	$	61.99	¾
=== income
	$	23.44
Earned by day-labor,		13.34
=In all,	$	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'))]));

تُحفظ حسابات ثورو بصيغة TSV في content.accounts.en.md، كتلة لكل حساب، فتترجم الطبعة الإسبانية البنود وتستبدل الفاصلة بالنقطة العشرية دون أن تمسّ الشيفرة. في كل صف البند وعلامة الدولار والمبلغ والكسر والملاحظة. للأنصاف والأرباع عمود خاص: عرض ½ هو 713 وحدة وعرض ¾ هو 689، أكثر من الرقم الجدولي، وليس في Alegreya فراغ بعرض الرقم تُكمَّل به المبالغ الأخرى. المجموع مصنوع من خلايا رأس، فيرسم groupRules فوقه خطًا رفيعًا، كما رسم طابع ثورو خطًا قبل In all. تُكتب علامة الدولار \$، لأن $ وحدها تفتح معادلة في الخلية كما في المتن. نوع المورد account بلا رقم ولا بادئة تعليق، وموضعه الافتراضي 'here'، فيقع كل حساب حيث تقدّمه جملة ثورو.

#3 · الافتتاح يعلن أرقامه بنفسه

script.js · الأسطر 126–144في الشيفرة الكاملة
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) } } },
] } };

لنص التصميم عائلته الخاصة، فلا تصل إليه أرقام المتن. Concord, 1845–1847 بخط Alegreya SC ويطلب 'lining' بنفسه، مثل العناوين. الصورة الصغيرة هي حفر صفحة العنوان في طبعة 1854، مقصوصة ومضبوطة أسود على أبيض؛ ويأخذ العنوان الفرعي والعنوان والسنوات عرض السطر ويتوسّطون تحتها.

#4 · ترويسات تذكر القسم في الصفحة الفردية

script.js · الأسطر 148–161في الشيفرة الكاملة
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) })] };

تحمل الصفحة الزوجية عنوان الكتاب، والفردية القسم الجاري {firstMark.h2}، فتقول الصفحة 3 The Bean-Field, 1845. أرقام الصفحات بخط Alegreya وتحتفظ بأرقامه قديمة الطراز.

الصفحة 2: أرقام حساب البيت قديمة الطراز ومتساوية العرض، فتصطفّ السنتات حتى المجموع.

الوصفة كاملة

Sandbox
// ═══ 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 = 'en'; // @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.

يعمل ملف script.js المجمّع كما هو: الصقه في سكربت الوحدة (module) لأي صفحة، أو افتح الوصفة على CodePen. مجلد الوصفة على GitHub ↗ (يفتح في تبويب جديد)

تنويعات

#نضّد الحسابات بأرقام مصطفّة

كثيرًا ما يضع دفتر الحسابات أو قائمة الأسعار في كتاب حديث أرقامه بارتفاع الحروف الكبيرة.

-  figureStyle: 'tabular', bodyFontFamily: TEXT, bodyFontSize: pt(9.8), bodyColor: col('ink'),
+  figureStyle: 'lining-tabular', bodyFontFamily: TEXT, bodyFontSize: pt(9.8), bodyColor: col('ink'),

#نضّد أرقام الصفحات بأرقام مصطفّة

تناسب أرقام الصفحات المصطفّة ترويسات مكتوبة بالحروف الكبيرة.

-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',

أخطاء شائعة

  • كثيرًا ما تُسقط ملفات latin التي يقدّمها Fontsource الخاصية onum: لا تحتفظ Lora وEB Garamond وSource Serif 4 وCrimson Pro وNewsreader وLiterata إلا بـpnum وtnum. مع هذه الملفات لا يغيّر figureStyle: 'oldstyle' شيئًا ويبقى المتن بأرقام مصطفّة. اختر خطًا أرقامه الأصلية قديمة الطراز، مثل Alegreya أو Gelasio أو Cormorant Garamond، وفيها lnum للعناوين، أو حمّل الملفات الكاملة من المسبك.
  • علامة $ في Markdown أو في خلية جدول تفتح معادلة داخل السطر. اكتب \$14.72½ في المتن و\$ في الخلية.

الحقوق

الوصفة
Ignacio Ferro
النص
  • 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 · ملك عام
  • The headnote, and the Spanish translation of the passages and the accounts · Ignacio Ferro · CC BY 4.0
الصور
الخطوط
Alegreya (SIL OFL 1.1) · Alegreya SC (SIL OFL 1.1) · Alegreya Sans (SIL OFL 1.1)
الشيفرة
MIT، مثل Postext
SandboxPDF