openreader/src/hooks/pdf/usePDFTextProcessing.ts
Richard Roberson 737ab5f919 Restructure
2025-01-29 21:13:18 -07:00

97 lines
3.1 KiB
TypeScript

import { useCallback } from 'react';
import { pdfjs } from 'react-pdf';
import type { TextItem } from 'pdfjs-dist/types/src/display/api';
// Set worker from public directory
pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.mjs';
export function usePDFTextProcessing() {
/**
* Extracts text content from a specific page of the PDF
*
* @param {string} pdfURL - The URL of the PDF
* @param {number} pageNumber - The page number to extract
* @returns {Promise<string>} The extracted text
*/
const extractTextFromPDF = useCallback(async (pdfURL: string, pageNumber: number): Promise<string> => {
try {
const base64Data = pdfURL.split(',')[1];
const binaryData = atob(base64Data);
const bytes = new Uint8Array(binaryData.length);
for (let i = 0; i < binaryData.length; i++) {
bytes[i] = binaryData.charCodeAt(i);
}
const loadingTask = pdfjs.getDocument({ data: bytes });
const pdf = await loadingTask.promise;
// Get only the specified page
const page = await pdf.getPage(pageNumber);
const textContent = await page.getTextContent();
// Filter out non-text items and assert proper type
const textItems = textContent.items.filter((item): item is TextItem =>
'str' in item && 'transform' in item
);
// Group text items into lines based on their vertical position
const tolerance = 2;
const lines: TextItem[][] = [];
let currentLine: TextItem[] = [];
let currentY: number | null = null;
textItems.forEach((item) => {
const y = item.transform[5];
if (currentY === null) {
currentY = y;
currentLine.push(item);
} else if (Math.abs(y - currentY) < tolerance) {
currentLine.push(item);
} else {
lines.push(currentLine);
currentLine = [item];
currentY = y;
}
});
lines.push(currentLine);
// Process each line to build text
let pageText = '';
for (const line of lines) {
// Sort items horizontally within the line
line.sort((a, b) => a.transform[4] - b.transform[4]);
let lineText = '';
let prevItem: TextItem | null = null;
for (const item of line) {
if (!prevItem) {
lineText = item.str;
} else {
const prevEndX = prevItem.transform[4] + (prevItem.width ?? 0);
const currentStartX = item.transform[4];
const space = currentStartX - prevEndX;
// Add space if gap is significant, otherwise concatenate directly
if (space > ((item.width ?? 0) * 0.3)) {
lineText += ' ' + item.str;
} else {
lineText += item.str;
}
}
prevItem = item;
}
pageText += lineText + ' ';
}
return pageText.replace(/\s+/g, ' ').trim();
} catch (error) {
console.error('Error extracting text from PDF:', error);
throw new Error('Failed to extract text from PDF');
}
}, []);
return {
extractTextFromPDF,
};
}