Skip to content

Commit 2896ebf

Browse files
committed
feat: add local formula OCR and copy in reading mode
1 parent ce47d2d commit 2896ebf

31 files changed

Lines changed: 1997 additions & 7 deletions

‎bun.lock‎

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎electron/build/package.mjs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ const paths = await packager({
9595
arch,
9696
out: test ? '/tmp/pdfmathreader-slim-test-build' : resolve(root, 'release'),
9797
overwrite: true,
98-
asar: { unpack: '**/*.node' },
98+
asar: { unpack: '**/*.node', unpackDir: '**/onnxruntime-node/bin' },
9999
ignore: [
100100
/^\/release(?:\/|$)/,
101101
/^\/\.cache(?:\/|$)/,

‎electron/build/production-stage.mjs‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,24 @@ export async function stageApplication({
100100
: phase === 'dependencies'
101101
? ['express', 'pdf-lib', '@firecrawl/pdf-inspector']
102102
: ['@firecrawl/pdf-inspector', 'pdf-lib'];
103+
if (!names.includes('onnxruntime-node')) names.push('onnxruntime-node');
103104
if (test && !names.includes('pdf-lib')) names.push('pdf-lib');
104105
const fallback = !hasNativeInspector(platform, arch);
105106
if (fallback && !names.includes('pdfjs-dist')) names.push('pdfjs-dist');
106107
if (fallback && !names.includes('@thednp/dommatrix')) names.push('@thednp/dommatrix');
107108
const copied = await copyDependencies(root, stage, names, fallback);
109+
// Ship only the target CPU runtime; other architectures multiply bundle size.
110+
const onnxBin = join(stage, 'node_modules/onnxruntime-node/bin/napi-v6');
111+
const { rm: removeRuntime } = await import('node:fs/promises');
112+
for (const target of await readdir(onnxBin)) {
113+
if (target !== platform)
114+
await removeRuntime(join(onnxBin, target), { recursive: true, force: true });
115+
else
116+
for (const targetArch of await readdir(join(onnxBin, target)))
117+
if (targetArch !== arch)
118+
await removeRuntime(join(onnxBin, target, targetArch), { recursive: true, force: true });
119+
}
120+
108121
const browserLicenses = [
109122
'@fluentui/web-components',
110123
'@fluentui/tokens',
@@ -168,6 +181,7 @@ export async function stageApplication({
168181
mainFields: ['module', 'main'],
169182
external: [
170183
'electron',
184+
'onnxruntime-node',
171185
// Keep PDF editing outside the startup bundles so dynamic imports also
172186
// avoid parsing the library when only the Start Page is requested.
173187
'pdf-lib',

‎electron/main/services/preferences.mjs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const DEFAULT_PREFERENCES = Object.freeze({
2929
documentOpenMode: 'translation',
3030
interactionMode: 'reading',
3131
restoreDocuments: true,
32+
formulaOcrEnabled: false,
3233
reduceResourceUsage: true,
3334
reduceBackgroundFrameRate: true,
3435
reuseTranslations: true,
@@ -227,6 +228,7 @@ const VALIDATORS = {
227228
reduceResourceUsage: (value) => typeof value === 'boolean',
228229
reduceBackgroundFrameRate: (value) => typeof value === 'boolean',
229230
reuseTranslations: (value) => typeof value === 'boolean',
231+
formulaOcrEnabled: (value) => typeof value === 'boolean',
230232
emphasizeTopicSentences: (value) => typeof value === 'boolean',
231233
emphasizeInformation: (value) => typeof value === 'boolean',
232234
emphasizeResearchFindings: (value) => typeof value === 'boolean',

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@firecrawl/pdf-inspector": "latest",
2525
"@thednp/dommatrix": "3.1.2",
2626
"express": "^5.1.0",
27+
"onnxruntime-node": "1.22.0",
2728
"pdf-lib": "^1.17.1",
2829
"pdfjs-dist": "^6.3.289"
2930
},

‎server/app.mjs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ import {
4141
import { registerDocumentRoutes } from './http/document-routes.mjs';
4242
import { registerKernelRoutes } from './http/kernel-routes.mjs';
4343
import { registerTranslationRoutes } from './http/translation-routes.mjs';
44+
import { registerFormulaOcrRoutes } from './http/formula-ocr-routes.mjs';
45+
import { createFormulaOcrService } from './formula/formula-ocr-service.mjs';
4446

4547
export async function startServer({
4648
port = 5173,
@@ -96,6 +98,9 @@ export async function startServer({
9698
args: [],
9799
}),
98100
});
101+
const formulaOcr = createFormulaOcrService({
102+
cacheDir: join(cacheDir, '..', 'models'),
103+
});
99104
const documents = createDocumentStore();
100105
const documentCache = createDocumentCache(cacheDir);
101106
const providerLimiter = createLimiter(4, { label: 'provider' });
@@ -188,6 +193,7 @@ export async function startServer({
188193
registerAccessMiddleware(app, { token, origin: () => origin, development });
189194
registerPerformanceMiddleware(app, performanceTracker);
190195

196+
registerFormulaOcrRoutes(app, { service: formulaOcr });
191197
registerProviderPortRoute(app, isProviderPortOpen);
192198
registerCacheRoutes(app, { cacheManager });
193199
registerDeveloperRoutes(app, {
@@ -284,6 +290,7 @@ export async function startServer({
284290
layoutEntries.clear();
285291
documents.clear();
286292
server.closeAllConnections();
293+
await formulaOcr.dispose();
287294
await engines.close();
288295
await localTranslator.close?.();
289296
await new Promise((r) => server.close(r));

0 commit comments

Comments
 (0)