← back to AbramsOS

lib/pdf-parser.js

45 lines

// PDF text extractor for receipt attachments (tier-3 of receipt-extractor pipeline).
// Wraps pdf-parse. Returns extracted plain text + page count + basic metadata.

const fs = require('fs');
const path = require('path');
// Lazy-require pdf-parse to avoid loading its test fixture at startup.
function pdfParse() { return require('pdf-parse'); }

const UPLOADS_DIR = path.join(__dirname, '..', 'uploads');

async function parseFile(absPath) {
  const buf = await fs.promises.readFile(absPath);
  return parseBuffer(buf);
}

async function parseBuffer(buf) {
  const data = await pdfParse()(buf);
  return {
    text: (data.text || '').trim(),
    pages: data.numpages || 0,
    info: data.info || {},
    metadata: data.metadata || null,
    version: data.version || null,
  };
}

/**
 * Resolve a document.object_path to a real file on disk.
 * Drive files are saved as `drive:<id>` with a real file at uploads/drive-<id>.<ext>.
 */
function resolveDocumentPath(objectPath, mime) {
  if (objectPath?.startsWith('drive:')) {
    const driveId = objectPath.slice('drive:'.length);
    const ext = mime === 'application/pdf' ? '.pdf' : mime?.startsWith('image/jpeg') ? '.jpg' : mime?.startsWith('image/png') ? '.png' : '';
    return path.join(UPLOADS_DIR, `drive-${driveId}${ext}`);
  }
  // Future: gmail attachments will save under uploads/gmail-<msg>-<idx>.pdf
  if (objectPath && !path.isAbsolute(objectPath)) {
    return path.join(UPLOADS_DIR, objectPath);
  }
  return objectPath;
}

module.exports = { parseFile, parseBuffer, resolveDocumentPath, UPLOADS_DIR };