UNPKG

@fin.cx/einvoice

Version:

A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.

151 lines 10.7 kB
import { BaseXMLExtractor } from './base.extractor.js'; /** * Text-based XML extractor for PDF documents * Extracts XML by searching for XML patterns in the PDF text * Used as a fallback when other extraction methods fail */ export class TextXMLExtractor extends BaseXMLExtractor { constructor() { super(...arguments); // Maximum chunk size to process at once (4MB) this.CHUNK_SIZE = 4 * 1024 * 1024; // Maximum number of chunks to check (effective 20MB search limit) this.MAX_CHUNKS = 5; // Common XML patterns to look for this.XML_PATTERNS = [ '<?xml', '<CrossIndustryInvoice', '<CrossIndustryDocument', '<Invoice', '<CreditNote', '<rsm:CrossIndustryInvoice', '<rsm:CrossIndustryDocument', '<ram:CrossIndustryDocument', '<ubl:Invoice', '<ubl:CreditNote', '<FatturaElettronica' ]; } /** * Extract XML from a PDF buffer by searching for XML patterns in the text * Uses a chunked approach to handle large files efficiently * @param pdfBuffer PDF buffer * @returns XML content or null if not found */ async extractXml(pdfBuffer) { try { console.log('Attempting text-based XML extraction from PDF...'); // Convert Buffer to Uint8Array if needed const buffer = Buffer.isBuffer(pdfBuffer) ? new Uint8Array(pdfBuffer) : pdfBuffer; // Try extracting XML using the chunked approach return this.extractXmlFromBufferChunked(buffer); } catch (error) { console.error('Error in text-based extraction:', error); return null; } } /** * Extract XML from buffer using a chunked approach * This helps avoid memory issues with large PDFs * @param buffer Buffer to search in * @returns XML content or null if not found */ extractXmlFromBufferChunked(buffer) { // Process the PDF in chunks for (let chunkIndex = 0; chunkIndex < this.MAX_CHUNKS; chunkIndex++) { const startPos = chunkIndex * this.CHUNK_SIZE; if (startPos >= buffer.length) break; const endPos = Math.min(startPos + this.CHUNK_SIZE, buffer.length); const chunk = buffer.slice(startPos, endPos); // Try to extract XML from this chunk const chunkResult = this.processChunk(chunk, startPos); if (chunkResult) { return chunkResult; } } console.warn('No valid XML found in any chunk of the PDF'); return null; } /** * Process a single chunk of the PDF buffer * @param chunk Chunk buffer to process * @param chunkOffset Offset position of the chunk in the original buffer * @returns XML content or null if not found */ processChunk(chunk, chunkOffset) { try { // First try UTF-8 encoding for this chunk const utf8String = this.decodeBufferToString(chunk, 'utf-8'); let xmlContent = this.searchForXmlInString(utf8String); if (xmlContent) { console.log(`Found XML content in chunk at offset ${chunkOffset} using UTF-8 encoding`); return xmlContent; } // If UTF-8 fails, try Latin-1 (ISO-8859-1) which can handle binary better const latin1String = this.decodeBufferToString(chunk, 'latin1'); xmlContent = this.searchForXmlInString(latin1String); if (xmlContent) { console.log(`Found XML content in chunk at offset ${chunkOffset} using Latin-1 encoding`); return xmlContent; } // No XML found in this chunk return null; } catch (error) { console.warn(`Error processing chunk at offset ${chunkOffset}:`, error); return null; } } /** * Safely decode a buffer to string using the specified encoding * @param buffer Buffer to decode * @param encoding Encoding to use ('utf-8' or 'latin1') * @returns Decoded string */ decodeBufferToString(buffer, encoding) { try { if (encoding === 'utf-8') { return new TextDecoder('utf-8', { fatal: false }).decode(buffer); } else { // For Latin-1 we can use a direct mapping (bytes 0-255 map directly to code points 0-255) // This is more reliable for binary data than TextDecoder for legacy encodings return Array.from(buffer) .map(byte => String.fromCharCode(byte)) .join(''); } } catch (error) { console.warn(`Error decoding buffer using ${encoding}:`, error); // Return empty string on error to allow processing to continue return ''; } } /** * Search for XML patterns in a string * @param content String to search in * @returns XML content or null if not found */ searchForXmlInString(content) { if (!content) return null; // Search for each XML pattern for (const pattern of this.XML_PATTERNS) { const patternIndex = content.indexOf(pattern); if (patternIndex !== -1) { console.log(`Found XML pattern "${pattern}" at position ${patternIndex}`); // Try to extract the XML content starting from the pattern position const xmlContent = this.extractXmlFromString(content, patternIndex); // Validate the extracted content if (xmlContent && this.isValidXml(xmlContent)) { console.log('Successfully extracted and validated XML from text'); return xmlContent; } } } return null; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGV4dC5leHRyYWN0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90cy9mb3JtYXRzL3BkZi9leHRyYWN0b3JzL3RleHQuZXh0cmFjdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRXZEOzs7O0dBSUc7QUFDSCxNQUFNLE9BQU8sZ0JBQWlCLFNBQVEsZ0JBQWdCO0lBQXREOztRQUNFLDhDQUE4QztRQUM3QixlQUFVLEdBQUcsQ0FBQyxHQUFHLElBQUksR0FBRyxJQUFJLENBQUM7UUFFOUMsa0VBQWtFO1FBQ2pELGVBQVUsR0FBRyxDQUFDLENBQUM7UUFFaEMsa0NBQWtDO1FBQ2pCLGlCQUFZLEdBQUc7WUFDOUIsT0FBTztZQUNQLHVCQUF1QjtZQUN2Qix3QkFBd0I7WUFDeEIsVUFBVTtZQUNWLGFBQWE7WUFDYiwyQkFBMkI7WUFDM0IsNEJBQTRCO1lBQzVCLDRCQUE0QjtZQUM1QixjQUFjO1lBQ2QsaUJBQWlCO1lBQ2pCLHFCQUFxQjtTQUN0QixDQUFDO0lBc0lKLENBQUM7SUFwSUM7Ozs7O09BS0c7SUFDSSxLQUFLLENBQUMsVUFBVSxDQUFDLFNBQThCO1FBQ3BELElBQUksQ0FBQztZQUNILE9BQU8sQ0FBQyxHQUFHLENBQUMsa0RBQWtELENBQUMsQ0FBQztZQUVoRSx5Q0FBeUM7WUFDekMsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxVQUFVLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztZQUVsRixnREFBZ0Q7WUFDaEQsT0FBTyxJQUFJLENBQUMsMkJBQTJCLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDbEQsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixPQUFPLENBQUMsS0FBSyxDQUFDLGlDQUFpQyxFQUFFLEtBQUss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