From 4386b4e5bf11bcbdce3878dd16fb25fb64f9e826 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Fri, 2 Oct 2026 20:04:41 +0000 Subject: [PATCH] Arabic scripture in the packet uses plain alef: U+0671 alef wasla becomes U+0627 in the stored ar strings (the blessing and Mark 5, 13 letters), display only, words and vowels unchanged A device whose fonts lack U+0671 printed it as a box. Only those 13 code points change (0671: 13 -> 0, 0627: +13); the bold verse is still a substring of the passage. check:packet-strings-parity fails on any U+0671 in an ar value (it fails on the previous file). The Arabic scripture blocks also name Arabic faces before serif. --- internal/check-packet-strings-parity.mjs | 16 ++++++++++++++++ library/packet/packet-render.js | 6 ++++++ library/scripts/posters/packet-strings.json | 8 ++++---- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/internal/check-packet-strings-parity.mjs b/internal/check-packet-strings-parity.mjs index 17b2040c7..42becf7fc 100644 --- a/internal/check-packet-strings-parity.mjs +++ b/internal/check-packet-strings-parity.mjs @@ -125,6 +125,22 @@ for (const loc of locales) { } } +// ── ARABIC: NO ALEF WASLA (U+0671) ───────────────────────────────────────────── +// The Van Dyck texts write the article with alef wasla (ٱ). A device whose fonts lack U+0671 printed it as a +// BOX, three times in the blessing and eight in Mark 5. The stored ar text uses plain alef (U+0627), the +// ordinary print form: display only, no word or vowel changes (_meta.note_on_blessing). Asserted on EVERY ar +// value, nested ones included, so the next passage pasted from the same source cannot bring it back. +{ + const hits = [] + const walk = (v, path) => { + if (typeof v === 'string') { const n = [...v].filter((ch) => ch === '\u0671').length; if (n) hits.push(`${path} (${n})`) } + else if (v && typeof v === 'object') for (const [k, x] of Object.entries(v)) walk(x, `${path}.${k}`) + } + walk(cat.ar || {}, 'ar') + if (hits.length) fails.push(`ar carries U+0671 alef wasla, which prints as a box where a font lacks it — store plain alef (U+0627): ${hits.slice(0, 4).join(', ')}`) + else notes.push('ar: no U+0671 alef wasla in any value (plain alef, the print form every Arabic font draws)') +} + console.log('PACKET-STRINGS-PARITY') console.log(` catalog : ${CATALOG.replace(REPO + '/', '')}`) console.log(` locales : ${locales.length} (${locales.join(' ')})`) diff --git a/library/packet/packet-render.js b/library/packet/packet-render.js index e8340274c..71d6fd3c7 100644 --- a/library/packet/packet-render.js +++ b/library/packet/packet-render.js @@ -270,6 +270,12 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de /* Upright, not italic, in scripts with no true italic: a slanted Arabic, Chinese or Devanagari line is a synthesized oblique, which reads as a rendering fault rather than as emphasis. */ .ws-bless:lang(ar),.ws-wheel:lang(ar),.ws-bless:lang(zh),.ws-wheel:lang(zh),.ws-bless:lang(hi),.ws-wheel:lang(hi){font-style:normal} + /* Arabic SCRIPTURE (the blessing, Mark 5) names real Arabic faces before the generic serif: Georgia has no + Arabic, so each Arabic letter falls to the first named face the device has, instead of to whatever + system font the browser picks, which on one test machine had no glyph for a letter the Van Dyck text + used. The text itself now uses only common letters (_meta.note_on_blessing); this is the second guard. + Scoped to the scripture blocks, so no other page's measured fit moves. */ + .ws-bless:lang(ar),.p4-passage:lang(ar),.p4-passage:lang(ar) *{font-family:Georgia,'Palatino Linotype','Noto Naskh Arabic',Amiri,'Traditional Arabic',Tahoma,serif !important} /* ── THE BOUNDED + CENTERED SETS (back matter) ────────────────────────── Two figures side by side. The SVGs carry no width/height — they scale to their diff --git a/library/scripts/posters/packet-strings.json b/library/scripts/posters/packet-strings.json index 351cea9c9..d3bad3d35 100644 --- a/library/scripts/posters/packet-strings.json +++ b/library/scripts/posters/packet-strings.json @@ -6,7 +6,7 @@ "note_on_references": "Scripture REFERENCES (Matthew 27:27, Mark 5:18-20) are deliberately left in English in every language block, so a reader can look them up in their own Bible regardless of the packet's language. ⚠ MEASURED TRADEOFF, not an oversight: on a rendered French page the English book names are visibly foreign next to fully-translated prayer text. If a language wants localised book names, add a `books` map to that block and teach the renderer to use it — do NOT translate the refs inline, because the same strings identify the passage for any future lookup.", "note_on_scripts": "ALL 11 app locales now ship. Script coverage MEASURED by rendering at 48px and READING THE PIXELS: Latin, Cyrillic (ru), Arabic (ar) and CJK (zh) shape correctly with the system Georgia/Palatino/serif stack. DEVANAGARI (hi) does NOT — it renders as tofu boxes — so Noto Serif Devanagari is vendored (OFL) and base64-INLINED into hi packets by the renderer's SCRIPT_FONT map. Inlined, not linked, because a packet is a standalone file that must print offline; ~165KB on a 3.5MB packet, and only for that language. mr and ne map to the same face.\n⚠ TWO MEASUREMENTS LIED ABOUT THIS, so do not automate it cheaply: (1) comparing shaped width against a missing font reported ar and zh BROKEN and hi FINE — exactly backwards, because Devanagari tofu boxes match real glyph advance; (2) an ink-column-profile heuristic scored CORRECTLY-RENDERED Devanagari the same as tofu (3 vs 2 distinct heights), because its baseline bar gives a uniform profile. check:packet-render therefore asserts only that a DECLARED @font-face LOADED — the mechanical half — and leaves glyph correctness to a human reading a screenshot. A detector that blocks working packets is worse than none.", "note_on_rtl": "ar is right-to-left. The renderer emits dir on from an RTL set (packet-render.js), and scripture REFERENCES are wrapped in dir=ltr + unicode-bidi:isolate. Without that isolation the bidi algorithm reorders a reference that STARTS WITH A DIGIT: '1 Peter 5:6-11' rendered as 'Peter 5:6-11 1'. Note the reference LIST then reads right-to-left on an RTL page (first item rightmost) — that is correct for an RTL reader, not a defect.", - "note_on_blessing": "blessing_text is Numbers 6:24-26, printed in full on the lightweight list's first page. Each block carries a PUBLIC-DOMAIN text for its language, verbatim from bible.helloao.org (eBible), with its credit in blessing_credit; only the source's own outer speech marks are replaced by the language's quotation marks. ro and hi have no strict public-domain text on this pipeline, so they omit both keys and print the English per key. bless_refs (en, and ru with Synodal numbering) is the ONE line of further blessing references; book names come from `books`." + "note_on_blessing": "blessing_text is Numbers 6:24-26, printed in full on the lightweight list's first page. Each block carries a PUBLIC-DOMAIN text for its language, verbatim from bible.helloao.org (eBible), with its credit in blessing_credit; only the source's own outer speech marks are replaced by the language's quotation marks. ro and hi have no strict public-domain text on this pipeline, so they omit both keys and print the English per key. bless_refs (en, and ru with Synodal numbering) is the ONE line of further blessing references; book names come from `books`. ar: U+0671 alef wasla → U+0627 alef, display only; words and vowels unchanged (the wasla marks an alef that is silent in connected speech; plain alef is the ordinary print form and renders wherever Arabic renders, where U+0671 printed as a box on devices whose fonts lack it). Applies to every ar value, the Mark 5 passage included; check:packet-strings-parity asserts none remains." }, "en": { "map_page_kicker": "UNENGAGED PEOPLE GROUPS", @@ -2652,9 +2652,9 @@ "pray_blessed": "هل باركتهم؟", "pray_know": "ماذا تريد أن أعرف؟", "pray_do": "ماذا تريد أن أفعل؟", - "point4_passage": "وَلَمَّا دَخَلَ ٱلسَّفِينَةَ طَلَبَ إِلَيْهِ ٱلَّذِي كَانَ مَجْنُونًا أَنْ يَكُونَ مَعَهُ، فَلَمْ يَدَعْهُ يَسُوعُ، بَلْ قَالَ لَهُ: «ٱذْهَبْ إِلَى بَيْتِكَ وَإِلَى أَهْلِكَ، وَأَخْبِرْهُمْ كَمْ صَنَعَ ٱلرَّبُّ بِكَ وَرَحِمَكَ». فَمَضَى وَٱبْتَدَأَ يُنَادِي فِي ٱلْعَشْرِ ٱلْمُدُنِ كَمْ صَنَعَ بِهِ يَسُوعُ. فَتَعَجَّبَ ٱلْجَمِيعُ.", + "point4_passage": "وَلَمَّا دَخَلَ السَّفِينَةَ طَلَبَ إِلَيْهِ الَّذِي كَانَ مَجْنُونًا أَنْ يَكُونَ مَعَهُ، فَلَمْ يَدَعْهُ يَسُوعُ، بَلْ قَالَ لَهُ: «اذْهَبْ إِلَى بَيْتِكَ وَإِلَى أَهْلِكَ، وَأَخْبِرْهُمْ كَمْ صَنَعَ الرَّبُّ بِكَ وَرَحِمَكَ». فَمَضَى وَابْتَدَأَ يُنَادِي فِي الْعَشْرِ الْمُدُنِ كَمْ صَنَعَ بِهِ يَسُوعُ. فَتَعَجَّبَ الْجَمِيعُ.", "point4_passage_credit": "مرقس ٥:‏١٨–٢٠ · Arabic Van Dyck Bible (ملك عام) · ebible.org", - "point4_passage_bold": "فَلَمْ يَدَعْهُ يَسُوعُ، بَلْ قَالَ لَهُ: «ٱذْهَبْ إِلَى بَيْتِكَ وَإِلَى أَهْلِكَ، وَأَخْبِرْهُمْ كَمْ صَنَعَ ٱلرَّبُّ بِكَ وَرَحِمَكَ».", + "point4_passage_bold": "فَلَمْ يَدَعْهُ يَسُوعُ، بَلْ قَالَ لَهُ: «اذْهَبْ إِلَى بَيْتِكَ وَإِلَى أَهْلِكَ، وَأَخْبِرْهُمْ كَمْ صَنَعَ الرَّبُّ بِكَ وَرَحِمَكَ».", "point4_heart_title": "قلب الآب السماوي", "point4_heart_lede": "يحتاجون أن يعرفوا أن أباهم السماوي يحبهم، ويريد أن يخلّصهم، ومن خلالهم يريد أن يخلُص جيرانهم من الشعوب غير المُصل إليها.", "point4_heart_leadin": "المزيد من الآيات عن قلب الآب السماوي", @@ -2753,7 +2753,7 @@ "pace_week": "نحو {n} في الأسبوع", "pace_month": "نحو {n} في الشهر", "pace_note": "الصلاة اليومية لأجل كل الشعوب للمتشفّعين الذين نالوا هذه الموهبة ويخصّصون للصلاة نحو ثلاث ساعات يوميًا. ولمعظمنا، الصلاة الأسبوعية إيقاع صحّي وثابت.", - "blessing_text": "«يُبَارِكُكَ ٱلرَّبُّ وَيَحْرُسُكَ. يُضِيءُ ٱلرَّبُّ بِوَجْهِهِ عَلَيْكَ وَيَرْحَمُكَ. يَرْفَعُ ٱلرَّبُّ وَجْهَهُ عَلَيْكَ وَيَمْنَحُكَ سَلَامًا.»", + "blessing_text": "«يُبَارِكُكَ الرَّبُّ وَيَحْرُسُكَ. يُضِيءُ الرَّبُّ بِوَجْهِهِ عَلَيْكَ وَيَرْحَمُكَ. يَرْفَعُ الرَّبُّ وَجْهَهُ عَلَيْكَ وَيَمْنَحُكَ سَلَامًا.»", "blessing_credit": "العدد ٦:‏٢٤–٢٦ · Arabic Van Dyck Bible (ملك عام) · ebible.org" }, "hi": {