// Full-text search within PDF documents import { PDFDocumentProxy } from "pdfjs-dist"; interface SearchResult { pageNumber: number; text: string; index: number; context: string; } // Full-text search within PDF documents // Procedural implementation (no OOP) interface SearchResult { pageNumber: number; text: string; index: number; context: string; } interface PDFSearchState { doc: PDFDocumentProxy | null; searchResults: SearchResult[]; currentResultIndex: number; } async function initializePDFSearch( doc: PDFDocumentProxy, ): Promise { return { doc, searchResults: [], currentResultIndex: 0, }; } async function searchPDF( state: PDFSearchState, query: string, ): Promise { if (!state.doc) return state; const searchResults: SearchResult[] = []; const lowerQuery = query.toLowerCase(); for (let pageNum = 1; pageNum <= state.doc.numPages; pageNum++) { const page = await state.doc.getPage(pageNum); const textContent = await page.getTextContent(); let fullText = ""; const textItems = textContent.items.map((item) => { if (typeof item === "string") return ""; fullText += item.str; return item.str; }); const pageText = textItems.join(" "); const matches = findSearchMatches(pageText, lowerQuery, pageNum); searchResults.push(...matches); } return { ...state, searchResults }; } function findSearchMatches( text: string, query: string, pageNumber: number, ): SearchResult[] { const matches: SearchResult[] = []; const lowerText = text.toLowerCase(); let index = 0; while ((index = lowerText.indexOf(query, index)) !== -1) { const start = Math.max(0, index - 50); const end = Math.min(text.length, index + query.length + 50); const context = text.slice(start, end); matches.push({ pageNumber, text: text.slice(index, index + query.length), index, context, }); index += query.length; } return matches; } function goToNextSearchResult( state: PDFSearchState, ): PDFSearchState & { result: SearchResult | null } { if (state.searchResults.length === 0) { return { ...state, result: null }; } const newIndex = (state.currentResultIndex + 1) % state.searchResults.length; return { ...state, currentResultIndex: newIndex, result: state.searchResults[newIndex], }; } function goToPreviousSearchResult( state: PDFSearchState, ): PDFSearchState & { result: SearchResult | null } { if (state.searchResults.length === 0) { return { ...state, result: null }; } const newIndex = (state.currentResultIndex - 1 + state.searchResults.length) % state.searchResults.length; return { ...state, currentResultIndex: newIndex, result: state.searchResults[newIndex], }; } function getSearchResultCount(state: PDFSearchState): number { return state.searchResults.length; } function clearSearchResults(state: PDFSearchState): PDFSearchState { return { ...state, searchResults: [], currentResultIndex: 0, }; }