feat: add fallback font loading and handling for various languages

This commit is contained in:
alam00000 committed 2026-09-17 13:56:16 +05:30
1 parent 9ae5c93886
commit 5a7927572f
28 files changed
+375 -4

No files matched your search

+51
View File
@@ -3,6 +3,7 @@ import {
getFontUrlForFamily,
languageToFontFamily,
} from '../config/font-mappings.js';
import { needsFontEmbedding } from './freetext-script.js';
const fontCache: Map<string, ArrayBuffer> = new Map();
@@ -134,6 +135,56 @@ export async function getFontForLanguage(lang: string): Promise<ArrayBuffer> {
}
}
const fallbackFontLoads = new Map<string, Promise<boolean>>();
const fallbackFontFamily = (lang: string): string =>
languageToFontFamily[lang] || 'Noto Sans';
export function fallbackFontLanguages(text: string): Set<string> {
const langs = new Set<string>();
for (const ch of text) {
if (needsFontEmbedding(ch)) langs.add(getLanguageForChar(ch));
}
return langs;
}
export function hasFallbackFonts(
loaded: Map<string, Uint8Array>,
langs: Iterable<string>
): boolean {
return [...langs].every((lang) => loaded.has(fallbackFontFamily(lang)));
}
export async function loadFallbackFonts(
into: Map<string, Uint8Array>,
langs: Iterable<string>
): Promise<boolean> {
const results = await Promise.all(
[...langs].map((lang) => {
const family = fallbackFontFamily(lang);
let load = fallbackFontLoads.get(family);
if (!load) {
load = getFontForLanguage(lang)
.then((buffer) => {
if (!fontCache.has(family)) {
throw new Error('Only a substitute font was available');
}
into.set(family, new Uint8Array(buffer));
return true;
})
.catch((error: unknown) => {
fallbackFontLoads.delete(family);
console.warn(`Fallback font unavailable for ${family}`, error);
return false;
});
fallbackFontLoads.set(family, load);
}
return load;
})
);
return results.every(Boolean);
}
export function detectScripts(text: string): string[] {
const scripts = new Set<string>();
+7 -1
View File
@@ -1,5 +1,6 @@
import type { FreeTextSystemFontAnnotation } from '@/types';
import { needsFontEmbedding } from './freetext-script.js';
import { fallbackFontLanguages, loadFallbackFonts } from './font-loader.js';
export const FPDF_ANNOT_FREETEXT = 3;
@@ -153,7 +154,7 @@ function buildRuns(
return [
{
text: contents,
family: 'Helvetica',
family: needsFontEmbedding(contents) ? 'Noto Sans' : 'Helvetica',
size,
rgba: hexToRgba(style?.fontColor ?? '#000000'),
bold: false,
@@ -196,6 +197,7 @@ export async function flattenFreeTextToPageText(
PdfEngine: {
create: () => Promise<EngineLike>;
localFonts: Map<string, Uint8Array>;
fallbackFonts: Map<string, Uint8Array>;
};
scriptFallbackFamilies: (cps: number[]) => string[];
};
@@ -245,6 +247,10 @@ export async function flattenFreeTextToPageText(
return { bytes, flattened: 0 };
}
await primeScriptFonts(mod, found);
await loadFallbackFonts(
mod.PdfEngine.fallbackFonts,
fallbackFontLanguages(found.join('\n'))
);
for (const pageIndex of [...pages].sort((x, y) => x - y)) {
if (pageIndex < 0 || pageIndex >= eng.pageCount) continue;