web-annotation-renderer 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +49 -42
- package/dist/index.js.map +1 -1
- package/dist/index10.cjs +1 -1
- package/dist/index10.cjs.map +1 -1
- package/dist/index10.js +172 -17
- package/dist/index10.js.map +1 -1
- package/dist/index11.cjs +1 -1
- package/dist/index11.cjs.map +1 -1
- package/dist/index11.js +13 -20
- package/dist/index11.js.map +1 -1
- package/dist/index12.cjs +1 -1
- package/dist/index12.cjs.map +1 -1
- package/dist/index12.js +167 -124
- package/dist/index12.js.map +1 -1
- package/dist/index13.cjs +1 -1
- package/dist/index13.cjs.map +1 -1
- package/dist/index13.js +29 -198
- package/dist/index13.js.map +1 -1
- package/dist/index14.cjs +1 -1
- package/dist/index14.cjs.map +1 -1
- package/dist/index14.js +78 -15
- package/dist/index14.js.map +1 -1
- package/dist/index15.cjs +1 -1
- package/dist/index15.cjs.map +1 -1
- package/dist/index15.js +115 -120
- package/dist/index15.js.map +1 -1
- package/dist/index16.cjs +1 -1
- package/dist/index16.cjs.map +1 -1
- package/dist/index16.js +100 -212
- package/dist/index16.js.map +1 -1
- package/dist/index17.cjs +1 -1
- package/dist/index17.cjs.map +1 -1
- package/dist/index17.js +55 -37
- package/dist/index17.js.map +1 -1
- package/dist/index18.cjs +1 -1
- package/dist/index18.cjs.map +1 -1
- package/dist/index18.js +139 -35
- package/dist/index18.js.map +1 -1
- package/dist/index19.cjs +1 -1
- package/dist/index19.cjs.map +1 -1
- package/dist/index19.js +37 -37
- package/dist/index19.js.map +1 -1
- package/dist/index2.cjs +1 -1
- package/dist/index2.cjs.map +1 -1
- package/dist/index2.js +95 -73
- package/dist/index2.js.map +1 -1
- package/dist/index20.cjs +1 -1
- package/dist/index20.cjs.map +1 -1
- package/dist/index20.js +29 -39
- package/dist/index20.js.map +1 -1
- package/dist/index21.cjs +1 -1
- package/dist/index21.cjs.map +1 -1
- package/dist/index21.js +38 -32
- package/dist/index21.js.map +1 -1
- package/dist/index22.cjs +1 -1
- package/dist/index22.cjs.map +1 -1
- package/dist/index22.js +22 -5
- package/dist/index22.js.map +1 -1
- package/dist/index23.cjs +2 -0
- package/dist/index23.cjs.map +1 -0
- package/dist/index23.js +8 -0
- package/dist/index23.js.map +1 -0
- package/dist/index24.cjs +2 -0
- package/dist/index24.cjs.map +1 -0
- package/dist/index24.js +8 -0
- package/dist/index24.js.map +1 -0
- package/dist/index3.cjs +1 -1
- package/dist/index3.cjs.map +1 -1
- package/dist/index3.js +1 -1
- package/dist/index4.cjs +1 -1
- package/dist/index4.cjs.map +1 -1
- package/dist/index4.js +72 -71
- package/dist/index4.js.map +1 -1
- package/dist/index5.cjs +1 -1
- package/dist/index5.cjs.map +1 -1
- package/dist/index5.js +217 -65
- package/dist/index5.js.map +1 -1
- package/dist/index6.cjs +1 -1
- package/dist/index6.cjs.map +1 -1
- package/dist/index6.js +60 -114
- package/dist/index6.js.map +1 -1
- package/dist/index7.cjs +1 -1
- package/dist/index7.cjs.map +1 -1
- package/dist/index7.js +19 -91
- package/dist/index7.js.map +1 -1
- package/dist/index8.cjs +1 -1
- package/dist/index8.cjs.map +1 -1
- package/dist/index8.js +19 -105
- package/dist/index8.js.map +1 -1
- package/dist/index9.cjs +1 -1
- package/dist/index9.cjs.map +1 -1
- package/dist/index9.js +123 -98
- package/dist/index9.js.map +1 -1
- package/package.json +4 -3
package/dist/index12.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const g=require("react/jsx-runtime"),n=require("react"),q=require("./index2.cjs"),E=require("./index17.cjs");function D({pdfUrl:v,page:u=1,scale:f=1.5,annotations:m=[],currentTime:d=0,audioRef:l,strokeConfig:a,onLoad:w,onError:t,onPageChange:A,className:x,style:C,canvasStyle:L}){const p=n.useRef(null),P=n.useRef(null),r=n.useRef(null),F=n.useRef(Promise.resolve()),[i,S]=n.useState(!1),o=n.useCallback(e=>{F.current=F.current.then(e).catch(s=>{console.error("AnnotPdf: Queued operation failed:",s)})},[]),h=n.useMemo(()=>{if(!a)return;const e=a.preset||"default",s=E.getPreset(e);return{highlight:{color:s.highlight.color,width:a.highlight?.width??s.highlight.width},text:{color:s.handText.color,width:a.text?.width??s.handText.width}}},[a]);n.useEffect(()=>{if(!(!p.current||!P.current)){try{r.current=new q.AnnotationRenderer({canvasElement:p.current,container:P.current,strokeConfig:h}),S(!0)}catch(e){console.error("AnnotPdf: Failed to initialize renderer:",e),t&&t(e)}return()=>{S(!1),r.current&&(r.current.destroy(),r.current=null)}}},[]),n.useEffect(()=>{if(!i||!v)return;let e=!1;return o(async()=>{if(r.current)try{const c=await r.current.loadPDF(v);if(e)return;if(!c.success){console.error("AnnotPdf: Failed to load PDF:",c.error),t&&t(new Error(c.error));return}w&&w({pageCount:c.pageCount})}catch(c){if(e)return;console.error("AnnotPdf: Failed to load PDF:",c),t&&t(c)}}),()=>{e=!0}},[v,i,o]),n.useEffect(()=>{!i||!u||typeof u!="number"||o(async()=>{if(r.current)try{const e=await r.current.setPage(u);if(!e.success){console.error("AnnotPdf: Failed to set page:",e.error),t&&t(new Error(e.error));return}A&&A(u)}catch(e){console.error("AnnotPdf: Failed to set page:",e),t&&t(e)}})},[u,i,o]),n.useEffect(()=>{!i||!f||typeof f!="number"||o(async()=>{if(r.current)try{const e=await r.current.setScale(f);e.success||(console.error("AnnotPdf: Failed to set scale:",e.error),t&&t(new Error(e.error)))}catch(e){console.error("AnnotPdf: Failed to set scale:",e),t&&t(e)}})},[f,i,o]),n.useEffect(()=>{if(!(!i||!r.current))try{r.current.setAnnotations(m||[])}catch(e){console.error("AnnotPdf: Failed to set annotations:",e),t&&t(e)}},[m,i]),n.useEffect(()=>{if(!l&&!(!i||!r.current||d===void 0||d===null))try{r.current.setTime(d)}catch(e){console.error("AnnotPdf: Failed to set time:",e),t&&t(e)}},[d,l,i]),n.useEffect(()=>{if(!l?.current||!i||!r.current)return;const e=l.current,s=r.current,c=()=>{s.startContinuousSync(()=>e.currentTime)},y=()=>{s.stopContinuousSync(),s.setTime(e.currentTime)},b=()=>{s.setTime(e.currentTime)};return e.addEventListener("play",c),e.addEventListener("pause",y),e.addEventListener("ended",y),e.addEventListener("seeked",b),e.paused||c(),()=>{e.removeEventListener("play",c),e.removeEventListener("pause",y),e.removeEventListener("ended",y),e.removeEventListener("seeked",b),s.stopContinuousSync()}},[l,i]);const R=n.useRef(!0);n.useEffect(()=>{if(R.current){R.current=!1;return}if(r.current&&h)try{r.current.updateStrokeConfig(h)}catch(e){console.error("AnnotPdf: Failed to update stroke config:",e),t&&t(e)}},[h]);const k={position:"relative",display:"inline-block",lineHeight:0,...C},T={position:"absolute",top:0,left:0,width:"100%",height:"100%",pointerEvents:"none",overflow:"hidden"},j={display:"block",...L};return g.jsxs("div",{className:x,style:k,children:[g.jsx("canvas",{ref:p,style:j}),g.jsx("div",{ref:P,style:T})]})}exports.default=D;
|
|
2
2
|
//# sourceMappingURL=index12.cjs.map
|
package/dist/index12.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index12.cjs","sources":["../src/utils/pdfExtractor.js"],"sourcesContent":["/**\n * PDF Data Extractor - Extract text and image blocks from PDF\n *\n * Uses PDF.js getOperatorList() for efficient metadata extraction without rendering.\n *\n * @module utils/pdfExtractor\n */\n\nimport * as pdfjsLib from \"pdfjs-dist\";\n\n/**\n * Extract text blocks from a PDF page with normalized coordinates\n *\n * @param {PDFPageProxy} page - PDF.js page object\n * @returns {Promise<Array>} Array of text blocks with normalized bounding boxes (0-1 range)\n */\nasync function extractTextBlocks(page) {\n try {\n const textContent = await page.getTextContent();\n const viewport = page.getViewport({ scale: 1.0 });\n\n const pageWidth = viewport.width;\n const pageHeight = viewport.height;\n\n const textBlocks = [];\n\n for (const item of textContent.items) {\n // Skip empty text\n if (!item.str || !item.str.trim()) {\n continue;\n }\n\n // Get transform matrix [a, b, c, d, e, f]\n const transform = item.transform;\n const fontSize = Math.sqrt(transform[2] * transform[2] + transform[3] * transform[3]);\n\n // Calculate bounding box (absolute coordinates)\n const x = transform[4];\n const y = viewport.height - transform[5]; // Convert to top-left origin\n const width = item.width;\n const height = item.height || fontSize;\n\n const x1 = x;\n const y1 = y - height;\n const x2 = x + width;\n const y2 = y;\n\n // Normalize coordinates to 0-1 range\n const bbox_norm = [\n Math.max(0.0, Math.min(1.0, x1 / pageWidth)),\n Math.max(0.0, Math.min(1.0, y1 / pageHeight)),\n Math.max(0.0, Math.min(1.0, x2 / pageWidth)),\n Math.max(0.0, Math.min(1.0, y2 / pageHeight))\n ];\n\n textBlocks.push({\n type: \"text\",\n text: item.str,\n bbox_norm: bbox_norm,\n font: item.fontName || \"unknown\",\n fontSize: fontSize,\n source: \"pdfjs\"\n });\n }\n\n return textBlocks;\n } catch (error) {\n console.error(\"Error extracting text blocks:\", error);\n return [];\n }\n}\n\n/**\n * Extract normalized bounding box from PDF transformation matrix\n *\n * @param {Array} transform - PDF transformation matrix [a, b, c, d, e, f]\n * @param {number} pageWidth - Page width for normalization\n * @param {number} pageHeight - Page height for coordinate conversion and normalization\n * @returns {Array} Normalized bounding box [x1, y1, x2, y2] in 0-1 range\n */\nfunction extractBBoxFromTransform(transform, pageWidth, pageHeight) {\n const x = transform[4];\n const y = transform[5];\n const scaleX = transform[0];\n const scaleY = transform[3];\n\n // Scale factors represent image dimensions\n const width = Math.abs(scaleX);\n const height = Math.abs(scaleY);\n\n // Convert from PDF bottom-left to image top-left origin\n const y1 = pageHeight - y - height;\n const y2 = pageHeight - y;\n const x1 = x;\n const x2 = x + width;\n\n // Normalize to 0-1 range\n return [\n Math.max(0.0, Math.min(1.0, x1 / pageWidth)),\n Math.max(0.0, Math.min(1.0, y1 / pageHeight)),\n Math.max(0.0, Math.min(1.0, x2 / pageWidth)),\n Math.max(0.0, Math.min(1.0, y2 / pageHeight))\n ];\n}\n\n/**\n * Multiply two transformation matrices\n *\n * @param {Array} m1 - First matrix [a, b, c, d, e, f]\n * @param {Array} m2 - Second matrix [a, b, c, d, e, f]\n * @returns {Array} Result matrix [a, b, c, d, e, f]\n */\nfunction multiplyTransforms(m1, m2) {\n return [\n m1[0] * m2[0] + m1[2] * m2[1],\n m1[1] * m2[0] + m1[3] * m2[1],\n m1[0] * m2[2] + m1[2] * m2[3],\n m1[1] * m2[2] + m1[3] * m2[3],\n m1[0] * m2[4] + m1[2] * m2[5] + m1[4],\n m1[1] * m2[4] + m1[3] * m2[5] + m1[5]\n ];\n}\n\n/**\n * Get human-readable operator name\n *\n * @param {number} op - Operator code\n * @param {Object} OPS - PDF.js OPS constants\n * @returns {string} Operator name\n */\nfunction getOperatorName(op, OPS) {\n const opNames = {\n [OPS.paintImageXObject]: \"paintImageXObject\",\n [OPS.paintInlineImageXObject]: \"paintInlineImageXObject\",\n [OPS.paintImageMaskXObject]: \"paintImageMaskXObject\",\n [OPS.paintJpegXObject]: \"paintJpegXObject\"\n };\n return opNames[op] || \"unknown\";\n}\n\n/**\n * Extract image blocks from a PDF page using getOperatorList with normalized coordinates\n *\n * @param {PDFPageProxy} page - PDF.js page object\n * @returns {Promise<Array>} Array of image blocks with normalized bounding boxes (0-1 range)\n */\nasync function extractImageBlocks(page) {\n try {\n const operatorList = await page.getOperatorList();\n const viewport = page.getViewport({ scale: 1.0 });\n\n const pageWidth = viewport.width;\n const pageHeight = viewport.height;\n\n const imageBlocks = [];\n const graphicsStateStack = []; // Track graphics state transformations\n let currentTransform = [1, 0, 0, 1, 0, 0]; // Identity matrix\n\n const ops = operatorList.fnArray;\n const args = operatorList.argsArray;\n\n // Image paint operator constants from PDF.js\n const OPS = pdfjsLib.OPS;\n const IMAGE_OPS = [\n OPS.paintImageXObject,\n OPS.paintInlineImageXObject,\n OPS.paintImageMaskXObject,\n OPS.paintJpegXObject\n ];\n\n for (let i = 0; i < ops.length; i++) {\n const op = ops[i];\n const arg = args[i];\n\n // Track graphics state stack\n if (op === OPS.save) {\n graphicsStateStack.push([...currentTransform]);\n } else if (op === OPS.restore) {\n if (graphicsStateStack.length > 0) {\n currentTransform = graphicsStateStack.pop();\n }\n } else if (op === OPS.transform) {\n // Update current transformation matrix\n const [a, b, c, d, e, f] = arg;\n currentTransform = multiplyTransforms(currentTransform, [a, b, c, d, e, f]);\n } else if (IMAGE_OPS.includes(op)) {\n // Extract image bounding box (normalized)\n const bbox_norm = extractBBoxFromTransform(currentTransform, pageWidth, pageHeight);\n\n imageBlocks.push({\n type: \"image\",\n bbox_norm: bbox_norm,\n source: \"pdfjs\",\n operator: getOperatorName(op, OPS)\n });\n }\n }\n\n return imageBlocks;\n } catch (error) {\n console.error(\"Error extracting image blocks:\", error);\n return [];\n }\n}\n\n/**\n * Extract all blocks (text and images) from a PDF page\n *\n * @param {string} pdfPath - Path to PDF file\n * @param {number} pageNumber - Page number (1-indexed)\n * @returns {Promise<Object>} Object with textBlocks and imageBlocks arrays\n */\nexport async function extractAllBlocks(pdfPath, pageNumber) {\n try {\n // Load PDF document\n const loadingTask = pdfjsLib.getDocument(pdfPath);\n const pdfDoc = await loadingTask.promise;\n\n // Validate page number\n if (pageNumber < 1 || pageNumber > pdfDoc.numPages) {\n throw new Error(`Invalid page number: ${pageNumber}. Document has ${pdfDoc.numPages} pages.`);\n }\n\n // Get page\n const page = await pdfDoc.getPage(pageNumber);\n const viewport = page.getViewport({ scale: 1.0 });\n\n // Extract text and image blocks in parallel\n const [textBlocks, imageBlocks] = await Promise.all([\n extractTextBlocks(page),\n extractImageBlocks(page)\n ]);\n\n return {\n textBlocks,\n imageBlocks,\n pageDimensions: {\n width: viewport.width,\n height: viewport.height\n },\n pageNumber: pageNumber\n };\n } catch (error) {\n console.error(`Error extracting blocks from page ${pageNumber}:`, error);\n throw error;\n }\n}\n\n/**\n * Extract blocks from multiple pages\n *\n * @param {string} pdfPath - Path to PDF file\n * @param {Array<number>} pageNumbers - Array of page numbers (1-indexed)\n * @returns {Promise<Array>} Array of extraction results, one per page\n */\nexport async function extractMultiplePages(pdfPath, pageNumbers) {\n const results = [];\n\n for (const pageNum of pageNumbers) {\n try {\n const pageData = await extractAllBlocks(pdfPath, pageNum);\n results.push(pageData);\n } catch (error) {\n console.error(`Failed to extract page ${pageNum}:`, error);\n results.push({\n textBlocks: [],\n imageBlocks: [],\n pageDimensions: null,\n pageNumber: pageNum,\n error: error.message\n });\n }\n }\n\n return results;\n}\n"],"names":["extractTextBlocks","page","textContent","viewport","pageWidth","pageHeight","textBlocks","item","transform","fontSize","x","y","width","height","x1","y1","x2","y2","bbox_norm","error","extractBBoxFromTransform","scaleX","scaleY","multiplyTransforms","m1","m2","getOperatorName","op","OPS","extractImageBlocks","operatorList","imageBlocks","graphicsStateStack","currentTransform","ops","args","pdfjsLib","IMAGE_OPS","i","arg","a","b","c","d","e","f","extractAllBlocks","pdfPath","pageNumber","pdfDoc","extractMultiplePages","pageNumbers","results","pageNum","pageData"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index12.cjs","sources":["../src/adapters/AnnotPdf.jsx"],"sourcesContent":["// ============================================================================\n// SECTION 1: IMPORTS\n// ============================================================================\n\nimport { useRef, useEffect, useCallback, useMemo, useState } from 'react';\nimport { AnnotationRenderer } from '../core/AnnotationRenderer.js';\nimport { getPreset } from '../pen/presets.js';\n\n// ============================================================================\n// SECTION 2: JSDOC DOCUMENTATION\n// ============================================================================\n\n/**\n * AnnotPdf - Declarative React component for PDF annotation rendering\n *\n * A React wrapper around the AnnotationRenderer core engine that provides\n * a declarative, props-based API for rendering PDF documents with\n * timeline-synchronized annotations.\n *\n * Features:\n * - Automatic lifecycle management (initialization and cleanup)\n * - Declarative prop-to-method synchronization\n * - PDF rendering with pdf.js\n * - Timeline-synchronized annotation display\n * - Support for highlight, text, and ink annotations\n * - Page navigation and zoom control\n *\n * @component\n * @example\n * // Basic usage\n * <AnnotPdf\n * pdfUrl=\"/document.pdf\"\n * page={1}\n * scale={1.5}\n * annotations={[]}\n * currentTime={0}\n * />\n *\n * @example\n * // With audio synchronization (discrete mode - ~4fps)\n * const [currentTime, setCurrentTime] = useState(0);\n *\n * <div>\n * <AnnotPdf\n * pdfUrl=\"/lecture.pdf\"\n * page={1}\n * scale={1.5}\n * annotations={annotations}\n * currentTime={currentTime}\n * onLoad={({ pageCount }) => console.log('Loaded:', pageCount)}\n * />\n * <audio\n * src=\"/lecture.mp3\"\n * onTimeUpdate={(e) => setCurrentTime(e.target.currentTime)}\n * controls\n * />\n * </div>\n *\n * @example\n * // With audio synchronization (continuous mode - 60fps, smoother)\n * const audioRef = useRef(null);\n *\n * <div>\n * <AnnotPdf\n * pdfUrl=\"/lecture.pdf\"\n * page={1}\n * scale={1.5}\n * annotations={annotations}\n * audioRef={audioRef}\n * onLoad={({ pageCount }) => console.log('Loaded:', pageCount)}\n * />\n * <audio ref={audioRef} src=\"/lecture.mp3\" controls />\n * </div>\n *\n * @param {Object} props - Component props\n * @param {string} props.pdfUrl - PDF document URL (required)\n * @param {number} [props.page=1] - Current page number (1-indexed)\n * @param {number} [props.scale=1.5] - Zoom scale factor\n * @param {Array} [props.annotations=[]] - Array of annotation objects\n * @param {number} [props.currentTime=0] - Timeline position in seconds (discrete mode)\n * @param {React.RefObject<HTMLAudioElement>} [props.audioRef] - Audio element ref for continuous sync (60fps)\n * @param {Function} [props.onLoad] - Callback when PDF loads: ({pageCount}) => void\n * @param {Function} [props.onError] - Callback on error: (error) => void\n * @param {Function} [props.onPageChange] - Callback when page changes: (page) => void\n * @param {Object} [props.strokeConfig] - Stroke rendering configuration\n * @param {string} [props.strokeConfig.preset] - Preset name: 'default', 'blue', 'minimal'\n * @param {Object} [props.strokeConfig.highlight] - Highlight style overrides\n * @param {number} [props.strokeConfig.highlight.width] - Highlight stroke width (16-32)\n * @param {Object} [props.strokeConfig.text] - Text style overrides\n * @param {number} [props.strokeConfig.text.width] - Text stroke width (1-4)\n * @param {string} [props.className] - CSS class for container div\n * @param {Object} [props.style] - Inline styles for container div\n * @param {Object} [props.canvasStyle] - Inline styles for canvas element\n * @returns {JSX.Element} PDF viewer component with annotation layers\n */\n\n// ============================================================================\n// SECTION 3: COMPONENT DEFINITION\n// ============================================================================\n\nfunction AnnotPdf({\n // Required props\n pdfUrl,\n\n // Optional props with defaults\n page = 1,\n scale = 1.5,\n annotations = [],\n currentTime = 0,\n\n // Continuous sync mode (use instead of currentTime for smoother animation)\n audioRef,\n\n // Stroke configuration\n strokeConfig,\n\n // Callbacks\n onLoad,\n onError,\n onPageChange,\n\n // Styling\n className,\n style,\n canvasStyle\n}) {\n\n // ==========================================================================\n // SECTION 4: REFS INITIALIZATION\n // ==========================================================================\n\n /**\n * Reference to the canvas element for PDF rendering\n * @type {React.RefObject<HTMLCanvasElement>}\n */\n const canvasRef = useRef(null);\n\n /**\n * Reference to the layer container div for annotation layers\n * @type {React.RefObject<HTMLDivElement>}\n */\n const layerContainerRef = useRef(null);\n\n /**\n * Reference to the AnnotationRenderer engine instance\n * Stored in ref to avoid triggering re-renders\n * @type {React.RefObject<AnnotationRenderer|null>}\n */\n const engineRef = useRef(null);\n\n /**\n * Reference to the render operation queue\n * Ensures sequential execution of async canvas operations\n * Prevents PDF.js race condition: \"Cannot use the same canvas during multiple render() operations\"\n * @type {React.RefObject<Promise<void>>}\n */\n const renderQueue = useRef(Promise.resolve());\n\n /**\n * State to track engine initialization\n * Used to trigger dependent effects after engine is ready\n */\n const [isEngineReady, setEngineReady] = useState(false);\n\n // ==========================================================================\n // SECTION 4.5: RENDER QUEUE HELPER\n // ==========================================================================\n\n /**\n * Queue a render operation to execute sequentially\n *\n * This helper ensures that async canvas operations (loadPDF, setPage, setScale)\n * execute one at a time, preventing concurrent access to the PDF.js canvas.\n * Uses Promise chaining to maintain operation order.\n *\n * @param {Function} operation - Async function returning a Promise\n * @returns {void}\n */\n const queueOperation = useCallback((operation) => {\n renderQueue.current = renderQueue.current\n .then(operation)\n .catch(error => {\n // Log errors but don't break the queue\n console.error('AnnotPdf: Queued operation failed:', error);\n });\n }, []);\n\n // ==========================================================================\n // SECTION 4.6: STROKE CONFIG BUILDER\n // ==========================================================================\n\n /**\n * Build merged stroke config from preset and overrides\n *\n * If strokeConfig.preset is provided, loads base config from presets.\n * Then merges any override values (highlight.width, text.width).\n */\n const mergedStrokeConfig = useMemo(() => {\n if (!strokeConfig) {\n return undefined;\n }\n\n // Start with preset or empty object\n const presetName = strokeConfig.preset || 'default';\n const presetConfig = getPreset(presetName);\n\n // Build config object matching StrokeRenderer format\n const config = {\n highlight: {\n color: presetConfig.highlight.color,\n width: strokeConfig.highlight?.width ?? presetConfig.highlight.width\n },\n text: {\n color: presetConfig.handText.color,\n width: strokeConfig.text?.width ?? presetConfig.handText.width\n }\n };\n\n return config;\n }, [strokeConfig]);\n\n // ==========================================================================\n // SECTION 5: ENGINE INITIALIZATION AND CLEANUP\n // ==========================================================================\n\n /**\n * Initialize AnnotationRenderer on component mount\n * Cleanup on component unmount\n */\n useEffect(() => {\n // Guard: Wait for DOM elements to be ready\n if (!canvasRef.current || !layerContainerRef.current) {\n return;\n }\n\n // Initialize engine with stroke config if provided\n try {\n engineRef.current = new AnnotationRenderer({\n canvasElement: canvasRef.current,\n container: layerContainerRef.current,\n strokeConfig: mergedStrokeConfig\n });\n // Signal that engine is ready for dependent effects\n setEngineReady(true);\n } catch (error) {\n console.error('AnnotPdf: Failed to initialize renderer:', error);\n if (onError) {\n onError(error);\n }\n }\n\n // Cleanup on unmount\n return () => {\n setEngineReady(false);\n if (engineRef.current) {\n engineRef.current.destroy();\n engineRef.current = null;\n }\n };\n }, []); // Empty deps - run once on mount\n\n // ==========================================================================\n // SECTION 6: PDF LOADING SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Load PDF document when pdfUrl prop changes\n * Handles async operation with cancellation support\n * Uses render queue to prevent concurrent canvas operations\n */\n useEffect(() => {\n // Guard: Wait for engine and valid pdfUrl\n if (!isEngineReady || !pdfUrl) {\n return;\n }\n\n let cancelled = false;\n\n const loadPdf = async () => {\n // Check engine exists at execution time (not queue time)\n if (!engineRef.current) {\n return;\n }\n\n try {\n const result = await engineRef.current.loadPDF(pdfUrl);\n\n // Check if component unmounted during async operation\n if (cancelled) return;\n\n // Check if load was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to load PDF:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n return;\n }\n\n // Call onLoad callback with pageCount from result\n if (onLoad) {\n onLoad({ pageCount: result.pageCount });\n }\n } catch (error) {\n if (cancelled) return;\n\n console.error('AnnotPdf: Failed to load PDF:', error);\n if (onError) {\n onError(error);\n }\n }\n };\n\n // Queue the PDF loading operation to prevent race conditions\n queueOperation(loadPdf);\n\n // Cleanup: Prevent state updates if component unmounts during load\n return () => {\n cancelled = true;\n };\n }, [pdfUrl, isEngineReady, queueOperation]);\n\n // ==========================================================================\n // SECTION 7: PAGE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync page prop to engine.setPage() method\n * Uses render queue to prevent concurrent canvas operations\n */\n useEffect(() => {\n // Guard: Wait for engine and valid page\n if (!isEngineReady || !page || typeof page !== 'number') {\n return;\n }\n\n // Queue the page change operation to prevent race conditions\n queueOperation(async () => {\n // Check engine exists at execution time (not queue time)\n if (!engineRef.current) {\n return;\n }\n\n try {\n const result = await engineRef.current.setPage(page);\n\n // Check if page change was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to set page:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n return;\n }\n\n // Optional: Notify parent of successful page change\n if (onPageChange) {\n onPageChange(page);\n }\n } catch (error) {\n console.error('AnnotPdf: Failed to set page:', error);\n if (onError) {\n onError(error);\n }\n }\n });\n }, [page, isEngineReady, queueOperation]);\n\n // ==========================================================================\n // SECTION 8: SCALE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync scale prop to engine.setScale() method\n * Uses render queue to prevent concurrent canvas operations\n */\n useEffect(() => {\n // Guard: Wait for engine and valid scale\n if (!isEngineReady || !scale || typeof scale !== 'number') {\n return;\n }\n\n // Queue the scale change operation to prevent race conditions\n queueOperation(async () => {\n // Check engine exists at execution time (not queue time)\n if (!engineRef.current) {\n return;\n }\n\n try {\n const result = await engineRef.current.setScale(scale);\n\n // Check if scale change was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to set scale:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n }\n } catch (error) {\n console.error('AnnotPdf: Failed to set scale:', error);\n if (onError) {\n onError(error);\n }\n }\n });\n }, [scale, isEngineReady, queueOperation]);\n\n // ==========================================================================\n // SECTION 9: ANNOTATIONS SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync annotations prop to engine.setAnnotations() method\n */\n useEffect(() => {\n // Guard: Wait for engine\n if (!isEngineReady || !engineRef.current) {\n return;\n }\n\n // Sync annotations to engine (default to empty array)\n try {\n engineRef.current.setAnnotations(annotations || []);\n } catch (error) {\n console.error('AnnotPdf: Failed to set annotations:', error);\n if (onError) {\n onError(error);\n }\n }\n }, [annotations, isEngineReady]);\n\n // ==========================================================================\n // SECTION 10: TIMELINE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync currentTime prop to engine.setTime() method\n * Only used when audioRef is NOT provided (discrete mode)\n */\n useEffect(() => {\n // Skip if using continuous sync mode\n if (audioRef) {\n return;\n }\n\n // Guard: Wait for engine and valid currentTime\n if (!isEngineReady || !engineRef.current || currentTime === undefined || currentTime === null) {\n return;\n }\n\n // Sync timeline to engine\n try {\n engineRef.current.setTime(currentTime);\n } catch (error) {\n console.error('AnnotPdf: Failed to set time:', error);\n if (onError) {\n onError(error);\n }\n }\n }, [currentTime, audioRef, isEngineReady]);\n\n // ==========================================================================\n // SECTION 10.1: CONTINUOUS SYNC MODE (audioRef)\n // ==========================================================================\n\n /**\n * Manage continuous sync mode when audioRef is provided\n *\n * Uses requestAnimationFrame for 60fps smooth animation instead of\n * discrete currentTime updates (~4fps from audio timeupdate events).\n */\n useEffect(() => {\n // Guard: Skip if not using continuous sync mode\n if (!audioRef?.current) {\n return;\n }\n\n // Guard: Wait for engine\n if (!isEngineReady || !engineRef.current) {\n return;\n }\n\n const audio = audioRef.current;\n const engine = engineRef.current;\n\n /**\n * Start continuous sync when audio plays\n */\n const handlePlay = () => {\n engine.startContinuousSync(() => audio.currentTime);\n };\n\n /**\n * Stop continuous sync when audio pauses\n */\n const handlePause = () => {\n engine.stopContinuousSync();\n // Do one final sync to ensure we're at the correct position\n engine.setTime(audio.currentTime);\n };\n\n /**\n * Handle seeking - update time immediately\n */\n const handleSeeked = () => {\n engine.setTime(audio.currentTime);\n };\n\n // Add event listeners\n audio.addEventListener('play', handlePlay);\n audio.addEventListener('pause', handlePause);\n audio.addEventListener('ended', handlePause);\n audio.addEventListener('seeked', handleSeeked);\n\n // If audio is already playing, start sync immediately\n if (!audio.paused) {\n handlePlay();\n }\n\n // Cleanup on unmount or audioRef change\n return () => {\n audio.removeEventListener('play', handlePlay);\n audio.removeEventListener('pause', handlePause);\n audio.removeEventListener('ended', handlePause);\n audio.removeEventListener('seeked', handleSeeked);\n engine.stopContinuousSync();\n };\n }, [audioRef, isEngineReady]);\n\n // ==========================================================================\n // SECTION 10.5: STROKE CONFIG SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync strokeConfig prop to engine at runtime for live preview\n * Skips initial render (handled by initialization)\n */\n const isFirstRender = useRef(true);\n\n useEffect(() => {\n // Skip first render - config is passed during initialization\n if (isFirstRender.current) {\n isFirstRender.current = false;\n return;\n }\n\n // Guard: Engine must exist\n if (!engineRef.current) {\n return;\n }\n\n // Update stroke config if provided\n if (mergedStrokeConfig) {\n try {\n engineRef.current.updateStrokeConfig(mergedStrokeConfig);\n } catch (error) {\n console.error('AnnotPdf: Failed to update stroke config:', error);\n if (onError) {\n onError(error);\n }\n }\n }\n }, [mergedStrokeConfig]);\n\n // ==========================================================================\n // SECTION 11: STYLING DEFINITIONS\n // ==========================================================================\n\n /**\n * Default container styles\n * Merged with user-provided styles (user styles override defaults)\n */\n const defaultContainerStyle = {\n position: 'relative',\n display: 'inline-block',\n lineHeight: 0, // Remove extra space below canvas\n ...style // User styles override defaults\n };\n\n /**\n * Default layer container styles\n * Positions layer div absolutely over canvas\n */\n const defaultLayerStyle = {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n pointerEvents: 'none', // Allow clicks to pass through to canvas\n overflow: 'hidden'\n };\n\n /**\n * Default canvas styles\n * Merged with user-provided canvasStyle\n */\n const defaultCanvasStyle = {\n display: 'block',\n ...canvasStyle // User styles override defaults\n };\n\n // ==========================================================================\n // SECTION 12: JSX RETURN\n // ==========================================================================\n\n return (\n <div className={className} style={defaultContainerStyle}>\n <canvas ref={canvasRef} style={defaultCanvasStyle} />\n <div ref={layerContainerRef} style={defaultLayerStyle} />\n </div>\n );\n}\n\n// ============================================================================\n// SECTION 13: EXPORT\n// ============================================================================\n\nexport default AnnotPdf;\n"],"names":["AnnotPdf","pdfUrl","page","scale","annotations","currentTime","audioRef","strokeConfig","onLoad","onError","onPageChange","className","style","canvasStyle","canvasRef","useRef","layerContainerRef","engineRef","renderQueue","isEngineReady","setEngineReady","useState","queueOperation","useCallback","operation","error","mergedStrokeConfig","useMemo","presetName","presetConfig","getPreset","useEffect","AnnotationRenderer","cancelled","result","audio","engine","handlePlay","handlePause","handleSeeked","isFirstRender","defaultContainerStyle","defaultLayerStyle","defaultCanvasStyle","jsxs","jsx"],"mappings":"yNAoGA,SAASA,EAAS,CAEhB,OAAAC,EAGA,KAAAC,EAAO,EACP,MAAAC,EAAQ,IACR,YAAAC,EAAc,CAAA,EACd,YAAAC,EAAc,EAGd,SAAAC,EAGA,aAAAC,EAGA,OAAAC,EACA,QAAAC,EACA,aAAAC,EAGA,UAAAC,EACA,MAAAC,EACA,YAAAC,CACF,EAAG,CAUD,MAAMC,EAAYC,EAAAA,OAAO,IAAI,EAMvBC,EAAoBD,EAAAA,OAAO,IAAI,EAO/BE,EAAYF,EAAAA,OAAO,IAAI,EAQvBG,EAAcH,EAAAA,OAAO,QAAQ,QAAA,CAAS,EAMtC,CAACI,EAAeC,CAAc,EAAIC,EAAAA,SAAS,EAAK,EAgBhDC,EAAiBC,cAAaC,GAAc,CAChDN,EAAY,QAAUA,EAAY,QAC/B,KAAKM,CAAS,EACd,MAAMC,GAAS,CAEd,QAAQ,MAAM,qCAAsCA,CAAK,CAC3D,CAAC,CACL,EAAG,CAAA,CAAE,EAYCC,EAAqBC,EAAAA,QAAQ,IAAM,CACvC,GAAI,CAACpB,EACH,OAIF,MAAMqB,EAAarB,EAAa,QAAU,UACpCsB,EAAeC,EAAAA,UAAUF,CAAU,EAczC,MAXe,CACb,UAAW,CACT,MAAOC,EAAa,UAAU,MAC9B,MAAOtB,EAAa,WAAW,OAASsB,EAAa,UAAU,KAAA,EAEjE,KAAM,CACJ,MAAOA,EAAa,SAAS,MAC7B,MAAOtB,EAAa,MAAM,OAASsB,EAAa,SAAS,KAAA,CAC3D,CAIJ,EAAG,CAACtB,CAAY,CAAC,EAUjBwB,EAAAA,UAAU,IAAM,CAEd,GAAI,GAACjB,EAAU,SAAW,CAACE,EAAkB,SAK7C,IAAI,CACFC,EAAU,QAAU,IAAIe,qBAAmB,CACzC,cAAelB,EAAU,QACzB,UAAWE,EAAkB,QAC7B,aAAcU,CAAA,CACf,EAEDN,EAAe,EAAI,CACrB,OAASK,EAAO,CACd,QAAQ,MAAM,2CAA4CA,CAAK,EAC3DhB,GACFA,EAAQgB,CAAK,CAEjB,CAGA,MAAO,IAAM,CACXL,EAAe,EAAK,EAChBH,EAAU,UACZA,EAAU,QAAQ,QAAA,EAClBA,EAAU,QAAU,KAExB,EACF,EAAG,CAAA,CAAE,EAWLc,EAAAA,UAAU,IAAM,CAEd,GAAI,CAACZ,GAAiB,CAAClB,EACrB,OAGF,IAAIgC,EAAY,GAsChB,OAAAX,EApCgB,SAAY,CAE1B,GAAKL,EAAU,QAIf,GAAI,CACF,MAAMiB,EAAS,MAAMjB,EAAU,QAAQ,QAAQhB,CAAM,EAGrD,GAAIgC,EAAW,OAGf,GAAI,CAACC,EAAO,QAAS,CACnB,QAAQ,MAAM,gCAAiCA,EAAO,KAAK,EACvDzB,GACFA,EAAQ,IAAI,MAAMyB,EAAO,KAAK,CAAC,EAEjC,MACF,CAGI1B,GACFA,EAAO,CAAE,UAAW0B,EAAO,SAAA,CAAW,CAE1C,OAAST,EAAO,CACd,GAAIQ,EAAW,OAEf,QAAQ,MAAM,gCAAiCR,CAAK,EAChDhB,GACFA,EAAQgB,CAAK,CAEjB,CACF,CAGsB,EAGf,IAAM,CACXQ,EAAY,EACd,CACF,EAAG,CAAChC,EAAQkB,EAAeG,CAAc,CAAC,EAU1CS,EAAAA,UAAU,IAAM,CAEV,CAACZ,GAAiB,CAACjB,GAAQ,OAAOA,GAAS,UAK/CoB,EAAe,SAAY,CAEzB,GAAKL,EAAU,QAIf,GAAI,CACF,MAAMiB,EAAS,MAAMjB,EAAU,QAAQ,QAAQf,CAAI,EAGnD,GAAI,CAACgC,EAAO,QAAS,CACnB,QAAQ,MAAM,gCAAiCA,EAAO,KAAK,EACvDzB,GACFA,EAAQ,IAAI,MAAMyB,EAAO,KAAK,CAAC,EAEjC,MACF,CAGIxB,GACFA,EAAaR,CAAI,CAErB,OAASuB,EAAO,CACd,QAAQ,MAAM,gCAAiCA,CAAK,EAChDhB,GACFA,EAAQgB,CAAK,CAEjB,CACF,CAAC,CACH,EAAG,CAACvB,EAAMiB,EAAeG,CAAc,CAAC,EAUxCS,EAAAA,UAAU,IAAM,CAEV,CAACZ,GAAiB,CAAChB,GAAS,OAAOA,GAAU,UAKjDmB,EAAe,SAAY,CAEzB,GAAKL,EAAU,QAIf,GAAI,CACF,MAAMiB,EAAS,MAAMjB,EAAU,QAAQ,SAASd,CAAK,EAGhD+B,EAAO,UACV,QAAQ,MAAM,iCAAkCA,EAAO,KAAK,EACxDzB,GACFA,EAAQ,IAAI,MAAMyB,EAAO,KAAK,CAAC,EAGrC,OAAST,EAAO,CACd,QAAQ,MAAM,iCAAkCA,CAAK,EACjDhB,GACFA,EAAQgB,CAAK,CAEjB,CACF,CAAC,CACH,EAAG,CAACtB,EAAOgB,EAAeG,CAAc,CAAC,EASzCS,EAAAA,UAAU,IAAM,CAEd,GAAI,GAACZ,GAAiB,CAACF,EAAU,SAKjC,GAAI,CACFA,EAAU,QAAQ,eAAeb,GAAe,CAAA,CAAE,CACpD,OAASqB,EAAO,CACd,QAAQ,MAAM,uCAAwCA,CAAK,EACvDhB,GACFA,EAAQgB,CAAK,CAEjB,CACF,EAAG,CAACrB,EAAae,CAAa,CAAC,EAU/BY,EAAAA,UAAU,IAAM,CAEd,GAAI,CAAAzB,GAKA,GAACa,GAAiB,CAACF,EAAU,SAAWZ,IAAgB,QAAaA,IAAgB,MAKzF,GAAI,CACFY,EAAU,QAAQ,QAAQZ,CAAW,CACvC,OAASoB,EAAO,CACd,QAAQ,MAAM,gCAAiCA,CAAK,EAChDhB,GACFA,EAAQgB,CAAK,CAEjB,CACF,EAAG,CAACpB,EAAaC,EAAUa,CAAa,CAAC,EAYzCY,EAAAA,UAAU,IAAM,CAOd,GALI,CAACzB,GAAU,SAKX,CAACa,GAAiB,CAACF,EAAU,QAC/B,OAGF,MAAMkB,EAAQ7B,EAAS,QACjB8B,EAASnB,EAAU,QAKnBoB,EAAa,IAAM,CACvBD,EAAO,oBAAoB,IAAMD,EAAM,WAAW,CACpD,EAKMG,EAAc,IAAM,CACxBF,EAAO,mBAAA,EAEPA,EAAO,QAAQD,EAAM,WAAW,CAClC,EAKMI,EAAe,IAAM,CACzBH,EAAO,QAAQD,EAAM,WAAW,CAClC,EAGA,OAAAA,EAAM,iBAAiB,OAAQE,CAAU,EACzCF,EAAM,iBAAiB,QAASG,CAAW,EAC3CH,EAAM,iBAAiB,QAASG,CAAW,EAC3CH,EAAM,iBAAiB,SAAUI,CAAY,EAGxCJ,EAAM,QACTE,EAAA,EAIK,IAAM,CACXF,EAAM,oBAAoB,OAAQE,CAAU,EAC5CF,EAAM,oBAAoB,QAASG,CAAW,EAC9CH,EAAM,oBAAoB,QAASG,CAAW,EAC9CH,EAAM,oBAAoB,SAAUI,CAAY,EAChDH,EAAO,mBAAA,CACT,CACF,EAAG,CAAC9B,EAAUa,CAAa,CAAC,EAU5B,MAAMqB,EAAgBzB,EAAAA,OAAO,EAAI,EAEjCgB,EAAAA,UAAU,IAAM,CAEd,GAAIS,EAAc,QAAS,CACzBA,EAAc,QAAU,GACxB,MACF,CAGA,GAAKvB,EAAU,SAKXS,EACF,GAAI,CACFT,EAAU,QAAQ,mBAAmBS,CAAkB,CACzD,OAASD,EAAO,CACd,QAAQ,MAAM,4CAA6CA,CAAK,EAC5DhB,GACFA,EAAQgB,CAAK,CAEjB,CAEJ,EAAG,CAACC,CAAkB,CAAC,EAUvB,MAAMe,EAAwB,CAC5B,SAAU,WACV,QAAS,eACT,WAAY,EACZ,GAAG7B,CAAA,EAOC8B,EAAoB,CACxB,SAAU,WACV,IAAK,EACL,KAAM,EACN,MAAO,OACP,OAAQ,OACR,cAAe,OACf,SAAU,QAAA,EAONC,EAAqB,CACzB,QAAS,QACT,GAAG9B,CAAA,EAOL,OACE+B,EAAAA,KAAC,MAAA,CAAI,UAAAjC,EAAsB,MAAO8B,EAChC,SAAA,CAAAI,EAAAA,IAAC,SAAA,CAAO,IAAK/B,EAAW,MAAO6B,EAAoB,EACnDE,EAAAA,IAAC,MAAA,CAAI,IAAK7B,EAAmB,MAAO0B,CAAA,CAAmB,CAAA,EACzD,CAEJ"}
|
package/dist/index12.js
CHANGED
|
@@ -1,132 +1,175 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Math.max(0, Math.min(1, c / o))
|
|
35
|
-
];
|
|
36
|
-
}
|
|
37
|
-
function I(e, t) {
|
|
38
|
-
return [
|
|
39
|
-
e[0] * t[0] + e[2] * t[1],
|
|
40
|
-
e[1] * t[0] + e[3] * t[1],
|
|
41
|
-
e[0] * t[2] + e[2] * t[3],
|
|
42
|
-
e[1] * t[2] + e[3] * t[3],
|
|
43
|
-
e[0] * t[4] + e[2] * t[5] + e[4],
|
|
44
|
-
e[1] * t[4] + e[3] * t[5] + e[5]
|
|
45
|
-
];
|
|
46
|
-
}
|
|
47
|
-
function O(e, t) {
|
|
48
|
-
return {
|
|
49
|
-
[t.paintImageXObject]: "paintImageXObject",
|
|
50
|
-
[t.paintInlineImageXObject]: "paintInlineImageXObject",
|
|
51
|
-
[t.paintImageMaskXObject]: "paintImageMaskXObject",
|
|
52
|
-
[t.paintJpegXObject]: "paintJpegXObject"
|
|
53
|
-
}[e] || "unknown";
|
|
54
|
-
}
|
|
55
|
-
async function X(e) {
|
|
56
|
-
try {
|
|
57
|
-
const t = await e.getOperatorList(), o = e.getViewport({ scale: 1 }), s = o.width, n = o.height, i = [], r = [];
|
|
58
|
-
let a = [1, 0, 0, 1, 0, 0];
|
|
59
|
-
const g = t.fnArray, l = t.argsArray, c = w.OPS, x = [
|
|
60
|
-
c.paintImageXObject,
|
|
61
|
-
c.paintInlineImageXObject,
|
|
62
|
-
c.paintImageMaskXObject,
|
|
63
|
-
c.paintJpegXObject
|
|
64
|
-
];
|
|
65
|
-
for (let h = 0; h < g.length; h++) {
|
|
66
|
-
const p = g[h], f = l[h];
|
|
67
|
-
if (p === c.save)
|
|
68
|
-
r.push([...a]);
|
|
69
|
-
else if (p === c.restore)
|
|
70
|
-
r.length > 0 && (a = r.pop());
|
|
71
|
-
else if (p === c.transform) {
|
|
72
|
-
const [u, m, b, M, k, y] = f;
|
|
73
|
-
a = I(a, [u, m, b, M, k, y]);
|
|
74
|
-
} else if (x.includes(p)) {
|
|
75
|
-
const u = j(a, s, n);
|
|
76
|
-
i.push({
|
|
77
|
-
type: "image",
|
|
78
|
-
bbox_norm: u,
|
|
79
|
-
source: "pdfjs",
|
|
80
|
-
operator: O(p, c)
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return i;
|
|
85
|
-
} catch (t) {
|
|
86
|
-
return console.error("Error extracting image blocks:", t), [];
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
async function B(e, t) {
|
|
90
|
-
try {
|
|
91
|
-
const s = await w.getDocument(e).promise;
|
|
92
|
-
if (t < 1 || t > s.numPages)
|
|
93
|
-
throw new Error(`Invalid page number: ${t}. Document has ${s.numPages} pages.`);
|
|
94
|
-
const n = await s.getPage(t), i = n.getViewport({ scale: 1 }), [r, a] = await Promise.all([
|
|
95
|
-
d(n),
|
|
96
|
-
X(n)
|
|
97
|
-
]);
|
|
1
|
+
import { jsxs as Q, jsx as x } from "react/jsx-runtime";
|
|
2
|
+
import { useRef as d, useState as q, useCallback as z, useMemo as H, useEffect as s } from "react";
|
|
3
|
+
import { AnnotationRenderer as M } from "./index2.js";
|
|
4
|
+
import { getPreset as N } from "./index17.js";
|
|
5
|
+
function J({
|
|
6
|
+
// Required props
|
|
7
|
+
pdfUrl: v,
|
|
8
|
+
// Optional props with defaults
|
|
9
|
+
page: u = 1,
|
|
10
|
+
scale: f = 1.5,
|
|
11
|
+
annotations: g = [],
|
|
12
|
+
currentTime: h = 0,
|
|
13
|
+
// Continuous sync mode (use instead of currentTime for smoother animation)
|
|
14
|
+
audioRef: l,
|
|
15
|
+
// Stroke configuration
|
|
16
|
+
strokeConfig: a,
|
|
17
|
+
// Callbacks
|
|
18
|
+
onLoad: w,
|
|
19
|
+
onError: t,
|
|
20
|
+
onPageChange: A,
|
|
21
|
+
// Styling
|
|
22
|
+
className: b,
|
|
23
|
+
style: k,
|
|
24
|
+
canvasStyle: T
|
|
25
|
+
}) {
|
|
26
|
+
const m = d(null), P = d(null), r = d(null), F = d(Promise.resolve()), [i, S] = q(!1), c = z((e) => {
|
|
27
|
+
F.current = F.current.then(e).catch((n) => {
|
|
28
|
+
console.error("AnnotPdf: Queued operation failed:", n);
|
|
29
|
+
});
|
|
30
|
+
}, []), y = H(() => {
|
|
31
|
+
if (!a)
|
|
32
|
+
return;
|
|
33
|
+
const e = a.preset || "default", n = N(e);
|
|
98
34
|
return {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
width: i.width,
|
|
103
|
-
height: i.height
|
|
35
|
+
highlight: {
|
|
36
|
+
color: n.highlight.color,
|
|
37
|
+
width: a.highlight?.width ?? n.highlight.width
|
|
104
38
|
},
|
|
105
|
-
|
|
39
|
+
text: {
|
|
40
|
+
color: n.handText.color,
|
|
41
|
+
width: a.text?.width ?? n.handText.width
|
|
42
|
+
}
|
|
106
43
|
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
pageNumber: s,
|
|
123
|
-
error: n.message
|
|
124
|
-
});
|
|
44
|
+
}, [a]);
|
|
45
|
+
s(() => {
|
|
46
|
+
if (!(!m.current || !P.current)) {
|
|
47
|
+
try {
|
|
48
|
+
r.current = new M({
|
|
49
|
+
canvasElement: m.current,
|
|
50
|
+
container: P.current,
|
|
51
|
+
strokeConfig: y
|
|
52
|
+
}), S(!0);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
console.error("AnnotPdf: Failed to initialize renderer:", e), t && t(e);
|
|
55
|
+
}
|
|
56
|
+
return () => {
|
|
57
|
+
S(!1), r.current && (r.current.destroy(), r.current = null);
|
|
58
|
+
};
|
|
125
59
|
}
|
|
126
|
-
|
|
60
|
+
}, []), s(() => {
|
|
61
|
+
if (!i || !v)
|
|
62
|
+
return;
|
|
63
|
+
let e = !1;
|
|
64
|
+
return c(async () => {
|
|
65
|
+
if (r.current)
|
|
66
|
+
try {
|
|
67
|
+
const o = await r.current.loadPDF(v);
|
|
68
|
+
if (e) return;
|
|
69
|
+
if (!o.success) {
|
|
70
|
+
console.error("AnnotPdf: Failed to load PDF:", o.error), t && t(new Error(o.error));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
w && w({ pageCount: o.pageCount });
|
|
74
|
+
} catch (o) {
|
|
75
|
+
if (e) return;
|
|
76
|
+
console.error("AnnotPdf: Failed to load PDF:", o), t && t(o);
|
|
77
|
+
}
|
|
78
|
+
}), () => {
|
|
79
|
+
e = !0;
|
|
80
|
+
};
|
|
81
|
+
}, [v, i, c]), s(() => {
|
|
82
|
+
!i || !u || typeof u != "number" || c(async () => {
|
|
83
|
+
if (r.current)
|
|
84
|
+
try {
|
|
85
|
+
const e = await r.current.setPage(u);
|
|
86
|
+
if (!e.success) {
|
|
87
|
+
console.error("AnnotPdf: Failed to set page:", e.error), t && t(new Error(e.error));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
A && A(u);
|
|
91
|
+
} catch (e) {
|
|
92
|
+
console.error("AnnotPdf: Failed to set page:", e), t && t(e);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}, [u, i, c]), s(() => {
|
|
96
|
+
!i || !f || typeof f != "number" || c(async () => {
|
|
97
|
+
if (r.current)
|
|
98
|
+
try {
|
|
99
|
+
const e = await r.current.setScale(f);
|
|
100
|
+
e.success || (console.error("AnnotPdf: Failed to set scale:", e.error), t && t(new Error(e.error)));
|
|
101
|
+
} catch (e) {
|
|
102
|
+
console.error("AnnotPdf: Failed to set scale:", e), t && t(e);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}, [f, i, c]), s(() => {
|
|
106
|
+
if (!(!i || !r.current))
|
|
107
|
+
try {
|
|
108
|
+
r.current.setAnnotations(g || []);
|
|
109
|
+
} catch (e) {
|
|
110
|
+
console.error("AnnotPdf: Failed to set annotations:", e), t && t(e);
|
|
111
|
+
}
|
|
112
|
+
}, [g, i]), s(() => {
|
|
113
|
+
if (!l && !(!i || !r.current || h === void 0 || h === null))
|
|
114
|
+
try {
|
|
115
|
+
r.current.setTime(h);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
console.error("AnnotPdf: Failed to set time:", e), t && t(e);
|
|
118
|
+
}
|
|
119
|
+
}, [h, l, i]), s(() => {
|
|
120
|
+
if (!l?.current || !i || !r.current)
|
|
121
|
+
return;
|
|
122
|
+
const e = l.current, n = r.current, o = () => {
|
|
123
|
+
n.startContinuousSync(() => e.currentTime);
|
|
124
|
+
}, p = () => {
|
|
125
|
+
n.stopContinuousSync(), n.setTime(e.currentTime);
|
|
126
|
+
}, L = () => {
|
|
127
|
+
n.setTime(e.currentTime);
|
|
128
|
+
};
|
|
129
|
+
return e.addEventListener("play", o), e.addEventListener("pause", p), e.addEventListener("ended", p), e.addEventListener("seeked", L), e.paused || o(), () => {
|
|
130
|
+
e.removeEventListener("play", o), e.removeEventListener("pause", p), e.removeEventListener("ended", p), e.removeEventListener("seeked", L), n.stopContinuousSync();
|
|
131
|
+
};
|
|
132
|
+
}, [l, i]);
|
|
133
|
+
const C = d(!0);
|
|
134
|
+
s(() => {
|
|
135
|
+
if (C.current) {
|
|
136
|
+
C.current = !1;
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (r.current && y)
|
|
140
|
+
try {
|
|
141
|
+
r.current.updateStrokeConfig(y);
|
|
142
|
+
} catch (e) {
|
|
143
|
+
console.error("AnnotPdf: Failed to update stroke config:", e), t && t(e);
|
|
144
|
+
}
|
|
145
|
+
}, [y]);
|
|
146
|
+
const R = {
|
|
147
|
+
position: "relative",
|
|
148
|
+
display: "inline-block",
|
|
149
|
+
lineHeight: 0,
|
|
150
|
+
// Remove extra space below canvas
|
|
151
|
+
...k
|
|
152
|
+
// User styles override defaults
|
|
153
|
+
}, D = {
|
|
154
|
+
position: "absolute",
|
|
155
|
+
top: 0,
|
|
156
|
+
left: 0,
|
|
157
|
+
width: "100%",
|
|
158
|
+
height: "100%",
|
|
159
|
+
pointerEvents: "none",
|
|
160
|
+
// Allow clicks to pass through to canvas
|
|
161
|
+
overflow: "hidden"
|
|
162
|
+
}, j = {
|
|
163
|
+
display: "block",
|
|
164
|
+
...T
|
|
165
|
+
// User styles override defaults
|
|
166
|
+
};
|
|
167
|
+
return /* @__PURE__ */ Q("div", { className: b, style: R, children: [
|
|
168
|
+
/* @__PURE__ */ x("canvas", { ref: m, style: j }),
|
|
169
|
+
/* @__PURE__ */ x("div", { ref: P, style: D })
|
|
170
|
+
] });
|
|
127
171
|
}
|
|
128
172
|
export {
|
|
129
|
-
|
|
130
|
-
T as extractMultiplePages
|
|
173
|
+
J as default
|
|
131
174
|
};
|
|
132
175
|
//# sourceMappingURL=index12.js.map
|
package/dist/index12.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index12.js","sources":["../src/utils/pdfExtractor.js"],"sourcesContent":["/**\n * PDF Data Extractor - Extract text and image blocks from PDF\n *\n * Uses PDF.js getOperatorList() for efficient metadata extraction without rendering.\n *\n * @module utils/pdfExtractor\n */\n\nimport * as pdfjsLib from \"pdfjs-dist\";\n\n/**\n * Extract text blocks from a PDF page with normalized coordinates\n *\n * @param {PDFPageProxy} page - PDF.js page object\n * @returns {Promise<Array>} Array of text blocks with normalized bounding boxes (0-1 range)\n */\nasync function extractTextBlocks(page) {\n try {\n const textContent = await page.getTextContent();\n const viewport = page.getViewport({ scale: 1.0 });\n\n const pageWidth = viewport.width;\n const pageHeight = viewport.height;\n\n const textBlocks = [];\n\n for (const item of textContent.items) {\n // Skip empty text\n if (!item.str || !item.str.trim()) {\n continue;\n }\n\n // Get transform matrix [a, b, c, d, e, f]\n const transform = item.transform;\n const fontSize = Math.sqrt(transform[2] * transform[2] + transform[3] * transform[3]);\n\n // Calculate bounding box (absolute coordinates)\n const x = transform[4];\n const y = viewport.height - transform[5]; // Convert to top-left origin\n const width = item.width;\n const height = item.height || fontSize;\n\n const x1 = x;\n const y1 = y - height;\n const x2 = x + width;\n const y2 = y;\n\n // Normalize coordinates to 0-1 range\n const bbox_norm = [\n Math.max(0.0, Math.min(1.0, x1 / pageWidth)),\n Math.max(0.0, Math.min(1.0, y1 / pageHeight)),\n Math.max(0.0, Math.min(1.0, x2 / pageWidth)),\n Math.max(0.0, Math.min(1.0, y2 / pageHeight))\n ];\n\n textBlocks.push({\n type: \"text\",\n text: item.str,\n bbox_norm: bbox_norm,\n font: item.fontName || \"unknown\",\n fontSize: fontSize,\n source: \"pdfjs\"\n });\n }\n\n return textBlocks;\n } catch (error) {\n console.error(\"Error extracting text blocks:\", error);\n return [];\n }\n}\n\n/**\n * Extract normalized bounding box from PDF transformation matrix\n *\n * @param {Array} transform - PDF transformation matrix [a, b, c, d, e, f]\n * @param {number} pageWidth - Page width for normalization\n * @param {number} pageHeight - Page height for coordinate conversion and normalization\n * @returns {Array} Normalized bounding box [x1, y1, x2, y2] in 0-1 range\n */\nfunction extractBBoxFromTransform(transform, pageWidth, pageHeight) {\n const x = transform[4];\n const y = transform[5];\n const scaleX = transform[0];\n const scaleY = transform[3];\n\n // Scale factors represent image dimensions\n const width = Math.abs(scaleX);\n const height = Math.abs(scaleY);\n\n // Convert from PDF bottom-left to image top-left origin\n const y1 = pageHeight - y - height;\n const y2 = pageHeight - y;\n const x1 = x;\n const x2 = x + width;\n\n // Normalize to 0-1 range\n return [\n Math.max(0.0, Math.min(1.0, x1 / pageWidth)),\n Math.max(0.0, Math.min(1.0, y1 / pageHeight)),\n Math.max(0.0, Math.min(1.0, x2 / pageWidth)),\n Math.max(0.0, Math.min(1.0, y2 / pageHeight))\n ];\n}\n\n/**\n * Multiply two transformation matrices\n *\n * @param {Array} m1 - First matrix [a, b, c, d, e, f]\n * @param {Array} m2 - Second matrix [a, b, c, d, e, f]\n * @returns {Array} Result matrix [a, b, c, d, e, f]\n */\nfunction multiplyTransforms(m1, m2) {\n return [\n m1[0] * m2[0] + m1[2] * m2[1],\n m1[1] * m2[0] + m1[3] * m2[1],\n m1[0] * m2[2] + m1[2] * m2[3],\n m1[1] * m2[2] + m1[3] * m2[3],\n m1[0] * m2[4] + m1[2] * m2[5] + m1[4],\n m1[1] * m2[4] + m1[3] * m2[5] + m1[5]\n ];\n}\n\n/**\n * Get human-readable operator name\n *\n * @param {number} op - Operator code\n * @param {Object} OPS - PDF.js OPS constants\n * @returns {string} Operator name\n */\nfunction getOperatorName(op, OPS) {\n const opNames = {\n [OPS.paintImageXObject]: \"paintImageXObject\",\n [OPS.paintInlineImageXObject]: \"paintInlineImageXObject\",\n [OPS.paintImageMaskXObject]: \"paintImageMaskXObject\",\n [OPS.paintJpegXObject]: \"paintJpegXObject\"\n };\n return opNames[op] || \"unknown\";\n}\n\n/**\n * Extract image blocks from a PDF page using getOperatorList with normalized coordinates\n *\n * @param {PDFPageProxy} page - PDF.js page object\n * @returns {Promise<Array>} Array of image blocks with normalized bounding boxes (0-1 range)\n */\nasync function extractImageBlocks(page) {\n try {\n const operatorList = await page.getOperatorList();\n const viewport = page.getViewport({ scale: 1.0 });\n\n const pageWidth = viewport.width;\n const pageHeight = viewport.height;\n\n const imageBlocks = [];\n const graphicsStateStack = []; // Track graphics state transformations\n let currentTransform = [1, 0, 0, 1, 0, 0]; // Identity matrix\n\n const ops = operatorList.fnArray;\n const args = operatorList.argsArray;\n\n // Image paint operator constants from PDF.js\n const OPS = pdfjsLib.OPS;\n const IMAGE_OPS = [\n OPS.paintImageXObject,\n OPS.paintInlineImageXObject,\n OPS.paintImageMaskXObject,\n OPS.paintJpegXObject\n ];\n\n for (let i = 0; i < ops.length; i++) {\n const op = ops[i];\n const arg = args[i];\n\n // Track graphics state stack\n if (op === OPS.save) {\n graphicsStateStack.push([...currentTransform]);\n } else if (op === OPS.restore) {\n if (graphicsStateStack.length > 0) {\n currentTransform = graphicsStateStack.pop();\n }\n } else if (op === OPS.transform) {\n // Update current transformation matrix\n const [a, b, c, d, e, f] = arg;\n currentTransform = multiplyTransforms(currentTransform, [a, b, c, d, e, f]);\n } else if (IMAGE_OPS.includes(op)) {\n // Extract image bounding box (normalized)\n const bbox_norm = extractBBoxFromTransform(currentTransform, pageWidth, pageHeight);\n\n imageBlocks.push({\n type: \"image\",\n bbox_norm: bbox_norm,\n source: \"pdfjs\",\n operator: getOperatorName(op, OPS)\n });\n }\n }\n\n return imageBlocks;\n } catch (error) {\n console.error(\"Error extracting image blocks:\", error);\n return [];\n }\n}\n\n/**\n * Extract all blocks (text and images) from a PDF page\n *\n * @param {string} pdfPath - Path to PDF file\n * @param {number} pageNumber - Page number (1-indexed)\n * @returns {Promise<Object>} Object with textBlocks and imageBlocks arrays\n */\nexport async function extractAllBlocks(pdfPath, pageNumber) {\n try {\n // Load PDF document\n const loadingTask = pdfjsLib.getDocument(pdfPath);\n const pdfDoc = await loadingTask.promise;\n\n // Validate page number\n if (pageNumber < 1 || pageNumber > pdfDoc.numPages) {\n throw new Error(`Invalid page number: ${pageNumber}. Document has ${pdfDoc.numPages} pages.`);\n }\n\n // Get page\n const page = await pdfDoc.getPage(pageNumber);\n const viewport = page.getViewport({ scale: 1.0 });\n\n // Extract text and image blocks in parallel\n const [textBlocks, imageBlocks] = await Promise.all([\n extractTextBlocks(page),\n extractImageBlocks(page)\n ]);\n\n return {\n textBlocks,\n imageBlocks,\n pageDimensions: {\n width: viewport.width,\n height: viewport.height\n },\n pageNumber: pageNumber\n };\n } catch (error) {\n console.error(`Error extracting blocks from page ${pageNumber}:`, error);\n throw error;\n }\n}\n\n/**\n * Extract blocks from multiple pages\n *\n * @param {string} pdfPath - Path to PDF file\n * @param {Array<number>} pageNumbers - Array of page numbers (1-indexed)\n * @returns {Promise<Array>} Array of extraction results, one per page\n */\nexport async function extractMultiplePages(pdfPath, pageNumbers) {\n const results = [];\n\n for (const pageNum of pageNumbers) {\n try {\n const pageData = await extractAllBlocks(pdfPath, pageNum);\n results.push(pageData);\n } catch (error) {\n console.error(`Failed to extract page ${pageNum}:`, error);\n results.push({\n textBlocks: [],\n imageBlocks: [],\n pageDimensions: null,\n pageNumber: pageNum,\n error: error.message\n });\n }\n }\n\n return results;\n}\n"],"names":["extractTextBlocks","page","textContent","viewport","pageWidth","pageHeight","textBlocks","item","transform","fontSize","x","y","width","height","x1","y1","x2","y2","bbox_norm","error","extractBBoxFromTransform","scaleX","scaleY","multiplyTransforms","m1","m2","getOperatorName","op","OPS","extractImageBlocks","operatorList","imageBlocks","graphicsStateStack","currentTransform","ops","args","pdfjsLib","IMAGE_OPS","i","arg","a","b","c","d","e","f","extractAllBlocks","pdfPath","pageNumber","pdfDoc","extractMultiplePages","pageNumbers","results","pageNum","pageData"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index12.js","sources":["../src/adapters/AnnotPdf.jsx"],"sourcesContent":["// ============================================================================\n// SECTION 1: IMPORTS\n// ============================================================================\n\nimport { useRef, useEffect, useCallback, useMemo, useState } from 'react';\nimport { AnnotationRenderer } from '../core/AnnotationRenderer.js';\nimport { getPreset } from '../pen/presets.js';\n\n// ============================================================================\n// SECTION 2: JSDOC DOCUMENTATION\n// ============================================================================\n\n/**\n * AnnotPdf - Declarative React component for PDF annotation rendering\n *\n * A React wrapper around the AnnotationRenderer core engine that provides\n * a declarative, props-based API for rendering PDF documents with\n * timeline-synchronized annotations.\n *\n * Features:\n * - Automatic lifecycle management (initialization and cleanup)\n * - Declarative prop-to-method synchronization\n * - PDF rendering with pdf.js\n * - Timeline-synchronized annotation display\n * - Support for highlight, text, and ink annotations\n * - Page navigation and zoom control\n *\n * @component\n * @example\n * // Basic usage\n * <AnnotPdf\n * pdfUrl=\"/document.pdf\"\n * page={1}\n * scale={1.5}\n * annotations={[]}\n * currentTime={0}\n * />\n *\n * @example\n * // With audio synchronization (discrete mode - ~4fps)\n * const [currentTime, setCurrentTime] = useState(0);\n *\n * <div>\n * <AnnotPdf\n * pdfUrl=\"/lecture.pdf\"\n * page={1}\n * scale={1.5}\n * annotations={annotations}\n * currentTime={currentTime}\n * onLoad={({ pageCount }) => console.log('Loaded:', pageCount)}\n * />\n * <audio\n * src=\"/lecture.mp3\"\n * onTimeUpdate={(e) => setCurrentTime(e.target.currentTime)}\n * controls\n * />\n * </div>\n *\n * @example\n * // With audio synchronization (continuous mode - 60fps, smoother)\n * const audioRef = useRef(null);\n *\n * <div>\n * <AnnotPdf\n * pdfUrl=\"/lecture.pdf\"\n * page={1}\n * scale={1.5}\n * annotations={annotations}\n * audioRef={audioRef}\n * onLoad={({ pageCount }) => console.log('Loaded:', pageCount)}\n * />\n * <audio ref={audioRef} src=\"/lecture.mp3\" controls />\n * </div>\n *\n * @param {Object} props - Component props\n * @param {string} props.pdfUrl - PDF document URL (required)\n * @param {number} [props.page=1] - Current page number (1-indexed)\n * @param {number} [props.scale=1.5] - Zoom scale factor\n * @param {Array} [props.annotations=[]] - Array of annotation objects\n * @param {number} [props.currentTime=0] - Timeline position in seconds (discrete mode)\n * @param {React.RefObject<HTMLAudioElement>} [props.audioRef] - Audio element ref for continuous sync (60fps)\n * @param {Function} [props.onLoad] - Callback when PDF loads: ({pageCount}) => void\n * @param {Function} [props.onError] - Callback on error: (error) => void\n * @param {Function} [props.onPageChange] - Callback when page changes: (page) => void\n * @param {Object} [props.strokeConfig] - Stroke rendering configuration\n * @param {string} [props.strokeConfig.preset] - Preset name: 'default', 'blue', 'minimal'\n * @param {Object} [props.strokeConfig.highlight] - Highlight style overrides\n * @param {number} [props.strokeConfig.highlight.width] - Highlight stroke width (16-32)\n * @param {Object} [props.strokeConfig.text] - Text style overrides\n * @param {number} [props.strokeConfig.text.width] - Text stroke width (1-4)\n * @param {string} [props.className] - CSS class for container div\n * @param {Object} [props.style] - Inline styles for container div\n * @param {Object} [props.canvasStyle] - Inline styles for canvas element\n * @returns {JSX.Element} PDF viewer component with annotation layers\n */\n\n// ============================================================================\n// SECTION 3: COMPONENT DEFINITION\n// ============================================================================\n\nfunction AnnotPdf({\n // Required props\n pdfUrl,\n\n // Optional props with defaults\n page = 1,\n scale = 1.5,\n annotations = [],\n currentTime = 0,\n\n // Continuous sync mode (use instead of currentTime for smoother animation)\n audioRef,\n\n // Stroke configuration\n strokeConfig,\n\n // Callbacks\n onLoad,\n onError,\n onPageChange,\n\n // Styling\n className,\n style,\n canvasStyle\n}) {\n\n // ==========================================================================\n // SECTION 4: REFS INITIALIZATION\n // ==========================================================================\n\n /**\n * Reference to the canvas element for PDF rendering\n * @type {React.RefObject<HTMLCanvasElement>}\n */\n const canvasRef = useRef(null);\n\n /**\n * Reference to the layer container div for annotation layers\n * @type {React.RefObject<HTMLDivElement>}\n */\n const layerContainerRef = useRef(null);\n\n /**\n * Reference to the AnnotationRenderer engine instance\n * Stored in ref to avoid triggering re-renders\n * @type {React.RefObject<AnnotationRenderer|null>}\n */\n const engineRef = useRef(null);\n\n /**\n * Reference to the render operation queue\n * Ensures sequential execution of async canvas operations\n * Prevents PDF.js race condition: \"Cannot use the same canvas during multiple render() operations\"\n * @type {React.RefObject<Promise<void>>}\n */\n const renderQueue = useRef(Promise.resolve());\n\n /**\n * State to track engine initialization\n * Used to trigger dependent effects after engine is ready\n */\n const [isEngineReady, setEngineReady] = useState(false);\n\n // ==========================================================================\n // SECTION 4.5: RENDER QUEUE HELPER\n // ==========================================================================\n\n /**\n * Queue a render operation to execute sequentially\n *\n * This helper ensures that async canvas operations (loadPDF, setPage, setScale)\n * execute one at a time, preventing concurrent access to the PDF.js canvas.\n * Uses Promise chaining to maintain operation order.\n *\n * @param {Function} operation - Async function returning a Promise\n * @returns {void}\n */\n const queueOperation = useCallback((operation) => {\n renderQueue.current = renderQueue.current\n .then(operation)\n .catch(error => {\n // Log errors but don't break the queue\n console.error('AnnotPdf: Queued operation failed:', error);\n });\n }, []);\n\n // ==========================================================================\n // SECTION 4.6: STROKE CONFIG BUILDER\n // ==========================================================================\n\n /**\n * Build merged stroke config from preset and overrides\n *\n * If strokeConfig.preset is provided, loads base config from presets.\n * Then merges any override values (highlight.width, text.width).\n */\n const mergedStrokeConfig = useMemo(() => {\n if (!strokeConfig) {\n return undefined;\n }\n\n // Start with preset or empty object\n const presetName = strokeConfig.preset || 'default';\n const presetConfig = getPreset(presetName);\n\n // Build config object matching StrokeRenderer format\n const config = {\n highlight: {\n color: presetConfig.highlight.color,\n width: strokeConfig.highlight?.width ?? presetConfig.highlight.width\n },\n text: {\n color: presetConfig.handText.color,\n width: strokeConfig.text?.width ?? presetConfig.handText.width\n }\n };\n\n return config;\n }, [strokeConfig]);\n\n // ==========================================================================\n // SECTION 5: ENGINE INITIALIZATION AND CLEANUP\n // ==========================================================================\n\n /**\n * Initialize AnnotationRenderer on component mount\n * Cleanup on component unmount\n */\n useEffect(() => {\n // Guard: Wait for DOM elements to be ready\n if (!canvasRef.current || !layerContainerRef.current) {\n return;\n }\n\n // Initialize engine with stroke config if provided\n try {\n engineRef.current = new AnnotationRenderer({\n canvasElement: canvasRef.current,\n container: layerContainerRef.current,\n strokeConfig: mergedStrokeConfig\n });\n // Signal that engine is ready for dependent effects\n setEngineReady(true);\n } catch (error) {\n console.error('AnnotPdf: Failed to initialize renderer:', error);\n if (onError) {\n onError(error);\n }\n }\n\n // Cleanup on unmount\n return () => {\n setEngineReady(false);\n if (engineRef.current) {\n engineRef.current.destroy();\n engineRef.current = null;\n }\n };\n }, []); // Empty deps - run once on mount\n\n // ==========================================================================\n // SECTION 6: PDF LOADING SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Load PDF document when pdfUrl prop changes\n * Handles async operation with cancellation support\n * Uses render queue to prevent concurrent canvas operations\n */\n useEffect(() => {\n // Guard: Wait for engine and valid pdfUrl\n if (!isEngineReady || !pdfUrl) {\n return;\n }\n\n let cancelled = false;\n\n const loadPdf = async () => {\n // Check engine exists at execution time (not queue time)\n if (!engineRef.current) {\n return;\n }\n\n try {\n const result = await engineRef.current.loadPDF(pdfUrl);\n\n // Check if component unmounted during async operation\n if (cancelled) return;\n\n // Check if load was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to load PDF:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n return;\n }\n\n // Call onLoad callback with pageCount from result\n if (onLoad) {\n onLoad({ pageCount: result.pageCount });\n }\n } catch (error) {\n if (cancelled) return;\n\n console.error('AnnotPdf: Failed to load PDF:', error);\n if (onError) {\n onError(error);\n }\n }\n };\n\n // Queue the PDF loading operation to prevent race conditions\n queueOperation(loadPdf);\n\n // Cleanup: Prevent state updates if component unmounts during load\n return () => {\n cancelled = true;\n };\n }, [pdfUrl, isEngineReady, queueOperation]);\n\n // ==========================================================================\n // SECTION 7: PAGE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync page prop to engine.setPage() method\n * Uses render queue to prevent concurrent canvas operations\n */\n useEffect(() => {\n // Guard: Wait for engine and valid page\n if (!isEngineReady || !page || typeof page !== 'number') {\n return;\n }\n\n // Queue the page change operation to prevent race conditions\n queueOperation(async () => {\n // Check engine exists at execution time (not queue time)\n if (!engineRef.current) {\n return;\n }\n\n try {\n const result = await engineRef.current.setPage(page);\n\n // Check if page change was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to set page:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n return;\n }\n\n // Optional: Notify parent of successful page change\n if (onPageChange) {\n onPageChange(page);\n }\n } catch (error) {\n console.error('AnnotPdf: Failed to set page:', error);\n if (onError) {\n onError(error);\n }\n }\n });\n }, [page, isEngineReady, queueOperation]);\n\n // ==========================================================================\n // SECTION 8: SCALE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync scale prop to engine.setScale() method\n * Uses render queue to prevent concurrent canvas operations\n */\n useEffect(() => {\n // Guard: Wait for engine and valid scale\n if (!isEngineReady || !scale || typeof scale !== 'number') {\n return;\n }\n\n // Queue the scale change operation to prevent race conditions\n queueOperation(async () => {\n // Check engine exists at execution time (not queue time)\n if (!engineRef.current) {\n return;\n }\n\n try {\n const result = await engineRef.current.setScale(scale);\n\n // Check if scale change was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to set scale:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n }\n } catch (error) {\n console.error('AnnotPdf: Failed to set scale:', error);\n if (onError) {\n onError(error);\n }\n }\n });\n }, [scale, isEngineReady, queueOperation]);\n\n // ==========================================================================\n // SECTION 9: ANNOTATIONS SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync annotations prop to engine.setAnnotations() method\n */\n useEffect(() => {\n // Guard: Wait for engine\n if (!isEngineReady || !engineRef.current) {\n return;\n }\n\n // Sync annotations to engine (default to empty array)\n try {\n engineRef.current.setAnnotations(annotations || []);\n } catch (error) {\n console.error('AnnotPdf: Failed to set annotations:', error);\n if (onError) {\n onError(error);\n }\n }\n }, [annotations, isEngineReady]);\n\n // ==========================================================================\n // SECTION 10: TIMELINE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync currentTime prop to engine.setTime() method\n * Only used when audioRef is NOT provided (discrete mode)\n */\n useEffect(() => {\n // Skip if using continuous sync mode\n if (audioRef) {\n return;\n }\n\n // Guard: Wait for engine and valid currentTime\n if (!isEngineReady || !engineRef.current || currentTime === undefined || currentTime === null) {\n return;\n }\n\n // Sync timeline to engine\n try {\n engineRef.current.setTime(currentTime);\n } catch (error) {\n console.error('AnnotPdf: Failed to set time:', error);\n if (onError) {\n onError(error);\n }\n }\n }, [currentTime, audioRef, isEngineReady]);\n\n // ==========================================================================\n // SECTION 10.1: CONTINUOUS SYNC MODE (audioRef)\n // ==========================================================================\n\n /**\n * Manage continuous sync mode when audioRef is provided\n *\n * Uses requestAnimationFrame for 60fps smooth animation instead of\n * discrete currentTime updates (~4fps from audio timeupdate events).\n */\n useEffect(() => {\n // Guard: Skip if not using continuous sync mode\n if (!audioRef?.current) {\n return;\n }\n\n // Guard: Wait for engine\n if (!isEngineReady || !engineRef.current) {\n return;\n }\n\n const audio = audioRef.current;\n const engine = engineRef.current;\n\n /**\n * Start continuous sync when audio plays\n */\n const handlePlay = () => {\n engine.startContinuousSync(() => audio.currentTime);\n };\n\n /**\n * Stop continuous sync when audio pauses\n */\n const handlePause = () => {\n engine.stopContinuousSync();\n // Do one final sync to ensure we're at the correct position\n engine.setTime(audio.currentTime);\n };\n\n /**\n * Handle seeking - update time immediately\n */\n const handleSeeked = () => {\n engine.setTime(audio.currentTime);\n };\n\n // Add event listeners\n audio.addEventListener('play', handlePlay);\n audio.addEventListener('pause', handlePause);\n audio.addEventListener('ended', handlePause);\n audio.addEventListener('seeked', handleSeeked);\n\n // If audio is already playing, start sync immediately\n if (!audio.paused) {\n handlePlay();\n }\n\n // Cleanup on unmount or audioRef change\n return () => {\n audio.removeEventListener('play', handlePlay);\n audio.removeEventListener('pause', handlePause);\n audio.removeEventListener('ended', handlePause);\n audio.removeEventListener('seeked', handleSeeked);\n engine.stopContinuousSync();\n };\n }, [audioRef, isEngineReady]);\n\n // ==========================================================================\n // SECTION 10.5: STROKE CONFIG SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync strokeConfig prop to engine at runtime for live preview\n * Skips initial render (handled by initialization)\n */\n const isFirstRender = useRef(true);\n\n useEffect(() => {\n // Skip first render - config is passed during initialization\n if (isFirstRender.current) {\n isFirstRender.current = false;\n return;\n }\n\n // Guard: Engine must exist\n if (!engineRef.current) {\n return;\n }\n\n // Update stroke config if provided\n if (mergedStrokeConfig) {\n try {\n engineRef.current.updateStrokeConfig(mergedStrokeConfig);\n } catch (error) {\n console.error('AnnotPdf: Failed to update stroke config:', error);\n if (onError) {\n onError(error);\n }\n }\n }\n }, [mergedStrokeConfig]);\n\n // ==========================================================================\n // SECTION 11: STYLING DEFINITIONS\n // ==========================================================================\n\n /**\n * Default container styles\n * Merged with user-provided styles (user styles override defaults)\n */\n const defaultContainerStyle = {\n position: 'relative',\n display: 'inline-block',\n lineHeight: 0, // Remove extra space below canvas\n ...style // User styles override defaults\n };\n\n /**\n * Default layer container styles\n * Positions layer div absolutely over canvas\n */\n const defaultLayerStyle = {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n pointerEvents: 'none', // Allow clicks to pass through to canvas\n overflow: 'hidden'\n };\n\n /**\n * Default canvas styles\n * Merged with user-provided canvasStyle\n */\n const defaultCanvasStyle = {\n display: 'block',\n ...canvasStyle // User styles override defaults\n };\n\n // ==========================================================================\n // SECTION 12: JSX RETURN\n // ==========================================================================\n\n return (\n <div className={className} style={defaultContainerStyle}>\n <canvas ref={canvasRef} style={defaultCanvasStyle} />\n <div ref={layerContainerRef} style={defaultLayerStyle} />\n </div>\n );\n}\n\n// ============================================================================\n// SECTION 13: EXPORT\n// ============================================================================\n\nexport default AnnotPdf;\n"],"names":["AnnotPdf","pdfUrl","page","scale","annotations","currentTime","audioRef","strokeConfig","onLoad","onError","onPageChange","className","style","canvasStyle","canvasRef","useRef","layerContainerRef","engineRef","renderQueue","isEngineReady","setEngineReady","useState","queueOperation","useCallback","operation","error","mergedStrokeConfig","useMemo","presetName","presetConfig","getPreset","useEffect","AnnotationRenderer","cancelled","result","audio","engine","handlePlay","handlePause","handleSeeked","isFirstRender","defaultContainerStyle","defaultLayerStyle","defaultCanvasStyle","jsxs","jsx"],"mappings":"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"}
|
package/dist/index13.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const p=require("./index6.cjs");function h(g,t){const{id:o,start:u,end:f,quads:r}=g;if(!r||r.length===0)return[];const s=[],q=f-u,S=r.length,i=q/S;for(let e=0;e<r.length;e++){const b=r[e],{x:a,y:C,w:$,h:j}=b,d=C+j/2;let n=[[a,d],[a+$,d]];t.jitter?.amplitude>0&&(n=p.applyJitter(n,t.jitter,`${o}-${e}`));let c=null;(t.pressure?.taperIn>0||t.pressure?.taperOut>0)&&(c=p.applyPressure(n,t.pressure));const l=u+e*i,k=l+i;s.push({id:`${o}-${e}`,points:n,start:l,end:k,color:t.color||"rgba(255, 255, 0, 0.3)",width:t.width||24,lineCap:t.lineCap||"butt",pressures:c})}return s}exports.default=h;exports.highlightToStrokes=h;
|
|
2
2
|
//# sourceMappingURL=index13.cjs.map
|