Download bindings/nodejs/index.js from martinsmessias/rasgapdf: direct link, hf CLI and curl.
- Browser
- Download file 6.9 kB
-
https://huggingface.co/martinsmessias/rasgapdf/resolve/main/bindings/nodejs/index.js
- Command line
-
hf download hf://martinsmessias/rasgapdf/bindings/nodejs/index.js
-
curl -L -o index.js https://huggingface.co/martinsmessias/rasgapdf/resolve/main/bindings/nodejs/index.js
6.9 kB
| /** | |
| * RasgaPDF - Node.js Binding | |
| * Ultra-fast 2D Spatial PDF Parser powered by Rust | |
| */ | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const { execFileSync } = require('child_process'); | |
| let ffiLib = null; | |
| function findBinary() { | |
| const binaryName = process.platform === 'win32' ? 'rasgapdf.exe' : 'rasgapdf'; | |
| const candidates = [ | |
| path.join(__dirname, '..', '..', 'target', 'release', binaryName), | |
| path.join(__dirname, '..', '..', '..', 'target', 'release', binaryName), | |
| path.join(process.env.HOME || '', 'rasgapdf', 'target', 'release', binaryName), | |
| `/usr/local/bin/${binaryName}`, | |
| `/usr/bin/${binaryName}` | |
| ]; | |
| for (const c of candidates) { | |
| if (fs.existsSync(c)) return c; | |
| } | |
| return binaryName; | |
| } | |
| function findSharedLib() { | |
| const libName = process.platform === 'win32' ? 'rasgapdf.dll' : (process.platform === 'darwin' ? 'librasgapdf.dylib' : 'librasgapdf.so'); | |
| const candidates = [ | |
| path.join(__dirname, libName), | |
| path.join(__dirname, '..', '..', 'target', 'release', libName), | |
| path.join(__dirname, '..', '..', '..', 'target', 'release', libName), | |
| path.join(process.env.HOME || '', 'rasgapdf', 'target', 'release', libName), | |
| `/usr/local/lib/${libName}`, | |
| `/usr/lib/${libName}` | |
| ]; | |
| for (const c of candidates) { | |
| if (fs.existsSync(c)) return c; | |
| } | |
| return null; | |
| } | |
| // Inicializa FFI se koffi estiver disponível | |
| const sharedLibPath = findSharedLib(); | |
| if (sharedLibPath) { | |
| try { | |
| const koffi = require('koffi'); | |
| const lib = koffi.load(sharedLibPath); | |
| ffiLib = { | |
| parseRaw: lib.func('char* rasgapdf_parse_raw(str)'), | |
| parseFile: lib.func('char* rasgapdf_parse_file_options(str, str)'), | |
| extractImages: lib.func('char* rasgapdf_extract_images(str, str)'), | |
| freeString: lib.func('void rasgapdf_free_string(char*)'), | |
| version: lib.func('str rasgapdf_version()') | |
| }; | |
| } catch (e) { | |
| // Fallback para CLI execution | |
| } | |
| } | |
| /** | |
| * ⚡ Modo 1 (Bruto): Retorna exclusivamente a lista plana de coordenadas X/Y | |
| * sem overhead de agrupamento em blocos ou tabelas. | |
| * | |
| * @param {string} filePath Caminho do arquivo PDF | |
| * @returns {object} RawSpatialDocument { file_name, total_pages, total_spans, parse_time_ms, spans: [...] } | |
| */ | |
| function parseRaw(filePath) { | |
| const absPath = path.resolve(filePath); | |
| if (!fs.existsSync(absPath)) { | |
| throw new Error(`PDF file not found: ${absPath}`); | |
| } | |
| // Se FFI estiver ativo | |
| if (ffiLib && ffiLib.parseRaw) { | |
| const ptr = ffiLib.parseRaw(absPath); | |
| if (!ptr) throw new Error('Null pointer returned from native rasgapdf library'); | |
| try { | |
| const parsed = JSON.parse(ptr); | |
| if (parsed.error) throw new Error(parsed.message || 'Error parsing PDF'); | |
| return parsed; | |
| } finally { | |
| ffiLib.freeString(ptr); | |
| } | |
| } | |
| // Fallback via binary CLI | |
| const binPath = findBinary(); | |
| const args = [absPath, '--raw']; | |
| const stdout = execFileSync(binPath, args, { encoding: 'utf-8', maxBuffer: 100 * 1024 * 1024 }); | |
| return JSON.parse(stdout); | |
| } | |
| /** | |
| * 🎯 Modo 2 (Estruturado): Processa o PDF e retorna a árvore geométrica hierárquica | |
| * com blocos de texto, linhas, spans e tabelas detectadas. | |
| * | |
| * Passe `extract_images: true` para preencher `page.images` e | |
| * `extract_drawings: true` para preencher `page.drawings` (ambos desligados por padrão). | |
| * | |
| * @param {string} filePath Caminho do arquivo PDF | |
| * @param {object} options Opções de detecção espacial | |
| * @returns {object} SpatialDocument | |
| */ | |
| function parsePdf(filePath, options = {}) { | |
| const absPath = path.resolve(filePath); | |
| if (!fs.existsSync(absPath)) { | |
| throw new Error(`PDF file not found: ${absPath}`); | |
| } | |
| const defaultOptions = { | |
| mode: 'structured', | |
| detect_tables: true, | |
| min_table_columns: 3, | |
| column_tolerance: 16.0, | |
| row_tolerance: 6.0, | |
| extract_images: false, | |
| extract_drawings: false, | |
| filter_background_fills: true, | |
| ...options | |
| }; | |
| // Se FFI estiver ativo | |
| if (ffiLib) { | |
| const optsJson = JSON.stringify(defaultOptions); | |
| const ptr = ffiLib.parseFile(absPath, optsJson); | |
| if (!ptr) throw new Error('Null pointer returned from native rasgapdf library'); | |
| try { | |
| const parsed = JSON.parse(ptr); | |
| if (parsed.error) throw new Error(parsed.message || 'Error parsing PDF'); | |
| return parsed; | |
| } finally { | |
| ffiLib.freeString(ptr); | |
| } | |
| } | |
| // Fallback rápido via binary CLI | |
| const binPath = findBinary(); | |
| const args = [absPath]; | |
| if (!defaultOptions.detect_tables) args.push('--no-tables'); | |
| args.push('--min-table-cols', String(defaultOptions.min_table_columns)); | |
| args.push('--col-tolerance', String(defaultOptions.column_tolerance)); | |
| args.push('--row-tolerance', String(defaultOptions.row_tolerance)); | |
| if (defaultOptions.extract_images) args.push('--images'); | |
| if (defaultOptions.extract_drawings) args.push('--drawings'); | |
| if (defaultOptions.filter_background_fills === false) args.push('--keep-backgrounds'); | |
| const stdout = execFileSync(binPath, args, { encoding: 'utf-8', maxBuffer: 100 * 1024 * 1024 }); | |
| return JSON.parse(stdout); | |
| } | |
| /** | |
| * 🖼️ Extrai os streams originais das imagens embutidas para `outputDir`. | |
| * | |
| * Nenhum pixel é decodificado: o stream codificado é copiado como está | |
| * (ex.: `DCTDecode` → `.jpg`). Um `manifest.json` é gravado junto. | |
| * | |
| * @param {string} filePath Caminho do arquivo PDF | |
| * @param {string} outputDir Pasta destino (criada se não existir) | |
| * @returns {object} { file_name, total_images, written, images: [{ page, image }] } | |
| */ | |
| function extractImages(filePath, outputDir) { | |
| const absPath = path.resolve(filePath); | |
| if (!fs.existsSync(absPath)) { | |
| throw new Error(`PDF file not found: ${absPath}`); | |
| } | |
| if (!outputDir) { | |
| throw new Error('outputDir is required'); | |
| } | |
| const absOut = path.resolve(outputDir); | |
| if (ffiLib && ffiLib.extractImages) { | |
| const ptr = ffiLib.extractImages(absPath, absOut); | |
| if (!ptr) throw new Error('Null pointer returned from native rasgapdf library'); | |
| try { | |
| const parsed = JSON.parse(ptr); | |
| if (parsed.error) throw new Error(parsed.message || 'Error extracting images'); | |
| return parsed; | |
| } finally { | |
| ffiLib.freeString(ptr); | |
| } | |
| } | |
| // Fallback via binary CLI: o manifesto é gravado em disco. | |
| const binPath = findBinary(); | |
| execFileSync(binPath, [absPath, '--extract-images', absOut], { | |
| encoding: 'utf-8', | |
| maxBuffer: 100 * 1024 * 1024 | |
| }); | |
| return JSON.parse(fs.readFileSync(path.join(absOut, 'manifest.json'), 'utf-8')); | |
| } | |
| function version() { | |
| if (ffiLib) return ffiLib.version(); | |
| return '0.2.0'; | |
| } | |
| module.exports = { | |
| parseRaw, | |
| parsePdf, | |
| extractImages, | |
| version | |
| }; | |