mirror of
https://github.com/alam00000/bentopdf.git
synced 2026-11-10 17:14:34 +01:00
feat: add fallback font loading and handling for various languages
This commit is contained in:
1 parent
9ae5c93886
commit
5a7927572f
28 files changed
+375
-4
No files matched your search
@@ -1,7 +1,12 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { getFontAssetFileName } from '../js/config/font-mappings';
|
||||
import { resolveFontUrl } from '../js/utils/font-loader';
|
||||
import {
|
||||
fallbackFontLanguages,
|
||||
hasFallbackFonts,
|
||||
loadFallbackFonts,
|
||||
resolveFontUrl,
|
||||
} from '../js/utils/font-loader';
|
||||
|
||||
describe('font-loader', () => {
|
||||
it('uses the default public font URL when no offline font base URL is configured', () => {
|
||||
@@ -25,4 +30,49 @@ describe('font-loader', () => {
|
||||
'NotoSansCJKsc-Regular.otf'
|
||||
);
|
||||
});
|
||||
|
||||
describe('fallback fonts', () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it('needs no font for text the standard fonts can encode', () => {
|
||||
expect(fallbackFontLanguages('Plain café š € “quotes”').size).toBe(0);
|
||||
expect([...fallbackFontLanguages('Příliš ŠĐČĆŽ')]).toEqual(['eng']);
|
||||
});
|
||||
|
||||
it('downloads a missing font once and reports success', async () => {
|
||||
const fetchFont = vi.fn(async () => new Response(new Uint8Array([1])));
|
||||
vi.stubGlobal('fetch', fetchFont);
|
||||
const fonts = new Map<string, Uint8Array>();
|
||||
const results = await Promise.all([
|
||||
loadFallbackFonts(fonts, ['eng']),
|
||||
loadFallbackFonts(fonts, ['eng']),
|
||||
]);
|
||||
expect(results).toEqual([true, true]);
|
||||
expect(fetchFont).toHaveBeenCalledTimes(1);
|
||||
expect(hasFallbackFonts(fonts, ['eng'])).toBe(true);
|
||||
});
|
||||
|
||||
it('reports failure when only a substitute font arrives, then retries', async () => {
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn(async (url: string) =>
|
||||
url.includes('Arabic')
|
||||
? Promise.reject(new Error('offline'))
|
||||
: new Response(new Uint8Array([1]))
|
||||
)
|
||||
);
|
||||
const fonts = new Map<string, Uint8Array>();
|
||||
expect(await loadFallbackFonts(fonts, ['ara'])).toBe(false);
|
||||
expect(hasFallbackFonts(fonts, ['ara'])).toBe(false);
|
||||
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn(async () => new Response(new Uint8Array([1])))
|
||||
);
|
||||
expect(await loadFallbackFonts(fonts, ['ara'])).toBe(true);
|
||||
expect(hasFallbackFonts(fonts, ['ara'])).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,9 @@
|
||||
// @vitest-environment node
|
||||
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
||||
import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import { flattenFreeTextToPageText } from '@/js/utils/freetext-flatten';
|
||||
import type { FreeTextSystemFontAnnotation } from '@/types';
|
||||
import {
|
||||
createEngine,
|
||||
modelOf,
|
||||
@@ -113,6 +116,49 @@ describe('flattening FreeText annotations into page text', () => {
|
||||
expect(target?.lockReason).toBe(0);
|
||||
});
|
||||
|
||||
it('keeps Czech and Croatian letters when no system font is available', async () => {
|
||||
const font = readFileSync(
|
||||
resolve(
|
||||
'node_modules/pdfjs-dist/standard_fonts/LiberationSans-Regular.ttf'
|
||||
)
|
||||
);
|
||||
const fetchFont = vi.fn(async () => new Response(font));
|
||||
vi.stubGlobal('fetch', fetchFont);
|
||||
const czech = 'Příliš žluťoučký kůň, úpěl ďábelské ódy.!?';
|
||||
const croatian = 'ŠĐČĆŽ šđčćž';
|
||||
const style = (id: string, contents: string) =>
|
||||
({
|
||||
id,
|
||||
pageIndex: 0,
|
||||
contents,
|
||||
fontSize: 12,
|
||||
fontColor: '#000000',
|
||||
textAlign: 0,
|
||||
verticalAlign: 0,
|
||||
opacity: 1,
|
||||
rect: { origin: { x: 0, y: 0 }, size: { width: 0, height: 0 } },
|
||||
fontPostScriptName: '',
|
||||
}) satisfies FreeTextSystemFontAnnotation;
|
||||
try {
|
||||
const source = freeTextAnnotationPdf([
|
||||
{ rect: [72, 600, 540, 640], contents: 'x', name: 'cs' },
|
||||
{ rect: [72, 500, 540, 540], contents: 'x', name: 'hr' },
|
||||
]);
|
||||
const { bytes, flattened } = await flattenFreeTextToPageText(
|
||||
source,
|
||||
[style('cs', czech), style('hr', croatian)],
|
||||
1
|
||||
);
|
||||
expect(flattened).toBe(2);
|
||||
expect(fetchFont).toHaveBeenCalledTimes(1);
|
||||
const text = allText(modelOf(engine, bytes));
|
||||
expect(text).toContain(czech);
|
||||
expect(text).toContain(croatian);
|
||||
} finally {
|
||||
vi.unstubAllGlobals();
|
||||
}
|
||||
});
|
||||
|
||||
it('returns the original bytes when the document cannot be opened', async () => {
|
||||
const junk = new Uint8Array([1, 2, 3, 4, 5]);
|
||||
const result = await flattenFreeTextToPageText(junk, [], 1);
|
||||
|
||||
Reference in new issue
Block a user