fix: register language-specific Noto fallback fonts for non-Latin scripts (#3158)

* fix: use language-specific Noto fonts for CJK PDF fallback

* feat: extend fallback to Arabic/Hebrew/Thai

---------

Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
This commit is contained in:
roberto
2026-06-17 20:37:09 +09:00
committed by GitHub
parent a523e13bfd
commit 2317a82106
6 changed files with 429 additions and 34 deletions
+41
View File
@@ -70,6 +70,47 @@ export function isCJKLocale(locale: Locale): boolean {
return locale === "zh-CN" || locale === "zh-TW" || locale === "ja-JP" || locale === "ko-KR";
}
// A writing system that needs a dedicated fallback font in the PDF renderer,
// because react-pdf (unlike a browser) has no automatic system-font fallback:
// a glyph only renders if a registered font contains it. We pick the matching
// Noto font per script so e.g. Hangul → Noto KR, Arabic → Noto Arabic, instead
// of falling back to a Latin/Han-only font and producing tofu.
export type Script = "hangul" | "kana" | "han-traditional" | "han-simplified" | "arabic" | "hebrew" | "thai";
// The CJK subset of `Script`. CJK needs extra per-character line breaking that
// must NOT be applied to Arabic (cursive, joined letters) or Thai (combining
// marks), so callers gate line-breaking on this rather than on `Script`.
export const cjkScripts: readonly Script[] = ["hangul", "kana", "han-traditional", "han-simplified"];
export function isCjkScript(script: Script): boolean {
return cjkScripts.includes(script);
}
// The script a locale primarily uses, used to order the fallback stack so the
// dominant language renders with its native font. Persian (fa-IR) uses the
// Arabic script.
export function getLocaleScript(locale?: Locale): Script | null {
switch (locale) {
case "ko-KR":
return "hangul";
case "ja-JP":
return "kana";
case "zh-TW":
return "han-traditional";
case "zh-CN":
return "han-simplified";
case "ar-SA":
case "fa-IR":
return "arabic";
case "he-IL":
return "hebrew";
case "th-TH":
return "thai";
default:
return null;
}
}
const RTL_LANGUAGES = new Set([
"ar", // Arabic
"ckb", // Kurdish (Sorani)