mirror of
https://github.com/alam00000/bentopdf.git
synced 2026-11-13 17:54:19 +01:00
fix(ocr): Arabic+Latin font fix and added tests
This commit is contained in:
1 parent
58a0053778
commit
0ce043c18e
8 files changed
+356
-165
No files matched your search
@@ -32,14 +32,26 @@ describe('compare OCR page recognition', () => {
|
||||
|
||||
mockWorker.recognize.mockResolvedValue({
|
||||
data: {
|
||||
words: [
|
||||
blocks: [
|
||||
{
|
||||
text: 'Hello',
|
||||
bbox: { x0: 10, y0: 20, x1: 60, y1: 40 },
|
||||
},
|
||||
{
|
||||
text: 'world',
|
||||
bbox: { x0: 70, y0: 20, x1: 120, y1: 40 },
|
||||
paragraphs: [
|
||||
{
|
||||
lines: [
|
||||
{
|
||||
words: [
|
||||
{
|
||||
text: 'Hello',
|
||||
bbox: { x0: 10, y0: 20, x1: 60, y1: 40 },
|
||||
},
|
||||
{
|
||||
text: 'world',
|
||||
bbox: { x0: 70, y0: 20, x1: 120, y1: 40 },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -52,7 +64,11 @@ describe('compare OCR page recognition', () => {
|
||||
1,
|
||||
expect.any(Function)
|
||||
);
|
||||
expect(mockWorker.recognize).toHaveBeenCalledWith(canvas);
|
||||
expect(mockWorker.recognize).toHaveBeenCalledWith(
|
||||
canvas,
|
||||
{},
|
||||
{ text: true, blocks: true }
|
||||
);
|
||||
expect(mockWorker.terminate).toHaveBeenCalledTimes(1);
|
||||
expect(model.source).toBe('ocr');
|
||||
expect(model.hasText).toBe(true);
|
||||
|
||||
@@ -12,11 +12,11 @@ describe('font-loader', () => {
|
||||
|
||||
it('builds a self-hosted font URL when an OCR font base URL is configured', () => {
|
||||
expect(
|
||||
resolveFontUrl('Noto Sans Arabic', {
|
||||
resolveFontUrl('Noto Naskh Arabic', {
|
||||
VITE_OCR_FONT_BASE_URL: 'https://internal.example.com/wasm/ocr/fonts/',
|
||||
})
|
||||
).toBe(
|
||||
'https://internal.example.com/wasm/ocr/fonts/NotoSansArabic-Regular.ttf'
|
||||
'https://internal.example.com/wasm/ocr/fonts/NotoNaskhArabic-Regular.ttf'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
normalizeText,
|
||||
calculateWordTransform,
|
||||
calculateSpaceTransform,
|
||||
calculateLineFontSize,
|
||||
} from '../js/utils/hocr-transform';
|
||||
import type { OcrWord } from '@/types';
|
||||
|
||||
@@ -298,6 +299,32 @@ describe('hocr-transform', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculateLineFontSize', () => {
|
||||
it('returns line height + baseline intercept', () => {
|
||||
const line = {
|
||||
bbox: { x0: 0, y0: 100, x1: 500, y1: 130 },
|
||||
baseline: { slope: 0, intercept: 2 },
|
||||
textangle: 0,
|
||||
words: [] as OcrWord[],
|
||||
direction: 'ltr' as const,
|
||||
injectWordBreaks: true,
|
||||
};
|
||||
expect(calculateLineFontSize(line)).toBe(32);
|
||||
});
|
||||
|
||||
it('clamps to a minimum of 1 for degenerate lines', () => {
|
||||
const line = {
|
||||
bbox: { x0: 0, y0: 0, x1: 0, y1: 0 },
|
||||
baseline: { slope: 0, intercept: 0 },
|
||||
textangle: 0,
|
||||
words: [] as OcrWord[],
|
||||
direction: 'ltr' as const,
|
||||
injectWordBreaks: true,
|
||||
};
|
||||
expect(calculateLineFontSize(line)).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculateSpaceTransform', () => {
|
||||
const baseLine = {
|
||||
bbox: { x0: 0, y0: 100, x1: 500, y1: 130 },
|
||||
|
||||
+16
-11
@@ -1,4 +1,4 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const {
|
||||
createConfiguredTesseractWorker,
|
||||
@@ -23,6 +23,7 @@ const mockWorker = {
|
||||
const mockPdfPage = {
|
||||
getViewport: vi.fn(() => ({ width: 200, height: 100 })),
|
||||
render: vi.fn(() => ({ promise: Promise.resolve() })),
|
||||
cleanup: vi.fn(),
|
||||
};
|
||||
|
||||
const mockPdfOutputPage = {
|
||||
@@ -177,27 +178,31 @@ describe('performOcr', () => {
|
||||
expect.any(Function)
|
||||
);
|
||||
expect(mockWorker.setParameters).toHaveBeenCalledWith({
|
||||
tessjs_create_hocr: '1',
|
||||
tessedit_pageseg_mode: '3',
|
||||
});
|
||||
expect(mockWorker.recognize).toHaveBeenCalledTimes(1);
|
||||
expect(mockWorker.terminate).toHaveBeenCalledTimes(1);
|
||||
expect(result.fullText).toContain('Recognized text');
|
||||
expect(result.pdfBytes).toBeInstanceOf(Uint8Array);
|
||||
expect(result.warnings).toEqual([]);
|
||||
});
|
||||
|
||||
it('terminates the Tesseract worker when OCR fails', async () => {
|
||||
it('records a warning and still terminates the worker when OCR fails on a page', async () => {
|
||||
mockWorker.recognize.mockRejectedValueOnce(new Error('ocr failed'));
|
||||
|
||||
await expect(
|
||||
performOcr(new Uint8Array([1, 2, 3]), {
|
||||
language: 'eng',
|
||||
resolution: 2,
|
||||
binarize: false,
|
||||
whitelist: '',
|
||||
})
|
||||
).rejects.toThrow('ocr failed');
|
||||
const result = await performOcr(new Uint8Array([1, 2, 3]), {
|
||||
language: 'eng',
|
||||
resolution: 2,
|
||||
binarize: false,
|
||||
whitelist: '',
|
||||
});
|
||||
|
||||
expect(mockWorker.terminate).toHaveBeenCalledTimes(1);
|
||||
expect(result.warnings).toHaveLength(1);
|
||||
expect(result.warnings[0]).toMatchObject({
|
||||
page: 1,
|
||||
kind: 'recognize-error',
|
||||
});
|
||||
expect(result.warnings[0].message).toContain('ocr failed');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user