web-annotation-renderer 0.6.4 → 0.7.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/CHANGELOG.md +129 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +62 -61
- package/dist/index.js.map +1 -1
- package/dist/index10.cjs +1 -1
- package/dist/index10.cjs.map +1 -1
- package/dist/index10.js +160 -13
- 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 -50
- package/dist/index11.js.map +1 -1
- package/dist/index12.cjs +1 -1
- package/dist/index12.cjs.map +1 -1
- package/dist/index12.js +48 -155
- package/dist/index12.js.map +1 -1
- package/dist/index13.cjs +1 -1
- package/dist/index13.cjs.map +1 -1
- package/dist/index13.js +150 -34
- package/dist/index13.js.map +1 -1
- package/dist/index14.cjs +1 -1
- package/dist/index14.cjs.map +1 -1
- package/dist/index14.js +32 -65
- package/dist/index14.js.map +1 -1
- package/dist/index15.cjs +1 -1
- package/dist/index15.cjs.map +1 -1
- package/dist/index15.js +66 -33
- package/dist/index15.js.map +1 -1
- package/dist/index16.cjs +1 -1
- package/dist/index16.cjs.map +1 -1
- package/dist/index16.js +35 -77
- package/dist/index16.js.map +1 -1
- package/dist/index17.cjs +1 -1
- package/dist/index17.cjs.map +1 -1
- package/dist/index17.js +53 -28
- package/dist/index17.js.map +1 -1
- package/dist/index18.cjs +1 -1
- package/dist/index18.cjs.map +1 -1
- package/dist/index18.js +28 -22
- package/dist/index18.js.map +1 -1
- package/dist/index19.cjs +1 -1
- package/dist/index19.cjs.map +1 -1
- package/dist/index19.js +22 -117
- package/dist/index19.js.map +1 -1
- package/dist/index2.cjs +1 -1
- package/dist/index2.cjs.map +1 -1
- package/dist/index2.js +94 -98
- package/dist/index2.js.map +1 -1
- package/dist/index20.cjs +1 -1
- package/dist/index20.cjs.map +1 -1
- package/dist/index20.js +137 -100
- package/dist/index20.js.map +1 -1
- package/dist/index21.cjs +1 -1
- package/dist/index21.cjs.map +1 -1
- package/dist/index21.js +34 -76
- package/dist/index21.js.map +1 -1
- package/dist/index22.cjs +1 -1
- package/dist/index22.cjs.map +1 -1
- package/dist/index22.js +35 -139
- package/dist/index22.js.map +1 -1
- package/dist/index23.cjs +1 -1
- package/dist/index23.cjs.map +1 -1
- package/dist/index23.js +37 -37
- package/dist/index23.js.map +1 -1
- package/dist/index24.cjs +1 -1
- package/dist/index24.cjs.map +1 -1
- package/dist/index24.js +69 -37
- package/dist/index24.js.map +1 -1
- package/dist/index25.cjs +1 -1
- package/dist/index25.cjs.map +1 -1
- package/dist/index25.js +40 -38
- package/dist/index25.js.map +1 -1
- package/dist/index26.cjs +1 -1
- package/dist/index26.cjs.map +1 -1
- package/dist/index26.js +4 -39
- package/dist/index26.js.map +1 -1
- package/dist/index27.cjs +1 -1
- package/dist/index27.js +4 -4
- package/dist/index28.cjs +1 -1
- package/dist/index28.cjs.map +1 -1
- package/dist/index28.js +71 -5
- package/dist/index28.js.map +1 -1
- package/dist/index29.cjs +1 -1
- package/dist/index29.cjs.map +1 -1
- package/dist/index29.js +24 -69
- package/dist/index29.js.map +1 -1
- package/dist/index3.cjs +1 -1
- package/dist/index3.cjs.map +1 -1
- package/dist/index3.js +31 -31
- package/dist/index3.js.map +1 -1
- package/dist/index5.cjs +1 -1
- package/dist/index5.cjs.map +1 -1
- package/dist/index5.js +237 -190
- package/dist/index5.js.map +1 -1
- package/dist/index6.cjs +1 -1
- package/dist/index6.cjs.map +1 -1
- package/dist/index6.js +37 -19
- package/dist/index6.js.map +1 -1
- package/dist/index7.cjs +1 -1
- package/dist/index7.cjs.map +1 -1
- package/dist/index7.js +11 -17
- package/dist/index7.js.map +1 -1
- package/dist/index8.cjs +1 -1
- package/dist/index8.cjs.map +1 -1
- package/dist/index8.js +16 -125
- package/dist/index8.js.map +1 -1
- package/dist/index9.cjs +1 -1
- package/dist/index9.cjs.map +1 -1
- package/dist/index9.js +118 -201
- package/dist/index9.js.map +1 -1
- package/package.json +6 -3
package/dist/index10.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index10.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
|
1
|
+
{"version":3,"file":"index10.js","sources":["../src/types/validators.js"],"sourcesContent":["/**\n * Annotation Data Normalization (Schema-Driven)\n *\n * Defensive normalizers that validate raw annotation input, clamp\n * out-of-range values, fill missing fields from defaults, and collect\n * human-readable warnings. The hand-rolled per-type normalizers were\n * replaced by per-type schemas walked through a small set of field-level\n * validators. Public function surface is preserved for backward compat.\n *\n * @module types/validators\n */\n\nimport {\n BASE_DEFAULTS,\n HIGHLIGHT_DEFAULTS,\n TEXT_DEFAULTS,\n UNDERLINE_DEFAULTS,\n ARROW_DEFAULTS,\n CIRCLE_DEFAULTS,\n} from './defaults.js';\n\n// --- Field-level normalizers ------------------------------------------------\n\nconst warnInvalid = (warnings, id, fieldName, value, defaultValue) =>\n warnings.push(`[${id}]: Field \"${fieldName}\" invalid value \"${value}\", using default ${defaultValue}`);\n\n/**\n * Coerce a value into the 0–1 range. Strings are parsed; out-of-range values\n * clamp to 0/1; non-numeric values fall back to `defaultValue`. Each fixup\n * appends a warning.\n */\nexport function normalizeCoordinate(value, defaultValue, id, fieldName, warnings) {\n const num = typeof value === 'string' ? parseFloat(value) : value;\n if (typeof num !== 'number' || Number.isNaN(num)) {\n warnInvalid(warnings, id, fieldName, value, defaultValue);\n return defaultValue;\n }\n if (num < 0) {\n warnings.push(`[${id}]: Field \"${fieldName}\" value ${num} below range [0,1], clamping to 0`);\n return 0;\n }\n if (num > 1) {\n warnings.push(`[${id}]: Field \"${fieldName}\" value ${num} exceeds range [0,1], clamping to 1`);\n return 1;\n }\n return num;\n}\n\nconst HEX_RE = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;\nconst RGB_RE = /^rgba?\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*(,\\s*[\\d.]+\\s*)?\\)$/;\nconst NAMED_COLORS = new Set([\n 'red', 'blue', 'green', 'yellow', 'black', 'white', 'gray', 'grey',\n 'orange', 'purple', 'pink', 'brown', 'transparent',\n]);\n\n/** Validate a color string (hex / rgb(a) / a small named-color set). */\nexport function normalizeColor(value, defaultValue, id, warnings) {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (trimmed && (HEX_RE.test(trimmed) || RGB_RE.test(trimmed) || NAMED_COLORS.has(trimmed.toLowerCase()))) {\n return trimmed;\n }\n }\n warnings.push(`[${id}]: Invalid color format \"${value}\", using default ${defaultValue}`);\n return defaultValue;\n}\n\n/** Validate a strictly positive number (`> 0`). Strings are parsed first. */\nexport function normalizePositiveNumber(value, defaultValue, id, fieldName, warnings) {\n const num = typeof value === 'string' ? parseFloat(value) : value;\n if (typeof num !== 'number' || Number.isNaN(num) || num <= 0) {\n warnInvalid(warnings, id, fieldName, value, defaultValue);\n return defaultValue;\n }\n return num;\n}\n\n// --- Schema walker ----------------------------------------------------------\n\n/**\n * @typedef {Object} FieldSpec\n * @property {'coord'|'color'|'content'|'mode'|'quads'} kind\n * @property {*} [default] invalid/missing → this value\n * @property {string} [expectedMode] kind='mode': the only accepted literal\n * @property {Object} [quadDefault] kind='quads': per-quad fallback\n * @property {Array} [defaultQuads] kind='quads': fallback when array missing\n *\n * @typedef {Object} TypeSchema\n * @property {Object<string, FieldSpec>} fields top-level fields keyed by name\n * @property {Object<string, FieldSpec>} [style] optional style.* fields\n * @property {boolean} [styleRequired] warn when raw.style missing\n */\n\n/** Per-quad fallback shared by highlight & underline schemas. */\nconst DEFAULT_QUAD = { x: 0.1, y: 0.1, w: 0.8, h: 0.05 };\n\n/** Normalize one quad object via `normalizeCoordinate` per field. */\nfunction normalizeQuad(quad, id, warnings, fallback) {\n if (!quad || typeof quad !== 'object') {\n warnings.push(`[${id}]: Invalid quad object, using default`);\n return { ...fallback };\n }\n return {\n x: normalizeCoordinate(quad.x, fallback.x, id, 'quad.x', warnings),\n y: normalizeCoordinate(quad.y, fallback.y, id, 'quad.y', warnings),\n w: normalizeCoordinate(quad.w, fallback.w, id, 'quad.w', warnings),\n h: normalizeCoordinate(quad.h, fallback.h, id, 'quad.h', warnings),\n };\n}\n\n/** Normalize a single field according to its schema spec. */\nfunction applyFieldSpec(name, spec, value, id, warnings) {\n switch (spec.kind) {\n case 'coord':\n return normalizeCoordinate(value, spec.default, id, name, warnings);\n case 'color':\n return normalizeColor(value, spec.default, id, warnings);\n case 'content':\n if (typeof value === 'string' && value.trim().length > 0) return value;\n warnings.push(`[${id}]: Field \"${name}\" missing or empty, using default \"${spec.default}\"`);\n return spec.default;\n case 'mode':\n if (value === spec.expectedMode) return value;\n warnings.push(`[${id}]: Field \"${name}\" invalid value \"${value}\", using default \"${spec.expectedMode}\"`);\n return spec.expectedMode;\n case 'quads': {\n if (!Array.isArray(value) || value.length === 0) {\n warnings.push(`[${id}]: Field \"${name}\" missing or empty, using default`);\n return spec.defaultQuads;\n }\n const fallback = spec.quadDefault || DEFAULT_QUAD;\n return value.map((q) => normalizeQuad(q, id, warnings, fallback));\n }\n default:\n return value;\n }\n}\n\n/** Apply a `TypeSchema` to a raw annotation, producing the normalized output. */\nfunction applySchema(schema, base, raw, warnings) {\n const out = { ...base };\n for (const [name, spec] of Object.entries(schema.fields)) {\n out[name] = applyFieldSpec(name, spec, raw[name], base.id, warnings);\n }\n if (schema.style) {\n const hasStyle = raw.style && typeof raw.style === 'object';\n if (!hasStyle && schema.styleRequired) {\n warnings.push(`[${base.id}]: Field \"style\" missing or invalid, using default`);\n }\n const style = {};\n for (const [name, spec] of Object.entries(schema.style)) {\n const value = hasStyle ? raw.style[name] : undefined;\n style[name] = applyFieldSpec(`style.${name}`, spec, value, base.id, warnings);\n }\n out.style = style;\n }\n return out;\n}\n\n// --- Per-type schemas (single source of truth for field validation) --------\n\nconst coord = (def) => ({ kind: 'coord', default: def });\nconst color = (def) => ({ kind: 'color', default: def });\nconst coordsFromDefaults = (defaults, names) =>\n Object.fromEntries(names.map((n) => [n, coord(defaults[n])]));\n\n/** @type {Object<string, TypeSchema>} */\nconst SCHEMAS = {\n highlight: {\n fields: {\n mode: { kind: 'mode', expectedMode: HIGHLIGHT_DEFAULTS.mode },\n quads: { kind: 'quads', defaultQuads: HIGHLIGHT_DEFAULTS.quads, quadDefault: DEFAULT_QUAD },\n },\n style: { color: color(HIGHLIGHT_DEFAULTS.style.color) },\n styleRequired: true,\n },\n text: {\n fields: {\n content: { kind: 'content', default: TEXT_DEFAULTS.content },\n ...coordsFromDefaults(TEXT_DEFAULTS, ['x', 'y', 'w', 'h']),\n },\n style: {\n bg: color(TEXT_DEFAULTS.style.bg),\n color: color(TEXT_DEFAULTS.style.color),\n },\n styleRequired: true,\n },\n underline: {\n fields: {\n quads: { kind: 'quads', defaultQuads: UNDERLINE_DEFAULTS.quads, quadDefault: DEFAULT_QUAD },\n },\n style: { color: color(UNDERLINE_DEFAULTS.style.color) },\n },\n arrow: {\n fields: coordsFromDefaults(ARROW_DEFAULTS, ['from_x', 'from_y', 'to_x', 'to_y']),\n style: { color: color(ARROW_DEFAULTS.style.color) },\n },\n circle: {\n fields: coordsFromDefaults(CIRCLE_DEFAULTS, ['cx', 'cy', 'rx', 'ry']),\n style: { color: color(CIRCLE_DEFAULTS.style.color) },\n },\n};\n\n// --- Base-field normalization -----------------------------------------------\n\n/**\n * Normalize fields common to every annotation: `id`, `type`, `page`, `start`,\n * `end`. Auto-generates `id` when missing (logged to `info`).\n */\nexport function normalizeBaseFields(raw, warnings, info) {\n const base = { type: raw.type };\n\n if (typeof raw.id === 'string' && raw.id.trim().length > 0) {\n base.id = raw.id.trim();\n } else {\n base.id = `anno-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;\n info.push(`[${base.id}]: Auto-generated ID (original was missing or invalid)`);\n }\n\n if (typeof raw.page === 'number' && raw.page >= 1) {\n base.page = Math.floor(raw.page);\n } else {\n warnInvalid(warnings, base.id, 'page', raw.page, BASE_DEFAULTS.page);\n base.page = BASE_DEFAULTS.page;\n }\n\n if (typeof raw.start === 'number' && raw.start >= 0) {\n base.start = raw.start;\n } else {\n warnInvalid(warnings, base.id, 'start', raw.start, BASE_DEFAULTS.start);\n base.start = BASE_DEFAULTS.start;\n }\n\n if (typeof raw.end !== 'number' || raw.end < 0) {\n warnings.push(`[${base.id}]: Field \"end\" invalid value \"${raw.end}\", using start value ${base.start}`);\n base.end = base.start;\n } else if (raw.end < base.start) {\n warnings.push(`[${base.id}]: Field \"end\" (${raw.end}) < start (${base.start}), clamping to start`);\n base.end = base.start;\n } else {\n base.end = raw.end;\n }\n\n return base;\n}\n\n// Per-type wrappers — kept for API parity. `info` is accepted for the legacy\n// v0.6.x signature but unused; only `normalizeBaseFields` writes to it.\nconst wrap = (key) => (base, raw, warnings) => applySchema(SCHEMAS[key], base, raw, warnings);\nexport const normalizeHighlight = wrap('highlight');\nexport const normalizeText = wrap('text');\nexport const normalizeUnderline = wrap('underline');\nexport const normalizeArrow = wrap('arrow');\nexport const normalizeCircle = wrap('circle');\n\n// --- Single-annotation and array entry points ------------------------------\n\n/**\n * Normalize a single raw annotation, dispatching on `type`. Returns\n * `{annotation, warnings, info, critical}`; `critical` is non-null when the\n * input is unrecoverable (missing/unknown `type`, not an object).\n */\nexport function normalizeAnnotation(raw, index) {\n const warnings = [];\n const info = [];\n const fail = (reason) => ({ annotation: null, warnings, info, critical: reason });\n\n if (!raw || typeof raw !== 'object') return fail(`Annotation at index ${index}: Not a valid object`);\n if (typeof raw.type !== 'string' || raw.type.trim().length === 0) {\n return fail(`Annotation at index ${index}: Missing or invalid type field`);\n }\n\n const type = raw.type.trim();\n const base = normalizeBaseFields(raw, warnings, info);\n let annotation;\n\n if (SCHEMAS[type]) {\n annotation = applySchema(SCHEMAS[type], base, raw, warnings);\n } else if (type === 'ink') {\n // Ink carries pre-built strokes from upstream; pass through unchanged.\n annotation = { ...base, strokes: raw.strokes || [] };\n if (raw.style) annotation.style = raw.style;\n } else {\n return fail(`Annotation at index ${index}: Unsupported type \"${type}\"`);\n }\n\n return { annotation, warnings, info, critical: null };\n}\n\n/**\n * Main entry point. Normalizes each raw annotation and returns a\n * `ValidationResult` shape — `{normalized, warnings, info, skipped}`.\n *\n * @param {Array} rawAnnotations\n * @param {{warnInConsole?: boolean, onWarning?: Function}} [options]\n */\nexport function normalizeAnnotationArray(rawAnnotations, options = {}) {\n const warnInConsole = options.warnInConsole !== false;\n const onWarning = typeof options.onWarning === 'function' ? options.onWarning : null;\n\n const result = { normalized: [], warnings: [], info: [], skipped: [] };\n\n if (!Array.isArray(rawAnnotations)) {\n const msg = 'normalizeAnnotationArray: Input is not an array, returning empty result';\n result.warnings.push(msg);\n if (warnInConsole) console.warn(`[Annotation Normalizer] ${msg}`);\n return result;\n }\n\n rawAnnotations.forEach((raw, index) => {\n if (raw == null) {\n result.skipped.push({ index, annotation: raw, reason: 'Annotation is null or undefined' });\n return;\n }\n\n const { annotation, warnings, info, critical } = normalizeAnnotation(raw, index);\n\n if (critical) {\n result.skipped.push({ index, annotation: raw, reason: critical });\n if (warnInConsole) console.error(`[Annotation Normalizer] ${critical}`);\n return;\n }\n\n result.normalized.push(annotation);\n result.warnings.push(...warnings);\n result.info.push(...info);\n });\n\n if (warnInConsole && (result.warnings.length || result.info.length || result.skipped.length)) {\n console.group('[Annotation Normalizer] Validation Summary');\n if (result.normalized.length) console.info(`✓ Normalized ${result.normalized.length} annotation(s)`);\n if (result.skipped.length) {\n console.error(`✗ Skipped ${result.skipped.length} annotation(s)`);\n result.skipped.forEach((s) => console.error(` Index ${s.index}: ${s.reason}`));\n }\n if (result.warnings.length) {\n console.warn(`⚠ ${result.warnings.length} warning(s):`);\n result.warnings.forEach((w) => console.warn(` ${w}`));\n }\n if (result.info.length) {\n console.info(`ℹ ${result.info.length} info message(s):`);\n result.info.forEach((i) => console.info(` ${i}`));\n }\n console.groupEnd();\n }\n\n if (onWarning) onWarning(result);\n return result;\n}\n"],"names":["warnInvalid","warnings","id","fieldName","value","defaultValue","normalizeCoordinate","num","HEX_RE","RGB_RE","NAMED_COLORS","normalizeColor","trimmed","normalizePositiveNumber","DEFAULT_QUAD","normalizeQuad","quad","fallback","applyFieldSpec","name","spec","q","applySchema","schema","base","raw","out","hasStyle","style","coord","def","color","coordsFromDefaults","defaults","names","n","SCHEMAS","HIGHLIGHT_DEFAULTS","TEXT_DEFAULTS","UNDERLINE_DEFAULTS","ARROW_DEFAULTS","CIRCLE_DEFAULTS","normalizeBaseFields","info","BASE_DEFAULTS","wrap","key","normalizeHighlight","normalizeText","normalizeAnnotation","index","fail","reason","type","annotation","normalizeAnnotationArray","rawAnnotations","options","warnInConsole","onWarning","result","msg","critical","s","w","i"],"mappings":"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"}
|
package/dist/index11.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./index10.cjs"),e=require("./index25.cjs");exports.normalizeAnnotation=o.normalizeAnnotation;exports.normalizeAnnotationArray=o.normalizeAnnotationArray;exports.normalizeBaseFields=o.normalizeBaseFields;exports.normalizeColor=o.normalizeColor;exports.normalizeCoordinate=o.normalizeCoordinate;exports.normalizeHighlight=o.normalizeHighlight;exports.normalizePositiveNumber=o.normalizePositiveNumber;exports.normalizeText=o.normalizeText;exports.BASE_DEFAULTS=e.BASE_DEFAULTS;exports.HIGHLIGHT_DEFAULTS=e.HIGHLIGHT_DEFAULTS;exports.TEXT_DEFAULTS=e.TEXT_DEFAULTS;
|
|
2
2
|
//# sourceMappingURL=index11.cjs.map
|
package/dist/index11.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index11.cjs","sources":[
|
|
1
|
+
{"version":3,"file":"index11.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
package/dist/index11.js
CHANGED
|
@@ -1,53 +1,16 @@
|
|
|
1
|
-
import a from "./
|
|
2
|
-
import
|
|
3
|
-
let r = null, l = null;
|
|
4
|
-
function u(t) {
|
|
5
|
-
const n = i.syllables?.[t];
|
|
6
|
-
return !n || !n.paths ? null : {
|
|
7
|
-
paths: n.paths,
|
|
8
|
-
width: 1,
|
|
9
|
-
pathTiming: "sequential"
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
function s(t) {
|
|
13
|
-
return {
|
|
14
|
-
paths: t.map((n) => ({
|
|
15
|
-
points: n
|
|
16
|
-
})),
|
|
17
|
-
width: 1,
|
|
18
|
-
pathTiming: "sequential"
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
function f(t) {
|
|
22
|
-
if (!r)
|
|
23
|
-
return null;
|
|
24
|
-
const n = `U+${t.toString(16).toUpperCase().padStart(4, "0")}`, e = r[n];
|
|
25
|
-
return e ? s(e) : null;
|
|
26
|
-
}
|
|
27
|
-
let o = "/cjk.json";
|
|
28
|
-
function d(t) {
|
|
29
|
-
o = t;
|
|
30
|
-
}
|
|
31
|
-
async function c(t) {
|
|
32
|
-
return r ? void 0 : l || (l = fetch(t || o).then((e) => {
|
|
33
|
-
if (!e.ok) throw new Error(`HTTP ${e.status}`);
|
|
34
|
-
return e.json();
|
|
35
|
-
}).then((e) => {
|
|
36
|
-
r = e;
|
|
37
|
-
}).catch((e) => {
|
|
38
|
-
console.warn("Failed to load CJK font data:", e), l = null;
|
|
39
|
-
}), l);
|
|
40
|
-
}
|
|
41
|
-
function y(t) {
|
|
42
|
-
if (!t || t.length === 0)
|
|
43
|
-
return null;
|
|
44
|
-
const n = t.charCodeAt(0);
|
|
45
|
-
return t === " " ? { paths: [], width: 0.35 } : n >= 0 && n <= 127 ? a.glyphs?.[t] || null : n >= 44032 && n <= 55203 ? u(t) : n >= 19968 && n <= 40959 ? (!r && !l && c(), f(n)) : null;
|
|
46
|
-
}
|
|
1
|
+
import { normalizeAnnotation as e, normalizeAnnotationArray as i, normalizeBaseFields as n, normalizeColor as a, normalizeCoordinate as l, normalizeHighlight as m, normalizePositiveNumber as t, normalizeText as z } from "./index10.js";
|
|
2
|
+
import { BASE_DEFAULTS as T, HIGHLIGHT_DEFAULTS as E, TEXT_DEFAULTS as F } from "./index25.js";
|
|
47
3
|
export {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
4
|
+
T as BASE_DEFAULTS,
|
|
5
|
+
E as HIGHLIGHT_DEFAULTS,
|
|
6
|
+
F as TEXT_DEFAULTS,
|
|
7
|
+
e as normalizeAnnotation,
|
|
8
|
+
i as normalizeAnnotationArray,
|
|
9
|
+
n as normalizeBaseFields,
|
|
10
|
+
a as normalizeColor,
|
|
11
|
+
l as normalizeCoordinate,
|
|
12
|
+
m as normalizeHighlight,
|
|
13
|
+
t as normalizePositiveNumber,
|
|
14
|
+
z as normalizeText
|
|
52
15
|
};
|
|
53
16
|
//# sourceMappingURL=index11.js.map
|
package/dist/index11.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index11.js","sources":[
|
|
1
|
+
{"version":3,"file":"index11.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
package/dist/index12.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const i=require("./index26.cjs"),s=require("./index27.cjs");let r=null,l=null;function f(t){const e=s.default.syllables?.[t];return!e||!e.paths?null:{paths:e.paths,width:1,pathTiming:"sequential"}}function c(t){return{paths:t.map(e=>({points:e})),width:1,pathTiming:"sequential"}}function p(t){if(!r)return null;const e=`U+${t.toString(16).toUpperCase().padStart(4,"0")}`,n=r[e];return n?c(n):null}let o="/cjk.json";function d(t){o=t}async function u(t){return r?void 0:l||(l=fetch(t||o).then(n=>{if(!n.ok)throw new Error(`HTTP ${n.status}`);return n.json()}).then(n=>{r=n}).catch(n=>{console.warn("Failed to load CJK font data:",n),l=null}),l)}function a(t){if(!t||t.length===0)return null;const e=t.charCodeAt(0);return t===" "?{paths:[],width:.35}:e>=0&&e<=127?i.default.glyphs?.[t]||null:e>=44032&&e<=55203?f(t):e>=19968&&e<=40959?(!r&&!l&&u(),p(e)):null}exports.default=a;exports.preloadCJK=u;exports.resolveGlyph=a;exports.setCJKDataUrl=d;
|
|
2
2
|
//# sourceMappingURL=index12.cjs.map
|
package/dist/index12.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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';\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 (setConfig format)\n * @param {number} [props.strokeConfig.roughness] - Global roughness for all types\n * @param {number} [props.strokeConfig.bowing] - Global bowing for all types\n * @param {Object} [props.strokeConfig.highlight] - Highlight-specific options\n * @param {Object} [props.strokeConfig.text] - Text-specific options\n * @param {Object} [props.strokeConfig.underline] - Underline-specific options\n * @param {Object} [props.strokeConfig.arrow] - Arrow-specific options\n * @param {Object} [props.strokeConfig.circle] - Circle-specific options\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 PASS-THROUGH\n // ==========================================================================\n\n /**\n * Pass strokeConfig directly to engine without transformation\n *\n * The adapter is a pure pass-through - users construct config themselves\n * using presets (import { getPreset } from 'lib') if desired.\n * setConfig in StrokeRenderer handles merging with DEFAULT_CONFIG.\n */\n const mergedStrokeConfig = useMemo(() => strokeConfig, [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 /**\n * Start continuous sync when audio plays\n */\n const handlePlay = () => {\n if (!engineRef.current) return;\n engineRef.current.startContinuousSync(() => audio.currentTime);\n };\n\n /**\n * Stop continuous sync when audio pauses\n */\n const handlePause = () => {\n if (!engineRef.current) return;\n engineRef.current.stopContinuousSync();\n engineRef.current.setTime(audio.currentTime);\n };\n\n /**\n * Handle seeking - update time immediately\n */\n const handleSeeked = () => {\n if (!engineRef.current) return;\n engineRef.current.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 // Guard: engine may have been destroyed by initialization effect cleanup\n if (engineRef.current) {\n engineRef.current.stopContinuousSync();\n }\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","useEffect","AnnotationRenderer","cancelled","result","audio","handlePlay","handlePause","handleSeeked","isFirstRender","defaultContainerStyle","defaultLayerStyle","defaultCanvasStyle","jsxs","jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index12.cjs","sources":["../src/converters/glyphResolver.js"],"sourcesContent":["/**\n * Glyph Resolver - Resolves character glyphs based on Unicode ranges\n *\n * Supports:\n * - Latin (0x0000-0x007F): Direct lookup from single-stroke.json\n * - Korean (0xAC00-0xD7A3): Direct lookup from korean-syllables.json\n * - CJK (0x4E00-0x9FFF): Dynamic import from cjk.json\n *\n * @module converters/glyphResolver\n */\n\nimport latinFont from \"../strokes/single-stroke.json\";\nimport koreanSyllables from \"../strokes/korean-syllables.json\";\n\n// CJK data (dynamic import)\nlet cjkFont = null;\nlet cjkLoadingPromise = null;\n\n/**\n * Resolve Korean syllable glyph\n *\n * @param {string} char - Korean syllable character\n * @returns {Object|null} Glyph object or null\n */\nfunction resolveKorean(char) {\n const syllableData = koreanSyllables.syllables?.[char];\n if (!syllableData || !syllableData.paths) {\n return null;\n }\n\n return {\n paths: syllableData.paths,\n width: 1.0,\n pathTiming: \"sequential\",\n };\n}\n\n/**\n * Convert CJK format to our glyph format\n *\n * @param {Array} polylines - Array of polylines from chinese-hershey-font\n * @returns {Object} Glyph object\n */\nfunction convertCJKFormat(polylines) {\n return {\n paths: polylines.map((polyline) => ({\n points: polyline,\n })),\n width: 1.0,\n pathTiming: \"sequential\",\n };\n}\n\n/**\n * Resolve CJK glyph\n *\n * @param {number} code - Unicode code point\n * @returns {Object|null} Glyph or null\n */\nfunction resolveCJK(code) {\n if (!cjkFont) {\n return null;\n }\n\n const key = `U+${code.toString(16).toUpperCase().padStart(4, \"0\")}`;\n const polylines = cjkFont[key];\n\n if (!polylines) {\n return null;\n }\n\n return convertCJKFormat(polylines);\n}\n\n// Default CJK data URL (can be overridden)\nlet cjkDataUrl = \"/cjk.json\";\n\n/**\n * Set the URL for CJK font data\n *\n * @param {string} url - URL to fetch CJK data from\n */\nexport function setCJKDataUrl(url) {\n cjkDataUrl = url;\n}\n\n/**\n * Preload CJK font data\n *\n * Call this function early to start loading CJK data in the background.\n * The data will be available for subsequent resolveGlyph calls.\n *\n * @param {string} [url] - Optional URL override for CJK data\n * @returns {Promise<void>}\n */\nexport async function preloadCJK(url) {\n if (cjkFont) {\n return;\n }\n\n if (cjkLoadingPromise) {\n return cjkLoadingPromise;\n }\n\n const fetchUrl = url || cjkDataUrl;\n\n cjkLoadingPromise = fetch(fetchUrl)\n .then((res) => {\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n return res.json();\n })\n .then((data) => {\n cjkFont = data;\n })\n .catch((err) => {\n console.warn(\"Failed to load CJK font data:\", err);\n cjkLoadingPromise = null;\n });\n\n return cjkLoadingPromise;\n}\n\n/**\n * Resolve glyph for a single character\n *\n * @param {string} char - Single character\n * @returns {Object|null} Glyph object with paths, width, pathTiming, or null\n */\nexport function resolveGlyph(char) {\n if (!char || char.length === 0) {\n return null;\n }\n\n const code = char.charCodeAt(0);\n\n // Space character\n if (char === \" \") {\n return { paths: [], width: 0.35 };\n }\n\n // Latin (Basic ASCII)\n if (code >= 0x0000 && code <= 0x007f) {\n const glyph = latinFont.glyphs?.[char];\n return glyph || null;\n }\n\n // Korean Syllables\n if (code >= 0xac00 && code <= 0xd7a3) {\n return resolveKorean(char);\n }\n\n // CJK Unified Ideographs\n if (code >= 0x4e00 && code <= 0x9fff) {\n // Trigger background loading if not loaded\n if (!cjkFont && !cjkLoadingPromise) {\n preloadCJK();\n }\n return resolveCJK(code);\n }\n\n // Unsupported character\n return null;\n}\n\nexport default resolveGlyph;\n"],"names":["cjkFont","cjkLoadingPromise","resolveKorean","char","syllableData","koreanSyllables","convertCJKFormat","polylines","polyline","resolveCJK","code","key","cjkDataUrl","setCJKDataUrl","url","preloadCJK","res","data","err","resolveGlyph","latinFont"],"mappings":"wKAeA,IAAIA,EAAU,KACVC,EAAoB,KAQxB,SAASC,EAAcC,EAAM,CAC3B,MAAMC,EAAeC,EAAAA,QAAgB,YAAYF,CAAI,EACrD,MAAI,CAACC,GAAgB,CAACA,EAAa,MAC1B,KAGF,CACL,MAAOA,EAAa,MACpB,MAAO,EACP,WAAY,YAChB,CACA,CAQA,SAASE,EAAiBC,EAAW,CACnC,MAAO,CACL,MAAOA,EAAU,IAAKC,IAAc,CAClC,OAAQA,CACd,EAAM,EACF,MAAO,EACP,WAAY,YAChB,CACA,CAQA,SAASC,EAAWC,EAAM,CACxB,GAAI,CAACV,EACH,OAAO,KAGT,MAAMW,EAAM,KAAKD,EAAK,SAAS,EAAE,EAAE,YAAW,EAAG,SAAS,EAAG,GAAG,CAAC,GAC3DH,EAAYP,EAAQW,CAAG,EAE7B,OAAKJ,EAIED,EAAiBC,CAAS,EAHxB,IAIX,CAGA,IAAIK,EAAa,YAOV,SAASC,EAAcC,EAAK,CACjCF,EAAaE,CACf,CAWO,eAAeC,EAAWD,EAAK,CACpC,OAAId,EACF,OAGEC,IAMJA,EAAoB,MAFHa,GAAOF,CAEU,EAC/B,KAAMI,GAAQ,CACb,GAAI,CAACA,EAAI,GAAI,MAAM,IAAI,MAAM,QAAQA,EAAI,MAAM,EAAE,EACjD,OAAOA,EAAI,KAAI,CACjB,CAAC,EACA,KAAMC,GAAS,CACdjB,EAAUiB,CACZ,CAAC,EACA,MAAOC,GAAQ,CACd,QAAQ,KAAK,gCAAiCA,CAAG,EACjDjB,EAAoB,IACtB,CAAC,EAEIA,EACT,CAQO,SAASkB,EAAahB,EAAM,CACjC,GAAI,CAACA,GAAQA,EAAK,SAAW,EAC3B,OAAO,KAGT,MAAMO,EAAOP,EAAK,WAAW,CAAC,EAG9B,OAAIA,IAAS,IACJ,CAAE,MAAO,GAAI,MAAO,GAAI,EAI7BO,GAAQ,GAAUA,GAAQ,IACdU,EAAAA,QAAU,SAASjB,CAAI,GACrB,KAIdO,GAAQ,OAAUA,GAAQ,MACrBR,EAAcC,CAAI,EAIvBO,GAAQ,OAAUA,GAAQ,OAExB,CAACV,GAAW,CAACC,GACfc,EAAU,EAELN,EAAWC,CAAI,GAIjB,IACT"}
|
package/dist/index12.js
CHANGED
|
@@ -1,160 +1,53 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
function
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
annotations: P = [],
|
|
11
|
-
currentTime: d = 0,
|
|
12
|
-
// Continuous sync mode (use instead of currentTime for smoother animation)
|
|
13
|
-
audioRef: a,
|
|
14
|
-
// Stroke configuration
|
|
15
|
-
strokeConfig: h,
|
|
16
|
-
// Callbacks
|
|
17
|
-
onLoad: A,
|
|
18
|
-
onError: r,
|
|
19
|
-
onPageChange: F,
|
|
20
|
-
// Styling
|
|
21
|
-
className: L,
|
|
22
|
-
style: b,
|
|
23
|
-
canvasStyle: R
|
|
24
|
-
}) {
|
|
25
|
-
const p = l(null), m = l(null), t = l(null), S = l(Promise.resolve()), [n, C] = Q(!1), o = q((e) => {
|
|
26
|
-
S.current = S.current.then(e).catch((c) => {
|
|
27
|
-
console.error("AnnotPdf: Queued operation failed:", c);
|
|
28
|
-
});
|
|
29
|
-
}, []), y = z(() => h, [h]);
|
|
30
|
-
i(() => {
|
|
31
|
-
if (!(!p.current || !m.current)) {
|
|
32
|
-
try {
|
|
33
|
-
t.current = new H({
|
|
34
|
-
canvasElement: p.current,
|
|
35
|
-
container: m.current,
|
|
36
|
-
strokeConfig: y
|
|
37
|
-
}), C(!0);
|
|
38
|
-
} catch (e) {
|
|
39
|
-
console.error("AnnotPdf: Failed to initialize renderer:", e), r && r(e);
|
|
40
|
-
}
|
|
41
|
-
return () => {
|
|
42
|
-
C(!1), t.current && (t.current.destroy(), t.current = null);
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
}, []), i(() => {
|
|
46
|
-
if (!n || !v)
|
|
47
|
-
return;
|
|
48
|
-
let e = !1;
|
|
49
|
-
return o(async () => {
|
|
50
|
-
if (t.current)
|
|
51
|
-
try {
|
|
52
|
-
const s = await t.current.loadPDF(v);
|
|
53
|
-
if (e) return;
|
|
54
|
-
if (!s.success) {
|
|
55
|
-
console.error("AnnotPdf: Failed to load PDF:", s.error), r && r(new Error(s.error));
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
A && A({ pageCount: s.pageCount });
|
|
59
|
-
} catch (s) {
|
|
60
|
-
if (e) return;
|
|
61
|
-
console.error("AnnotPdf: Failed to load PDF:", s), r && r(s);
|
|
62
|
-
}
|
|
63
|
-
}), () => {
|
|
64
|
-
e = !0;
|
|
65
|
-
};
|
|
66
|
-
}, [v, n, o]), i(() => {
|
|
67
|
-
!n || !u || typeof u != "number" || o(async () => {
|
|
68
|
-
if (t.current)
|
|
69
|
-
try {
|
|
70
|
-
const e = await t.current.setPage(u);
|
|
71
|
-
if (!e.success) {
|
|
72
|
-
console.error("AnnotPdf: Failed to set page:", e.error), r && r(new Error(e.error));
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
F && F(u);
|
|
76
|
-
} catch (e) {
|
|
77
|
-
console.error("AnnotPdf: Failed to set page:", e), r && r(e);
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
}, [u, n, o]), i(() => {
|
|
81
|
-
!n || !f || typeof f != "number" || o(async () => {
|
|
82
|
-
if (t.current)
|
|
83
|
-
try {
|
|
84
|
-
const e = await t.current.setScale(f);
|
|
85
|
-
e.success || (console.error("AnnotPdf: Failed to set scale:", e.error), r && r(new Error(e.error)));
|
|
86
|
-
} catch (e) {
|
|
87
|
-
console.error("AnnotPdf: Failed to set scale:", e), r && r(e);
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
}, [f, n, o]), i(() => {
|
|
91
|
-
if (!(!n || !t.current))
|
|
92
|
-
try {
|
|
93
|
-
t.current.setAnnotations(P || []);
|
|
94
|
-
} catch (e) {
|
|
95
|
-
console.error("AnnotPdf: Failed to set annotations:", e), r && r(e);
|
|
96
|
-
}
|
|
97
|
-
}, [P, n]), i(() => {
|
|
98
|
-
if (!a && !(!n || !t.current || d === void 0 || d === null))
|
|
99
|
-
try {
|
|
100
|
-
t.current.setTime(d);
|
|
101
|
-
} catch (e) {
|
|
102
|
-
console.error("AnnotPdf: Failed to set time:", e), r && r(e);
|
|
103
|
-
}
|
|
104
|
-
}, [d, a, n]), i(() => {
|
|
105
|
-
if (!a?.current || !n || !t.current)
|
|
106
|
-
return;
|
|
107
|
-
const e = a.current, c = () => {
|
|
108
|
-
t.current && t.current.startContinuousSync(() => e.currentTime);
|
|
109
|
-
}, s = () => {
|
|
110
|
-
t.current && (t.current.stopContinuousSync(), t.current.setTime(e.currentTime));
|
|
111
|
-
}, w = () => {
|
|
112
|
-
t.current && t.current.setTime(e.currentTime);
|
|
113
|
-
};
|
|
114
|
-
return e.addEventListener("play", c), e.addEventListener("pause", s), e.addEventListener("ended", s), e.addEventListener("seeked", w), e.paused || c(), () => {
|
|
115
|
-
e.removeEventListener("play", c), e.removeEventListener("pause", s), e.removeEventListener("ended", s), e.removeEventListener("seeked", w), t.current && t.current.stopContinuousSync();
|
|
116
|
-
};
|
|
117
|
-
}, [a, n]);
|
|
118
|
-
const k = l(!0);
|
|
119
|
-
i(() => {
|
|
120
|
-
if (k.current) {
|
|
121
|
-
k.current = !1;
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
if (t.current && y)
|
|
125
|
-
try {
|
|
126
|
-
t.current.updateStrokeConfig(y);
|
|
127
|
-
} catch (e) {
|
|
128
|
-
console.error("AnnotPdf: Failed to update stroke config:", e), r && r(e);
|
|
129
|
-
}
|
|
130
|
-
}, [y]);
|
|
131
|
-
const x = {
|
|
132
|
-
position: "relative",
|
|
133
|
-
display: "inline-block",
|
|
134
|
-
lineHeight: 0,
|
|
135
|
-
// Remove extra space below canvas
|
|
136
|
-
...b
|
|
137
|
-
// User styles override defaults
|
|
138
|
-
}, D = {
|
|
139
|
-
position: "absolute",
|
|
140
|
-
top: 0,
|
|
141
|
-
left: 0,
|
|
142
|
-
width: "100%",
|
|
143
|
-
height: "100%",
|
|
144
|
-
pointerEvents: "none",
|
|
145
|
-
// Allow clicks to pass through to canvas
|
|
146
|
-
overflow: "hidden"
|
|
147
|
-
}, T = {
|
|
148
|
-
display: "block",
|
|
149
|
-
...R
|
|
150
|
-
// User styles override defaults
|
|
1
|
+
import a from "./index26.js";
|
|
2
|
+
import i from "./index27.js";
|
|
3
|
+
let r = null, l = null;
|
|
4
|
+
function u(t) {
|
|
5
|
+
const n = i.syllables?.[t];
|
|
6
|
+
return !n || !n.paths ? null : {
|
|
7
|
+
paths: n.paths,
|
|
8
|
+
width: 1,
|
|
9
|
+
pathTiming: "sequential"
|
|
151
10
|
};
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
11
|
+
}
|
|
12
|
+
function s(t) {
|
|
13
|
+
return {
|
|
14
|
+
paths: t.map((n) => ({
|
|
15
|
+
points: n
|
|
16
|
+
})),
|
|
17
|
+
width: 1,
|
|
18
|
+
pathTiming: "sequential"
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function f(t) {
|
|
22
|
+
if (!r)
|
|
23
|
+
return null;
|
|
24
|
+
const n = `U+${t.toString(16).toUpperCase().padStart(4, "0")}`, e = r[n];
|
|
25
|
+
return e ? s(e) : null;
|
|
26
|
+
}
|
|
27
|
+
let o = "/cjk.json";
|
|
28
|
+
function d(t) {
|
|
29
|
+
o = t;
|
|
30
|
+
}
|
|
31
|
+
async function c(t) {
|
|
32
|
+
return r ? void 0 : l || (l = fetch(t || o).then((e) => {
|
|
33
|
+
if (!e.ok) throw new Error(`HTTP ${e.status}`);
|
|
34
|
+
return e.json();
|
|
35
|
+
}).then((e) => {
|
|
36
|
+
r = e;
|
|
37
|
+
}).catch((e) => {
|
|
38
|
+
console.warn("Failed to load CJK font data:", e), l = null;
|
|
39
|
+
}), l);
|
|
40
|
+
}
|
|
41
|
+
function y(t) {
|
|
42
|
+
if (!t || t.length === 0)
|
|
43
|
+
return null;
|
|
44
|
+
const n = t.charCodeAt(0);
|
|
45
|
+
return t === " " ? { paths: [], width: 0.35 } : n >= 0 && n <= 127 ? a.glyphs?.[t] || null : n >= 44032 && n <= 55203 ? u(t) : n >= 19968 && n <= 40959 ? (!r && !l && c(), f(n)) : null;
|
|
156
46
|
}
|
|
157
47
|
export {
|
|
158
|
-
|
|
48
|
+
y as default,
|
|
49
|
+
c as preloadCJK,
|
|
50
|
+
y as resolveGlyph,
|
|
51
|
+
d as setCJKDataUrl
|
|
159
52
|
};
|
|
160
53
|
//# sourceMappingURL=index12.js.map
|
package/dist/index12.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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';\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 (setConfig format)\n * @param {number} [props.strokeConfig.roughness] - Global roughness for all types\n * @param {number} [props.strokeConfig.bowing] - Global bowing for all types\n * @param {Object} [props.strokeConfig.highlight] - Highlight-specific options\n * @param {Object} [props.strokeConfig.text] - Text-specific options\n * @param {Object} [props.strokeConfig.underline] - Underline-specific options\n * @param {Object} [props.strokeConfig.arrow] - Arrow-specific options\n * @param {Object} [props.strokeConfig.circle] - Circle-specific options\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 PASS-THROUGH\n // ==========================================================================\n\n /**\n * Pass strokeConfig directly to engine without transformation\n *\n * The adapter is a pure pass-through - users construct config themselves\n * using presets (import { getPreset } from 'lib') if desired.\n * setConfig in StrokeRenderer handles merging with DEFAULT_CONFIG.\n */\n const mergedStrokeConfig = useMemo(() => strokeConfig, [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 /**\n * Start continuous sync when audio plays\n */\n const handlePlay = () => {\n if (!engineRef.current) return;\n engineRef.current.startContinuousSync(() => audio.currentTime);\n };\n\n /**\n * Stop continuous sync when audio pauses\n */\n const handlePause = () => {\n if (!engineRef.current) return;\n engineRef.current.stopContinuousSync();\n engineRef.current.setTime(audio.currentTime);\n };\n\n /**\n * Handle seeking - update time immediately\n */\n const handleSeeked = () => {\n if (!engineRef.current) return;\n engineRef.current.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 // Guard: engine may have been destroyed by initialization effect cleanup\n if (engineRef.current) {\n engineRef.current.stopContinuousSync();\n }\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","useEffect","AnnotationRenderer","cancelled","result","audio","handlePlay","handlePause","handleSeeked","isFirstRender","defaultContainerStyle","defaultLayerStyle","defaultCanvasStyle","jsxs","jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index12.js","sources":["../src/converters/glyphResolver.js"],"sourcesContent":["/**\n * Glyph Resolver - Resolves character glyphs based on Unicode ranges\n *\n * Supports:\n * - Latin (0x0000-0x007F): Direct lookup from single-stroke.json\n * - Korean (0xAC00-0xD7A3): Direct lookup from korean-syllables.json\n * - CJK (0x4E00-0x9FFF): Dynamic import from cjk.json\n *\n * @module converters/glyphResolver\n */\n\nimport latinFont from \"../strokes/single-stroke.json\";\nimport koreanSyllables from \"../strokes/korean-syllables.json\";\n\n// CJK data (dynamic import)\nlet cjkFont = null;\nlet cjkLoadingPromise = null;\n\n/**\n * Resolve Korean syllable glyph\n *\n * @param {string} char - Korean syllable character\n * @returns {Object|null} Glyph object or null\n */\nfunction resolveKorean(char) {\n const syllableData = koreanSyllables.syllables?.[char];\n if (!syllableData || !syllableData.paths) {\n return null;\n }\n\n return {\n paths: syllableData.paths,\n width: 1.0,\n pathTiming: \"sequential\",\n };\n}\n\n/**\n * Convert CJK format to our glyph format\n *\n * @param {Array} polylines - Array of polylines from chinese-hershey-font\n * @returns {Object} Glyph object\n */\nfunction convertCJKFormat(polylines) {\n return {\n paths: polylines.map((polyline) => ({\n points: polyline,\n })),\n width: 1.0,\n pathTiming: \"sequential\",\n };\n}\n\n/**\n * Resolve CJK glyph\n *\n * @param {number} code - Unicode code point\n * @returns {Object|null} Glyph or null\n */\nfunction resolveCJK(code) {\n if (!cjkFont) {\n return null;\n }\n\n const key = `U+${code.toString(16).toUpperCase().padStart(4, \"0\")}`;\n const polylines = cjkFont[key];\n\n if (!polylines) {\n return null;\n }\n\n return convertCJKFormat(polylines);\n}\n\n// Default CJK data URL (can be overridden)\nlet cjkDataUrl = \"/cjk.json\";\n\n/**\n * Set the URL for CJK font data\n *\n * @param {string} url - URL to fetch CJK data from\n */\nexport function setCJKDataUrl(url) {\n cjkDataUrl = url;\n}\n\n/**\n * Preload CJK font data\n *\n * Call this function early to start loading CJK data in the background.\n * The data will be available for subsequent resolveGlyph calls.\n *\n * @param {string} [url] - Optional URL override for CJK data\n * @returns {Promise<void>}\n */\nexport async function preloadCJK(url) {\n if (cjkFont) {\n return;\n }\n\n if (cjkLoadingPromise) {\n return cjkLoadingPromise;\n }\n\n const fetchUrl = url || cjkDataUrl;\n\n cjkLoadingPromise = fetch(fetchUrl)\n .then((res) => {\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n return res.json();\n })\n .then((data) => {\n cjkFont = data;\n })\n .catch((err) => {\n console.warn(\"Failed to load CJK font data:\", err);\n cjkLoadingPromise = null;\n });\n\n return cjkLoadingPromise;\n}\n\n/**\n * Resolve glyph for a single character\n *\n * @param {string} char - Single character\n * @returns {Object|null} Glyph object with paths, width, pathTiming, or null\n */\nexport function resolveGlyph(char) {\n if (!char || char.length === 0) {\n return null;\n }\n\n const code = char.charCodeAt(0);\n\n // Space character\n if (char === \" \") {\n return { paths: [], width: 0.35 };\n }\n\n // Latin (Basic ASCII)\n if (code >= 0x0000 && code <= 0x007f) {\n const glyph = latinFont.glyphs?.[char];\n return glyph || null;\n }\n\n // Korean Syllables\n if (code >= 0xac00 && code <= 0xd7a3) {\n return resolveKorean(char);\n }\n\n // CJK Unified Ideographs\n if (code >= 0x4e00 && code <= 0x9fff) {\n // Trigger background loading if not loaded\n if (!cjkFont && !cjkLoadingPromise) {\n preloadCJK();\n }\n return resolveCJK(code);\n }\n\n // Unsupported character\n return null;\n}\n\nexport default resolveGlyph;\n"],"names":["cjkFont","cjkLoadingPromise","resolveKorean","char","syllableData","koreanSyllables","convertCJKFormat","polylines","polyline","resolveCJK","code","key","cjkDataUrl","setCJKDataUrl","url","preloadCJK","res","data","err","resolveGlyph","latinFont"],"mappings":";;AAeA,IAAIA,IAAU,MACVC,IAAoB;AAQxB,SAASC,EAAcC,GAAM;AAC3B,QAAMC,IAAeC,EAAgB,YAAYF,CAAI;AACrD,SAAI,CAACC,KAAgB,CAACA,EAAa,QAC1B,OAGF;AAAA,IACL,OAAOA,EAAa;AAAA,IACpB,OAAO;AAAA,IACP,YAAY;AAAA,EAChB;AACA;AAQA,SAASE,EAAiBC,GAAW;AACnC,SAAO;AAAA,IACL,OAAOA,EAAU,IAAI,CAACC,OAAc;AAAA,MAClC,QAAQA;AAAA,IACd,EAAM;AAAA,IACF,OAAO;AAAA,IACP,YAAY;AAAA,EAChB;AACA;AAQA,SAASC,EAAWC,GAAM;AACxB,MAAI,CAACV;AACH,WAAO;AAGT,QAAMW,IAAM,KAAKD,EAAK,SAAS,EAAE,EAAE,YAAW,EAAG,SAAS,GAAG,GAAG,CAAC,IAC3DH,IAAYP,EAAQW,CAAG;AAE7B,SAAKJ,IAIED,EAAiBC,CAAS,IAHxB;AAIX;AAGA,IAAIK,IAAa;AAOV,SAASC,EAAcC,GAAK;AACjC,EAAAF,IAAaE;AACf;AAWO,eAAeC,EAAWD,GAAK;AACpC,SAAId,IACF,SAGEC,MAMJA,IAAoB,MAFHa,KAAOF,CAEU,EAC/B,KAAK,CAACI,MAAQ;AACb,QAAI,CAACA,EAAI,GAAI,OAAM,IAAI,MAAM,QAAQA,EAAI,MAAM,EAAE;AACjD,WAAOA,EAAI,KAAI;AAAA,EACjB,CAAC,EACA,KAAK,CAACC,MAAS;AACd,IAAAjB,IAAUiB;AAAA,EACZ,CAAC,EACA,MAAM,CAACC,MAAQ;AACd,YAAQ,KAAK,iCAAiCA,CAAG,GACjDjB,IAAoB;AAAA,EACtB,CAAC,GAEIA;AACT;AAQO,SAASkB,EAAahB,GAAM;AACjC,MAAI,CAACA,KAAQA,EAAK,WAAW;AAC3B,WAAO;AAGT,QAAMO,IAAOP,EAAK,WAAW,CAAC;AAG9B,SAAIA,MAAS,MACJ,EAAE,OAAO,IAAI,OAAO,KAAI,IAI7BO,KAAQ,KAAUA,KAAQ,MACdU,EAAU,SAASjB,CAAI,KACrB,OAIdO,KAAQ,SAAUA,KAAQ,QACrBR,EAAcC,CAAI,IAIvBO,KAAQ,SAAUA,KAAQ,SAExB,CAACV,KAAW,CAACC,KACfc,EAAU,GAELN,EAAWC,CAAI,KAIjB;AACT;"}
|
package/dist/index13.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const h=require("./
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const h=require("react/jsx-runtime"),n=require("react"),k=require("./index2.cjs");function q({pdfUrl:v,page:o=1,scale:a=1.5,annotations:f=[],currentTime:d=0,audioRef:l,strokeConfig:y,onLoad:A,onError:r,onPageChange:m,className:b,style:L,canvasStyle:C}){const P=n.useRef(null),p=n.useRef(null),t=n.useRef(null),F=n.useRef(Promise.resolve()),[s,S]=n.useState(!1),w=n.useRef(f);n.useEffect(()=>{w.current=f},[f]);const c=n.useCallback(e=>{F.current=F.current.then(e).catch(i=>{console.error("AnnotPdf: Queued operation failed:",i)})},[]);n.useEffect(()=>{if(!(!P.current||!p.current)){try{t.current=new k.AnnotationRenderer({canvasElement:P.current,container:p.current,strokeConfig:y}),S(!0)}catch(e){console.error("AnnotPdf: Failed to initialize renderer:",e),r&&r(e)}return()=>{S(!1),t.current&&(t.current.destroy(),t.current=null)}}},[]),n.useEffect(()=>{if(!s||!v)return;let e=!1;return c(async()=>{if(t.current)try{const u=await t.current.loadPDF(v);if(e)return;if(!u.success){console.error("AnnotPdf: Failed to load PDF:",u.error),r&&r(new Error(u.error));return}A&&A({pageCount:u.pageCount})}catch(u){if(e)return;console.error("AnnotPdf: Failed to load PDF:",u),r&&r(u)}}),()=>{e=!0}},[v,s,c]),n.useEffect(()=>{!s||!o||typeof o!="number"||c(async()=>{if(t.current)try{const e=await t.current.setPage(o);if(!e.success){console.error("AnnotPdf: Failed to set page:",e.error),r&&r(new Error(e.error));return}t.current.setAnnotations(w.current||[]),m&&m(o)}catch(e){console.error("AnnotPdf: Failed to set page:",e),r&&r(e)}})},[o,s,c]),n.useEffect(()=>{!s||!a||typeof a!="number"||c(async()=>{if(t.current)try{const e=await t.current.setScale(a);e.success||(console.error("AnnotPdf: Failed to set scale:",e.error),r&&r(new Error(e.error)))}catch(e){console.error("AnnotPdf: Failed to set scale:",e),r&&r(e)}})},[a,s,c]),n.useEffect(()=>{if(!(!s||!t.current))try{t.current.setAnnotations(f||[])}catch(e){console.error("AnnotPdf: Failed to set annotations:",e),r&&r(e)}},[f,s]),n.useEffect(()=>{if(!l&&!(!s||!t.current||d===void 0||d===null))try{t.current.setTime(d)}catch(e){console.error("AnnotPdf: Failed to set time:",e),r&&r(e)}},[d,l,s]),n.useEffect(()=>{if(!l?.current||!s||!t.current)return;const e=l.current,i=()=>{t.current&&t.current.startContinuousSync(()=>e.currentTime)},u=()=>{t.current&&(t.current.stopContinuousSync(),t.current.setTime(e.currentTime))},R=()=>{t.current&&t.current.setTime(e.currentTime)};return e.addEventListener("play",i),e.addEventListener("pause",u),e.addEventListener("ended",u),e.addEventListener("seeked",R),e.paused||i(),()=>{e.removeEventListener("play",i),e.removeEventListener("pause",u),e.removeEventListener("ended",u),e.removeEventListener("seeked",R),t.current&&t.current.stopContinuousSync()}},[l,s]),n.useEffect(()=>{if(!s||!t.current||!y)return;const e=t.current.updateStrokeConfig(y);e&&!e.success&&r&&r(new Error(e.error))},[y,s]);const E={position:"relative",display:"inline-block",lineHeight:0,...L},j={position:"absolute",top:0,left:0,width:"100%",height:"100%",pointerEvents:"none",overflow:"hidden"},g={display:"block",...C};return h.jsxs("div",{className:b,style:E,children:[h.jsx("canvas",{ref:P,style:g}),h.jsx("div",{ref:p,style:j})]})}exports.default=q;
|
|
2
2
|
//# sourceMappingURL=index13.cjs.map
|