From 25bdb61c56d79d9cf90c34aef4cf785da9812642 Mon Sep 17 00:00:00 2001 From: suyao Date: Tue, 25 Mar 2025 17:24:35 +0800 Subject: [PATCH] feat(OCR): add MacSysOcrProvider for macOS OCR functionality - Introduced MacSysOcrProvider to handle OCR tasks using the mac-system-ocr library. - Enhanced the OcrProviderFactory to support the new macOS OCR provider. - Updated file handling to include base64 file processing. - Added a new SVG icon for the macOS OCR provider. - Updated the state management to include the new system OCR provider option. - Improved user interface to conditionally display the system OCR provider based on the operating system. --- package.json | 2 + src/main/ipc.ts | 1 + src/main/ocr/MacSysOcrProvider.ts | 91 +++++++++ src/main/ocr/MistralOcrProvider.ts | 2 +- src/main/ocr/OcrProviderFactory.ts | 15 +- src/main/services/KnowledgeService.ts | 4 +- .../src/assets/images/providers/macos.svg | 7 + src/renderer/src/config/ocrProviders.ts | 3 + src/renderer/src/pages/files/GeminiFiles.tsx | 5 +- .../ToolSettings/OcrSettings/index.tsx | 7 +- src/renderer/src/providers/GeminiProvider.ts | 3 +- src/renderer/src/store/migrate.ts | 6 + src/renderer/src/store/ocr.ts | 4 + src/renderer/src/utils/file.ts | 4 +- yarn.lock | 188 +++++++++++++++++- 15 files changed, 324 insertions(+), 18 deletions(-) create mode 100644 src/main/ocr/MacSysOcrProvider.ts create mode 100644 src/renderer/src/assets/images/providers/macos.svg diff --git a/package.json b/package.json index b844b8cc6..621aa6b5f 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "prepare": "husky" }, "dependencies": { + "@cherrystudio/mac-system-ocr": "^0.1.0", "@electron-toolkit/utils": "^3.0.0", "@electron/notarize": "^2.5.0", "@google/generative-ai": "^0.21.0", @@ -82,6 +83,7 @@ "markdown-it": "^14.1.0", "officeparser": "^4.1.1", "pdf-parse": "^1.1.1", + "pdf-to-img": "^4.4.0", "proxy-agent": "^6.5.0", "tar": "^7.4.3", "undici": "^7.4.0", diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 3c214e1aa..427492513 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -181,6 +181,7 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { ipcMain.handle('file:write', fileManager.writeFile) ipcMain.handle('file:saveImage', fileManager.saveImage) ipcMain.handle('file:base64Image', fileManager.base64Image) + ipcMain.handle('file:base64File', fileManager.base64File) ipcMain.handle('file:download', fileManager.downloadFile) ipcMain.handle('file:copy', fileManager.copyFile) ipcMain.handle('file:binaryFile', fileManager.binaryFile) diff --git a/src/main/ocr/MacSysOcrProvider.ts b/src/main/ocr/MacSysOcrProvider.ts new file mode 100644 index 000000000..be8eb7b7b --- /dev/null +++ b/src/main/ocr/MacSysOcrProvider.ts @@ -0,0 +1,91 @@ +import MacOCR, { OCRResult } from '@cherrystudio/mac-system-ocr' +import { FileSource, isLocalFile, LocalFileSource, OcrProvider } from '@types' +import Logger from 'electron-log' +import * as fs from 'fs' +import PQueue from 'p-queue' +import * as path from 'path' + +import BaseOcrProvider from './BaseOcrProvider' + +export default class MacSysOcrProvider extends BaseOcrProvider { + constructor(provider: OcrProvider) { + super(provider) + } + + private async processPage( + pageBuffer: Buffer, + pageNum: number, + totalPages: number, + sourceId: string, + writeStream: fs.WriteStream + ): Promise { + try { + const text: OCRResult = await MacOCR.recognizeFromBuffer(pageBuffer) + writeStream.write(text.text + '\n') + await this.sendOcrProgress(sourceId, (pageNum / totalPages) * 100) + } catch (error) { + Logger.error(`[OCR] Error processing page ${pageNum}:`, error) + throw error + } + } + + public async parseFile(sourceId: string, file: FileSource): Promise<{ processedFile: LocalFileSource }> { + Logger.info(`[OCR] Starting OCR process for file: ${file.name}`) + if (isLocalFile(file)) { + if (file.ext === '.pdf') { + try { + const { pdf } = await import('pdf-to-img') + const readStream = fs.createReadStream(file.path) + const results = await pdf(readStream) + const pages = results.length + Logger.info('[OCR] PDF successfully converted to image') + + const baseDir = path.dirname(file.path) + const baseName = path.basename(file.path, path.extname(file.path)) + const txtFileName = `${baseName}.txt` + const txtFilePath = path.join(baseDir, txtFileName) + + // Create a write stream + const writeStream = fs.createWriteStream(txtFilePath) + + const queue = new PQueue({ concurrency: 5 }) + const tasks: Promise[] = [] + + for (let i = 0; i < pages; i++) { + const pageNum = i + 1 + const task = queue.add(async () => { + const pageBuffer = await results.getPage(pageNum) + await this.processPage(pageBuffer, pageNum, pages, sourceId, writeStream) + }) + tasks.push(task) + } + + await Promise.all(tasks) + + await new Promise((resolve, reject) => { + writeStream.end(() => { + Logger.info('[OCR] OCR process completed successfully') + resolve() + }) + writeStream.on('error', reject) + }) + + return { + processedFile: { + ...file, + name: txtFileName, + path: txtFilePath, + ext: '.txt', + size: fs.statSync(txtFilePath).size + } + } + } catch (error) { + Logger.error('[OCR] Error during OCR process:', error) + throw error + } + } + return { processedFile: file } + } + throw new Error('Only local files are supported') + } +} diff --git a/src/main/ocr/MistralOcrProvider.ts b/src/main/ocr/MistralOcrProvider.ts index dd242ba16..307c9ef79 100644 --- a/src/main/ocr/MistralOcrProvider.ts +++ b/src/main/ocr/MistralOcrProvider.ts @@ -189,7 +189,7 @@ export default class MistralOcrProvider extends BaseOcrProvider { return { id: conversionId, - name: mdFileName, + name: file.name.replace(/\.[^/.]+$/, '.md'), origin_name: mdFileName, path: mdFilePath, created_at: new Date().toISOString(), diff --git a/src/main/ocr/OcrProviderFactory.ts b/src/main/ocr/OcrProviderFactory.ts index cf8df1675..a1244bef5 100644 --- a/src/main/ocr/OcrProviderFactory.ts +++ b/src/main/ocr/OcrProviderFactory.ts @@ -3,14 +3,19 @@ import { OcrProvider } from '@types' import BaseOcrProvider from './BaseOcrProvider' import DefaultOcrProvider from './DefaultOcrProvider' import Doc2xOcrProvider from './Doc2xOcrProvider' +import MacSysOcrProvider from './MacSysOcrProvider' import MistralOcrProvider from './MistralOcrProvider' export default class OcrProviderFactory { static create(provider: OcrProvider): BaseOcrProvider { - if (provider.id === 'doc2x') { - return new Doc2xOcrProvider(provider) - } else if (provider.id === 'mistral') { - return new MistralOcrProvider(provider) + switch (provider.id) { + case 'doc2x': + return new Doc2xOcrProvider(provider) + case 'mistral': + return new MistralOcrProvider(provider) + case 'system': + return new MacSysOcrProvider(provider) + default: + return new DefaultOcrProvider(provider) } - return new DefaultOcrProvider(provider) } } diff --git a/src/main/services/KnowledgeService.ts b/src/main/services/KnowledgeService.ts index ed4f4335e..2d954bcf0 100644 --- a/src/main/services/KnowledgeService.ts +++ b/src/main/services/KnowledgeService.ts @@ -159,6 +159,7 @@ class KnowledgeService { ): LoaderTask { const { base, item, forceReload } = options const file = item.content as FileType + file.source = 'local' const loaderTask: LoaderTask = { loaderTasks: [ @@ -169,12 +170,11 @@ class KnowledgeService { let fileToProcess: FileType = file if (base.preprocessing && base.ocrProvider && file.ext.toLowerCase() === '.pdf') { try { - file.source = 'local' const ocrProvider = new OcrProvider(base.ocrProvider) Logger.info(`Starting OCR processing for file: ${file.path}`) const { processedFile } = await ocrProvider.parseFile(item.id, file) - + Logger.info(`OCR processing completed: ${processedFile.path}`) fileToProcess = processedFile Logger.info(`OCR processing completed: ${fileToProcess.path}`) } catch (err) { diff --git a/src/renderer/src/assets/images/providers/macos.svg b/src/renderer/src/assets/images/providers/macos.svg new file mode 100644 index 000000000..3385e7350 --- /dev/null +++ b/src/renderer/src/assets/images/providers/macos.svg @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/src/renderer/src/config/ocrProviders.ts b/src/renderer/src/config/ocrProviders.ts index e03b48f5c..e39ef5604 100644 --- a/src/renderer/src/config/ocrProviders.ts +++ b/src/renderer/src/config/ocrProviders.ts @@ -1,4 +1,5 @@ import Doc2xLogo from '@renderer/assets/images/ocr/doc2x.svg' +import MacOSLogo from '@renderer/assets/images/providers/macos.svg' import MistralLogo from '@renderer/assets/images/providers/mistral.png' export function getOcrProviderLogo(providerId: string) { @@ -7,6 +8,8 @@ export function getOcrProviderLogo(providerId: string) { return Doc2xLogo case 'mistral': return MistralLogo + case 'system': + return MacOSLogo default: return undefined } diff --git a/src/renderer/src/pages/files/GeminiFiles.tsx b/src/renderer/src/pages/files/GeminiFiles.tsx index 07dea8fbd..6ba966307 100644 --- a/src/renderer/src/pages/files/GeminiFiles.tsx +++ b/src/renderer/src/pages/files/GeminiFiles.tsx @@ -19,8 +19,9 @@ const GeminiFiles: FC = ({ id }) => { const [loading, setLoading] = useState(false) const fetchFiles = useCallback(async () => { - const { files } = await window.api.fileService.list(provider.type, provider.apiKey) - files && setFiles(files.filter((file) => file.state === 'ACTIVE')) + const reponses = await window.api.fileService.list(provider.type, provider.apiKey) + const files = reponses.files.map((file) => file.originalFile as FileMetadataResponse) + setFiles(files) }, [provider]) const columns: ColumnsType = [ diff --git a/src/renderer/src/pages/settings/ToolSettings/OcrSettings/index.tsx b/src/renderer/src/pages/settings/ToolSettings/OcrSettings/index.tsx index e562ac6b1..5c1795015 100644 --- a/src/renderer/src/pages/settings/ToolSettings/OcrSettings/index.tsx +++ b/src/renderer/src/pages/settings/ToolSettings/OcrSettings/index.tsx @@ -1,3 +1,4 @@ +import { isMac } from '@renderer/config/constant' import { useTheme } from '@renderer/context/ThemeProvider' import { useDefaultOcrProvider, useOcrProviders } from '@renderer/hooks/useOcr' import { OcrProvider } from '@renderer/types' @@ -37,7 +38,11 @@ const KnowledgeSettings: FC = () => { style={{ width: '200px' }} onChange={(value: string) => updateSelectedOcrProvider(value)} placeholder={t('settings.tool.ocr.provider_placeholder')} - options={ocrProviders.map((p) => ({ value: p.id, label: p.name }))} + options={ocrProviders.map((p) => ({ + value: p.id, + label: p.name, + disabled: !isMac && p.id === 'system' // 在非 Mac 系统下禁用 system 选项 + }))} /> diff --git a/src/renderer/src/providers/GeminiProvider.ts b/src/renderer/src/providers/GeminiProvider.ts index 9891f9d0a..3943e768f 100644 --- a/src/renderer/src/providers/GeminiProvider.ts +++ b/src/renderer/src/providers/GeminiProvider.ts @@ -88,7 +88,7 @@ export default class GeminiProvider extends BaseProvider { const { data, mimeType } = await fileToBase64(file.path) return { inlineData: { - data, + data: data.toString('base64'), mimeType } } as InlineDataPart @@ -150,6 +150,7 @@ export default class GeminiProvider extends BaseProvider { } for (const file of message.files || []) { + console.log('file', file) if (file.type === FileTypes.IMAGE) { const base64Data = await window.api.file.base64Image(file.id + file.ext) parts.push({ diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index 0235ca844..5dacedb45 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -818,6 +818,12 @@ const migrateConfig = { } ] } + if (!state.ocr.providers.find((provider) => provider.id === 'system')) { + state.ocr.providers.push({ + id: 'system', + name: 'System(Mac Only)' + }) + } return state } } diff --git a/src/renderer/src/store/ocr.ts b/src/renderer/src/store/ocr.ts index 27c2346c2..1677f44b0 100644 --- a/src/renderer/src/store/ocr.ts +++ b/src/renderer/src/store/ocr.ts @@ -20,6 +20,10 @@ const initialState: OcrState = { model: 'mistral-ocr-latest', apiKey: '', apiHost: 'https://api.mistral.ai' + }, + { + id: 'system', + name: 'System(Mac Only)' } ], defaultProvider: '' diff --git a/src/renderer/src/utils/file.ts b/src/renderer/src/utils/file.ts index 02f20dae3..8ce24be9a 100644 --- a/src/renderer/src/utils/file.ts +++ b/src/renderer/src/utils/file.ts @@ -1,7 +1,7 @@ export const fileToBase64 = async (filePath: string) => { - const result = await window.api.file.base64Image(filePath) + const result = await window.api.file.base64File(filePath) return { - data: result.base64, + data: result.data, mimeType: result.mime } } diff --git a/yarn.lock b/yarn.lock index 61e365d7b..0dcf33010 100644 --- a/yarn.lock +++ b/yarn.lock @@ -451,6 +451,17 @@ __metadata: languageName: unknown linkType: soft +"@cherrystudio/mac-system-ocr@npm:^0.1.0": + version: 0.1.0 + resolution: "@cherrystudio/mac-system-ocr@npm:0.1.0" + dependencies: + bindings: "npm:^1.5.0" + node-api-headers: "npm:^1.0.1" + node-gyp: "npm:latest" + checksum: 10c0/a8257e4c6015f93c630091b44643e3c89e1bd6c2ece836a2ecfa517938c60932f0b8040d126b458dd9e5708194ce9c39c9b288ea5d81d55a111a7f11c1c21c7e + languageName: node + linkType: hard + "@develar/schema-utils@npm:~2.6.5": version: 2.6.5 resolution: "@develar/schema-utils@npm:2.6.5" @@ -2389,6 +2400,25 @@ __metadata: languageName: node linkType: hard +"@mapbox/node-pre-gyp@npm:^1.0.0": + version: 1.0.11 + resolution: "@mapbox/node-pre-gyp@npm:1.0.11" + dependencies: + detect-libc: "npm:^2.0.0" + https-proxy-agent: "npm:^5.0.0" + make-dir: "npm:^3.1.0" + node-fetch: "npm:^2.6.7" + nopt: "npm:^5.0.0" + npmlog: "npm:^5.0.1" + rimraf: "npm:^3.0.2" + semver: "npm:^7.3.5" + tar: "npm:^6.1.11" + bin: + node-pre-gyp: bin/node-pre-gyp + checksum: 10c0/2b24b93c31beca1c91336fa3b3769fda98e202fb7f9771f0f4062588d36dcc30fcf8118c36aa747fa7f7610d8cf601872bdaaf62ce7822bb08b545d1bbe086cc + languageName: node + linkType: hard + "@mistralai/mistralai@npm:^1.5.2": version: 1.5.2 resolution: "@mistralai/mistralai@npm:1.5.2" @@ -3816,6 +3846,7 @@ __metadata: "@agentic/searxng": "npm:^7.3.3" "@agentic/tavily": "npm:^7.3.3" "@anthropic-ai/sdk": "npm:^0.38.0" + "@cherrystudio/mac-system-ocr": "npm:^0.1.0" "@electron-toolkit/eslint-config-prettier": "npm:^3.0.0" "@electron-toolkit/eslint-config-ts": "npm:^3.0.0" "@electron-toolkit/preload": "npm:^3.0.0" @@ -3902,6 +3933,7 @@ __metadata: openai: "patch:openai@npm%3A4.77.3#~/.yarn/patches/openai-npm-4.77.3-59c6d42e7a.patch" p-queue: "npm:^8.1.0" pdf-parse: "npm:^1.1.1" + pdf-to-img: "npm:^4.4.0" prettier: "npm:^3.5.3" proxy-agent: "npm:^6.5.0" react: "npm:^18.2.0" @@ -4283,6 +4315,16 @@ __metadata: languageName: node linkType: hard +"are-we-there-yet@npm:^2.0.0": + version: 2.0.0 + resolution: "are-we-there-yet@npm:2.0.0" + dependencies: + delegates: "npm:^1.0.0" + readable-stream: "npm:^3.6.0" + checksum: 10c0/375f753c10329153c8d66dc95e8f8b6c7cc2aa66e05cb0960bd69092b10dae22900cacc7d653ad11d26b3ecbdbfe1e8bfb6ccf0265ba8077a7d979970f16b99c + languageName: node + linkType: hard + "are-we-there-yet@npm:^3.0.0": version: 3.0.1 resolution: "are-we-there-yet@npm:3.0.1" @@ -4948,6 +4990,29 @@ __metadata: languageName: node linkType: hard +"canvas@npm:3.1.0": + version: 3.1.0 + resolution: "canvas@npm:3.1.0" + dependencies: + node-addon-api: "npm:^7.0.0" + node-gyp: "npm:latest" + prebuild-install: "npm:^7.1.1" + checksum: 10c0/28da5184c1d7e97049ba6a24f10690b9ed4b303bbd25517d95c892fa3a6331417791657a3a7467068e40af0dda2dcc9120d062f7426a3d796131e69a30e3cbf1 + languageName: node + linkType: hard + +"canvas@npm:^2.11.2": + version: 2.11.2 + resolution: "canvas@npm:2.11.2" + dependencies: + "@mapbox/node-pre-gyp": "npm:^1.0.0" + nan: "npm:^2.17.0" + node-gyp: "npm:latest" + simple-get: "npm:^3.0.3" + checksum: 10c0/943368798ad1b66b18633aa34b6181e1038dac5433fc9727cd07be35f0a633f572b60d9edb95f5ff90b6a9128e86d5312035f91a2934101c73185b15d906230a + languageName: node + linkType: hard + "caseless@npm:~0.12.0": version: 0.12.0 resolution: "caseless@npm:0.12.0" @@ -5226,7 +5291,7 @@ __metadata: languageName: node linkType: hard -"color-support@npm:^1.1.3": +"color-support@npm:^1.1.2, color-support@npm:^1.1.3": version: 1.1.3 resolution: "color-support@npm:1.1.3" bin: @@ -5710,6 +5775,15 @@ __metadata: languageName: node linkType: hard +"decompress-response@npm:^4.2.0": + version: 4.2.1 + resolution: "decompress-response@npm:4.2.1" + dependencies: + mimic-response: "npm:^2.0.0" + checksum: 10c0/5e4821be332e80e3639acee2441c41d245fc07ac3ee85a6f28893c10c079d66d9bf09e8d84bffeae5656a4625e09e9b93fb4a5705adbe6b07202eea64fae1c8d + languageName: node + linkType: hard + "decompress-response@npm:^6.0.0": version: 6.0.0 resolution: "decompress-response@npm:6.0.0" @@ -7764,6 +7838,23 @@ __metadata: languageName: node linkType: hard +"gauge@npm:^3.0.0": + version: 3.0.2 + resolution: "gauge@npm:3.0.2" + dependencies: + aproba: "npm:^1.0.3 || ^2.0.0" + color-support: "npm:^1.1.2" + console-control-strings: "npm:^1.0.0" + has-unicode: "npm:^2.0.1" + object-assign: "npm:^4.1.1" + signal-exit: "npm:^3.0.0" + string-width: "npm:^4.2.3" + strip-ansi: "npm:^6.0.1" + wide-align: "npm:^1.1.2" + checksum: 10c0/75230ccaf216471e31025c7d5fcea1629596ca20792de50c596eb18ffb14d8404f927cd55535aab2eeecd18d1e11bd6f23ec3c2e9878d2dda1dc74bccc34b913 + languageName: node + linkType: hard + "gauge@npm:^4.0.3": version: 4.0.4 resolution: "gauge@npm:4.0.4" @@ -9924,6 +10015,15 @@ __metadata: languageName: node linkType: hard +"make-dir@npm:^3.1.0": + version: 3.1.0 + resolution: "make-dir@npm:3.1.0" + dependencies: + semver: "npm:^6.0.0" + checksum: 10c0/56aaafefc49c2dfef02c5c95f9b196c4eb6988040cf2c712185c7fe5c99b4091591a7fc4d4eafaaefa70ff763a26f6ab8c3ff60b9e75ea19876f49b18667ecaa + languageName: node + linkType: hard + "make-fetch-happen@npm:^14.0.3": version: 14.0.3 resolution: "make-fetch-happen@npm:14.0.3" @@ -11024,6 +11124,13 @@ __metadata: languageName: node linkType: hard +"mimic-response@npm:^2.0.0": + version: 2.1.0 + resolution: "mimic-response@npm:2.1.0" + checksum: 10c0/717475c840f20deca87a16cb2f7561f9115f5de225ea2377739e09890c81aec72f43c81fd4984650c4044e66be5a846fa7a517ac7908f01009e1e624e19864d5 + languageName: node + linkType: hard + "mimic-response@npm:^3.1.0": version: 3.1.0 resolution: "mimic-response@npm:3.1.0" @@ -11305,6 +11412,15 @@ __metadata: languageName: node linkType: hard +"nan@npm:^2.17.0": + version: 2.22.2 + resolution: "nan@npm:2.22.2" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/971f963b8120631880fa47a389c71b00cadc1c1b00ef8f147782a3f4387d4fc8195d0695911272d57438c11562fb27b24c4ae5f8c05d5e4eeb4478ba51bb73c5 + languageName: node + linkType: hard + "nan@npm:~2.10.0": version: 2.10.0 resolution: "nan@npm:2.10.0" @@ -11414,6 +11530,13 @@ __metadata: languageName: node linkType: hard +"node-api-headers@npm:^1.0.1": + version: 1.5.0 + resolution: "node-api-headers@npm:1.5.0" + checksum: 10c0/e8dfe99e8e3ca92cd5d37989413dfc96551e8f7883110b948917dad07e554cfbf1119130e96d0167f5cb5a05f651a4ac735402a305ff25d9ace422a2e429ae3b + languageName: node + linkType: hard + "node-domexception@npm:1.0.0, node-domexception@npm:^1.0.0": version: 1.0.0 resolution: "node-domexception@npm:1.0.0" @@ -11628,6 +11751,18 @@ __metadata: languageName: node linkType: hard +"npmlog@npm:^5.0.1": + version: 5.0.1 + resolution: "npmlog@npm:5.0.1" + dependencies: + are-we-there-yet: "npm:^2.0.0" + console-control-strings: "npm:^1.1.0" + gauge: "npm:^3.0.0" + set-blocking: "npm:^2.0.0" + checksum: 10c0/489ba519031013001135c463406f55491a17fc7da295c18a04937fe3a4d523fd65e88dd418a28b967ab743d913fdeba1e29838ce0ad8c75557057c481f7d49fa + languageName: node + linkType: hard + "npmlog@npm:^6.0.0": version: 6.0.2 resolution: "npmlog@npm:6.0.2" @@ -11661,7 +11796,7 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4, object-assign@npm:^4.0.1, object-assign@npm:^4.1.0": +"object-assign@npm:^4, object-assign@npm:^4.0.1, object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 @@ -12297,6 +12432,13 @@ __metadata: languageName: node linkType: hard +"path2d@npm:^0.2.0": + version: 0.2.2 + resolution: "path2d@npm:0.2.2" + checksum: 10c0/1bb76c7f275d07f1bc7ca12171d828e91bf8a12596f0765a52e9d4d47fe1a428455dc1dd4c9002924a9bc554f6ac25e09a6c22eaecf32e5e33fba2985b5168f8 + languageName: node + linkType: hard + "pdf-parse@npm:1.1.1, pdf-parse@npm:^1.1.1": version: 1.1.1 resolution: "pdf-parse@npm:1.1.1" @@ -12317,6 +12459,33 @@ __metadata: languageName: node linkType: hard +"pdf-to-img@npm:^4.4.0": + version: 4.4.0 + resolution: "pdf-to-img@npm:4.4.0" + dependencies: + canvas: "npm:3.1.0" + pdfjs-dist: "npm:4.2.67" + bin: + pdf2img: bin/cli.mjs + checksum: 10c0/1b6eb36042d3b54f067241a74dc80d27e976685a15e730736f61bdbbc8b76bdd20e58976e87f1b0b9d9cfe534179ebe748b0eb3ec71d0807b83384447f861ee2 + languageName: node + linkType: hard + +"pdfjs-dist@npm:4.2.67": + version: 4.2.67 + resolution: "pdfjs-dist@npm:4.2.67" + dependencies: + canvas: "npm:^2.11.2" + path2d: "npm:^0.2.0" + dependenciesMeta: + canvas: + optional: true + path2d: + optional: true + checksum: 10c0/1d6d427a2253b2c15cbb168d7f95fc26428134ff61113359653c36f92475a4abd8552913b1492489933304ccc4285328b5cbcff36825ae533b8cd4c279881348 + languageName: node + linkType: hard + "peberminta@npm:^0.9.0": version: 0.9.0 resolution: "peberminta@npm:0.9.0" @@ -14278,7 +14447,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^6.2.0, semver@npm:^6.3.1": +"semver@npm:^6.0.0, semver@npm:^6.2.0, semver@npm:^6.3.1": version: 6.3.1 resolution: "semver@npm:6.3.1" bin: @@ -14466,6 +14635,17 @@ __metadata: languageName: node linkType: hard +"simple-get@npm:^3.0.3": + version: 3.1.1 + resolution: "simple-get@npm:3.1.1" + dependencies: + decompress-response: "npm:^4.2.0" + once: "npm:^1.3.1" + simple-concat: "npm:^1.0.0" + checksum: 10c0/438c78844ea1b1e7268d13ee0b3a39c7d644183367aec916aed3b676b45d3037a61d9f975c200a49b42eb851f29f03745118af1e13c01e60a7b4044f2fd60be7 + languageName: node + linkType: hard + "simple-get@npm:^4.0.0": version: 4.0.1 resolution: "simple-get@npm:4.0.1" @@ -16124,7 +16304,7 @@ __metadata: languageName: node linkType: hard -"wide-align@npm:^1.1.0, wide-align@npm:^1.1.5": +"wide-align@npm:^1.1.0, wide-align@npm:^1.1.2, wide-align@npm:^1.1.5": version: 1.1.5 resolution: "wide-align@npm:1.1.5" dependencies: