Packet print: the prayer themes at 21px (~8.6pt on letter), the credit and privacy lines 0.28in above the sheet's edge, and a Hindi font stack beyond the vendored face

The fine print (the Metacamp credit on the wheel and points pages, the privacy line) sat 22px (0.14in) from the
edge on letter, inside the band printers cannot ink; it now sits at 50px. The points page gives back the room:
point text 18.5 → 18px, 6 → 5px between points, the Mark 5 passage 17 → 16px, the body 14px higher. Measured in
all 11 languages: the tightest (fr, hi) keep 46px under the last point (check:packet-render's pointsOver gates it).
Hindi (and Marathi, Nepali) name Noto Sans Devanagari, Mangal, Nirmala UI and Kohinoor Devanagari after the
vendored Noto Serif Devanagari, so a print without the inlined face still lands on a Devanagari font.
This commit is contained in:
Doxa Maps 2026-10-02 22:24:43 +00:00
parent 26f9177083
commit 63cef78455

View file

@ -34,13 +34,16 @@ const RTL = new Set(['ar', 'he', 'fa', 'ur', 'ps', 'sd', 'yi']);
// ⚠ A width heuristic got this exactly backwards — it reported Arabic and Chinese
// broken and Hindi fine, because Devanagari tofu boxes happen to match real glyph
// advance. Never infer glyph coverage from measurements; look at the pixels.
const DEVANAGARI_ALSO = "'Noto Sans Devanagari','Mangal','Nirmala UI','Kohinoor Devanagari'";
const SCRIPT_FONT = {
// `also`: the Devanagari faces a device commonly has, named after the vendored one, so a print whose caller
// supplies no fontData still lands on a real Devanagari face rather than on whatever the browser falls to.
hi: { family: 'Noto Serif Devanagari', dir: 'noto-serif-devanagari',
file: 'noto-serif-devanagari-400-devanagari.woff2' },
file: 'noto-serif-devanagari-400-devanagari.woff2', also: DEVANAGARI_ALSO },
mr: { family: 'Noto Serif Devanagari', dir: 'noto-serif-devanagari',
file: 'noto-serif-devanagari-400-devanagari.woff2' }, // Marathi, same script
file: 'noto-serif-devanagari-400-devanagari.woff2', also: DEVANAGARI_ALSO }, // Marathi, same script
ne: { family: 'Noto Serif Devanagari', dir: 'noto-serif-devanagari',
file: 'noto-serif-devanagari-400-devanagari.woff2' }, // Nepali, same script
file: 'noto-serif-devanagari-400-devanagari.woff2', also: DEVANAGARI_ALSO }, // Nepali, same script
};
// The set LAYOUTS (gospel's figures, promoted to library/ by regions). Imported rather than injected
@ -173,7 +176,7 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
// editing 22 hardcoded Georgia declarations. The browser falls through per
// CHARACTER, so Latin text in a Hindi packet still renders in Georgia.
const scriptStack = SF
? `.sheet,.sheet *{font-family:'${SF.family}',Georgia,'Palatino Linotype',serif !important}`
? `.sheet,.sheet *{font-family:'${SF.family}',${SF.also ? SF.also + ',' : ''}Georgia,'Palatino Linotype',serif !important}`
: '';
function css() {
@ -256,7 +259,7 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
font:400 ${WS_DESC_PX}px/1.3 Georgia,serif;margin-top:2px}
/* A TALLY cell: room for several groups of five marks, not a box for one tick. */
.ws-box{justify-self:center;width:${WS_TALLY_W - 10}px;height:${WS_ROW - 22}px;box-sizing:border-box;border:1.5px solid #b0a993;border-radius:6px;background:#fff}
.ws-foot{position:absolute;left:0;right:0;bottom:22px;text-align:center;font:400 12px Georgia,serif;color:#a89f8c}
.ws-foot{position:absolute;left:0;right:0;bottom:50px;text-align:center;font:400 12px Georgia,serif;color:#a89f8c}
/* The first page's description and pace table: quiet type, one rule per section, nothing boxed. */
.ws-sec{margin:0 0 6px;font:700 14px Georgia,serif;letter-spacing:.14em;text-transform:uppercase;color:#8a8577}
/* THE EXPLANATION BAND (explainBand): ruled above and below like the poster's header band, three columns
@ -284,12 +287,12 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
.xb-ex-note{margin-top:4px;font-size:14px;color:#6f6a5c}
/* The prayer themes and scenes above the twelve points: a thin numbered list, ruled off below. */
.pt-themes{margin:0 0 10px;padding:0 0 6px;border-bottom:1.5px solid #c9bfa4}
.pt-th-h{font:400 19px/1.3 Georgia,serif;color:#413d35}
.pt-th-h b,.pt-sc-h b{font:700 20px Georgia,serif;color:#1b1a17;letter-spacing:.02em}
.pt-th-h{font:400 21px/1.3 Georgia,serif;color:#413d35}
.pt-th-h b,.pt-sc-h b{font:700 22px Georgia,serif;color:#1b1a17;letter-spacing:.02em}
.pt-th-i{font-style:italic;color:#6d766e}
.pt-th-list{display:flex;flex-wrap:wrap;gap:1px 20px;margin:2px 0 0;padding:0;list-style:none;font:400 19px/1.3 Georgia,serif;color:#3b3226}
.pt-th-list{display:flex;flex-wrap:wrap;gap:1px 20px;margin:2px 0 0;padding:0;list-style:none;font:400 21px/1.3 Georgia,serif;color:#3b3226}
.pt-n{display:inline-block;min-width:1.3em;font-weight:700;color:#7a3b2e}
.pt-sc-r{font:600 18px Georgia,serif;color:#7a3b2e}
.pt-sc-r{font:600 19px Georgia,serif;color:#7a3b2e}
.ws-cols{margin:0;padding:0;list-style:none}
.ws-cols li{margin:0 0 6px;font:400 19px/1.45 Georgia,serif;color:#413d35}
.ws-cols li b{color:#1b1a17}
@ -396,7 +399,9 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
.d-fb{font:400 13px Georgia,serif;color:#6d766e;margin-top:2px}
.mt{font:400 17px Georgia,serif;color:#6d766e}
/* FINE PRINT — product decision: attribution minimal, fine print small. */
.fine{position:absolute;left:0;right:0;bottom:22px;text-align:center;
/* ≥ 0.25in above the sheet's bottom edge (44px on letter): at 22px the credit line sat 0.14in from the edge,
inside the band a printer cannot ink. */
.fine{position:absolute;left:0;right:0;bottom:50px;text-align:center;
font:400 13px Georgia,serif;color:#a89f8c}
/* THE MAP IS MISSING, and this says so where the map would have been.
⚠ Centred in the SAME region the map occupies, not tucked into the fine print: a reader
@ -1347,10 +1352,11 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
// Mark 5:18-20 and the Father's-heart list share this ONE sheet, in every locale: the sizes below are the
// largest that fit the longest locale. class="points-body" is a TEST HOOK: the page must fit ONE sheet in
// every locale (its scrollHeight within its clientHeight).
// ★ 2026-10-02: the prayer themes now head the page at a size that reads on paper (19px, ~7.7pt), so the
// points give back a little: 20 → 18.5px text and 10 → 6px between points (the heading stays 22). Measured in
// all 11 locales: the tightest (fr) keeps 43px under the last point.
const PT = { head: 22, sub: 19, text: 18.5, refs: 16, gap: 6, passage: 17 };
// ★ 2026-10-02: the prayer themes now head the page at a size that reads on paper (21px, ~8.6pt on letter) and
// the credit line sits 0.28in above the sheet's edge, so the points give back a little: 20 → 18px text, 10 → 5px
// between points, the Mark 5 passage 18 → 16px, and the body starts 14px higher (the heading stays 22). Measured
// in all 11 locales: the tightest (fr, hi) keep 46px under the last point; check:packet-render fails overflow.
const PT = { head: 22, sub: 19, text: 18, refs: 16, gap: 5, passage: 16 };
// ⚠ ISOLATE EACH REFERENCE FOR BIDI. Scripture references stay in English
// (a reader looks them up in their own Bible), so on an RTL page they are
// LTR runs inside an RTL paragraph. Without isolation the Unicode bidi
@ -1405,7 +1411,7 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
<div class="points-title" style="font:700 40px Georgia,serif;color:#1b1a17">${esc(t('points_title'))}</div>
<div style="font:italic 18px Georgia,serif;color:#6d766e;margin-top:6px">${esc(t('points_intro'))}</div>
</div>
<div class="points-body" style="position:absolute;left:${M}px;top:${M + 96}px;right:${M}px;bottom:52px">${themesBlock()}${rows}</div>
<div class="points-body" style="position:absolute;left:${M}px;top:${M + 82}px;right:${M}px;bottom:80px">${themesBlock()}${rows}</div>
<div class="fine">${esc(t('points_attrib'))}</div>`);
}