diff --git a/app/lib/pdf/barcodeDecoderWasm.ts b/app/lib/pdf/barcodeDecoderWasm.ts index 464879b..bb48645 100644 --- a/app/lib/pdf/barcodeDecoderWasm.ts +++ b/app/lib/pdf/barcodeDecoderWasm.ts @@ -117,7 +117,7 @@ const file2canvas = async function (imageFile: File): Promise canvas.height = img.height; if (!ctx) { - reject("Context is not set") + reject(new Error("Context is not set")) return; } @@ -232,6 +232,7 @@ const canvasToImageData = (canvas: HTMLCanvasElement): ImageData => { /** * Searches the given canvas for all PDF417 codes and decodes them. + * Uses a slicing strategy to improve detection when multiple barcodes are present. * @param {HTMLCanvasElement} canvas - the canvas to search for PDF417 codes * @return {Promise | null>} - an array of decoded results * */ @@ -240,28 +241,101 @@ const decodeFromCanvas = async (canvas: HTMLCanvasElement): Promise = results - .filter(result => result.text) - .map((result) => ({ - hub3aText: result.text, - billInfo: parseHubText(result.text), - })); + // Pre-allocate canvas pool (max 6 canvases needed for split=5) + const canvasPool = Array.from({ length: 6 }, () => { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + if (!ctx) { + throw new Error('Failed to get canvas context'); + } + return { canvas, ctx }; + }); - return (codesFound); + let bestResult: Array | null = null; + + for (let splitIx = 0; splitIx < splits.length; splitIx++) { + const split = splits[splitIx]; + const sectionsNeeded = split + 1; + + // Add overlap to ensure we don't miss codes at section boundaries + const overlap = split === 0 ? 0 : Math.round(height / 50); // 2% overlap + const sectionHeight = split === 0 ? height : (Math.floor(Math.floor(height / split) + overlap)); + + // Prepare canvases from pool + for (let i = 0; i < canvasPool.length; i++) { + const { canvas: sectionCanvas, ctx: sectionContext } = canvasPool[i]; + + if (i < sectionsNeeded) { + // Resize and use this canvas + sectionCanvas.width = width; + sectionCanvas.height = sectionHeight; + + // Calculate the starting Y position for each section + const startY = i === 0 ? 0 : i * sectionHeight - overlap; + + // Draw the section of the original canvas onto this section canvas + sectionContext.drawImage(canvas, 0, startY, width, sectionHeight, 0, 0, width, sectionHeight); + } else { + // Free unused canvases for this strategy + sectionCanvas.width = 0; + sectionCanvas.height = 0; + } + } + + const codesFoundInSection: Array = []; + + // Try to decode each section (only the ones we're using) + for (let i = 0; i < sectionsNeeded; i++) { + const { canvas: sectionCanvas } = canvasPool[i]; + + try { + // give browser a chance to re-paint + // this is needed to avoid UI freezing when decoding large images + await yieldToBrowser('decodeFromCanvas'); + + const imageData = canvasToImageData(sectionCanvas); + const results = await readBarcodes(imageData, readerOptions); + + if (results.length > 0 && results[0].text) { + const hub3aText = results[0].text; + codesFoundInSection.push({ + hub3aText, + billInfo: parseHubText(hub3aText), + }); + } + + } catch (error) { + // If no code was found in the current section, continue to next section + } + } + + await yieldToBrowser('after decodeFromCanvas'); + + // If in this iteration we found fewer or equal codes than in the previous best result, + // we can return the best result. This is an optimization. + if (bestResult && codesFoundInSection.length <= bestResult.length) { + return bestResult; + } + + bestResult = codesFoundInSection; + } + + return bestResult; } catch (error) { console.log(error); - return (null); + return null; } }