@embedpdf/engine-core 3.0.0-next.15 → 3.0.0-next.16

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.
Files changed (91) hide show
  1. package/dist/{AnnotationList-CkijRHet.d.cts → AnnotationList-DcyhF2kU.d.cts} +1836 -1308
  2. package/dist/{AnnotationList-CkijRHet.d.ts → AnnotationList-DcyhF2kU.d.ts} +1836 -1308
  3. package/dist/{Change-CjImsrN0.cjs → Change-Ct-dB8D5.cjs} +115 -2
  4. package/dist/Change-Ct-dB8D5.cjs.map +1 -0
  5. package/dist/{Change-CKoxfTNY.js → Change-tMR34hTU.js} +92 -3
  6. package/dist/Change-tMR34hTU.js.map +1 -0
  7. package/dist/{DocumentRenderService-BfFdFSwI.cjs → DocumentRenderService-9IjSQEIU.cjs} +2 -1
  8. package/dist/{DocumentRenderService-BfFdFSwI.cjs.map → DocumentRenderService-9IjSQEIU.cjs.map} +1 -1
  9. package/dist/{DocumentRenderService-DV3J9lMz.js → DocumentRenderService-DFrZ9wfW.js} +2 -1
  10. package/dist/{DocumentRenderService-DV3J9lMz.js.map → DocumentRenderService-DFrZ9wfW.js.map} +1 -1
  11. package/dist/{DocumentSecurityService-e6vp2de8.d.cts → DocumentSecurityService-D2zA4NKi.d.cts} +2 -2
  12. package/dist/{DocumentSecurityService-CU1M6Q7m.d.ts → DocumentSecurityService-YJV9df8P.d.ts} +2 -2
  13. package/dist/{LocalEngine-DFoBm4B3.d.cts → LocalEngine-DBt2-GWF.d.ts} +102 -18
  14. package/dist/{LocalEngine-DPriFujS.d.ts → LocalEngine-IA9zh3Jh.d.cts} +102 -18
  15. package/dist/{authority-DLMNR7ff.cjs → authority-BFp6XM04.cjs} +373 -52
  16. package/dist/authority-BFp6XM04.cjs.map +1 -0
  17. package/dist/{authority-B3BklsB4.js → authority-PuxpGFhJ.js} +320 -53
  18. package/dist/authority-PuxpGFhJ.js.map +1 -0
  19. package/dist/{builders-CNj0POeY.js → builders-DV62HA3I.js} +6 -2
  20. package/dist/builders-DV62HA3I.js.map +1 -0
  21. package/dist/{builders-Cgdp_YCn.cjs → builders-DgTAj_tP.cjs} +6 -2
  22. package/dist/builders-DgTAj_tP.cjs.map +1 -0
  23. package/dist/{calibrate-DJBXnhLE.cjs → calibrate-DJbi1wuK.cjs} +118 -25
  24. package/dist/calibrate-DJbi1wuK.cjs.map +1 -0
  25. package/dist/{calibrate-7T3fnP8-.js → calibrate-nQFgCuki.js} +83 -26
  26. package/dist/calibrate-nQFgCuki.js.map +1 -0
  27. package/dist/charmap-Cprti1ta.js.map +1 -1
  28. package/dist/charmap-aYlefQP9.cjs.map +1 -1
  29. package/dist/conformance.cjs +1744 -75
  30. package/dist/conformance.cjs.map +1 -1
  31. package/dist/conformance.d.cts +105 -10
  32. package/dist/conformance.d.ts +105 -10
  33. package/dist/conformance.js +1738 -76
  34. package/dist/conformance.js.map +1 -1
  35. package/dist/index.cjs +25 -6
  36. package/dist/index.d.cts +2 -2
  37. package/dist/index.d.ts +2 -2
  38. package/dist/index.js +7 -7
  39. package/dist/public.cjs +6 -4
  40. package/dist/public.d.cts +5 -5
  41. package/dist/public.d.ts +5 -5
  42. package/dist/public.js +5 -5
  43. package/dist/{resolver-CzxmD9Ck.js → resolver-BBggAxQc.js} +6 -3
  44. package/dist/resolver-BBggAxQc.js.map +1 -0
  45. package/dist/{resolver-D2OCtE8F.cjs → resolver-BWSWSF6R.cjs} +6 -3
  46. package/dist/resolver-BWSWSF6R.cjs.map +1 -0
  47. package/dist/{runtime-Cl0v56mm.d.ts → runtime-BaDKjxiu.d.ts} +67 -140
  48. package/dist/{runtime-p1WDzDo4.d.cts → runtime-CWEuiwbk.d.cts} +67 -140
  49. package/dist/runtime.cjs +267 -7
  50. package/dist/runtime.cjs.map +1 -1
  51. package/dist/runtime.d.cts +5 -5
  52. package/dist/runtime.d.ts +5 -5
  53. package/dist/runtime.js +246 -8
  54. package/dist/runtime.js.map +1 -1
  55. package/dist/{schema-DzlUNk38.js → schema-6S2VmlAj.js} +8 -2
  56. package/dist/schema-6S2VmlAj.js.map +1 -0
  57. package/dist/{schema-C-gU4vj7.cjs → schema-miH6tWQC.cjs} +13 -1
  58. package/dist/schema-miH6tWQC.cjs.map +1 -0
  59. package/dist/{schemas-D6xOGjqg.js → schemas-BE2QGOit.js} +256 -59
  60. package/dist/schemas-BE2QGOit.js.map +1 -0
  61. package/dist/{schemas-Berf1swF.cjs → schemas-DljJR9ut.cjs} +397 -80
  62. package/dist/schemas-DljJR9ut.cjs.map +1 -0
  63. package/dist/validation.cjs +1 -1
  64. package/dist/validation.d.cts +1 -1
  65. package/dist/validation.d.ts +1 -1
  66. package/dist/validation.js +1 -1
  67. package/dist/{viewport-DlL5Bxbg.cjs → viewport-CHG9qQUb.cjs} +2 -2
  68. package/dist/{viewport-DlL5Bxbg.cjs.map → viewport-CHG9qQUb.cjs.map} +1 -1
  69. package/dist/{viewport-BJW13EGZ.js → viewport-DQR5LyX3.js} +2 -2
  70. package/dist/{viewport-BJW13EGZ.js.map → viewport-DQR5LyX3.js.map} +1 -1
  71. package/dist/wire.cjs +225 -57
  72. package/dist/wire.cjs.map +1 -1
  73. package/dist/wire.d.cts +3548 -1151
  74. package/dist/wire.d.ts +3548 -1151
  75. package/dist/wire.js +198 -53
  76. package/dist/wire.js.map +1 -1
  77. package/package.json +1 -1
  78. package/dist/Change-CKoxfTNY.js.map +0 -1
  79. package/dist/Change-CjImsrN0.cjs.map +0 -1
  80. package/dist/authority-B3BklsB4.js.map +0 -1
  81. package/dist/authority-DLMNR7ff.cjs.map +0 -1
  82. package/dist/builders-CNj0POeY.js.map +0 -1
  83. package/dist/builders-Cgdp_YCn.cjs.map +0 -1
  84. package/dist/calibrate-7T3fnP8-.js.map +0 -1
  85. package/dist/calibrate-DJBXnhLE.cjs.map +0 -1
  86. package/dist/resolver-CzxmD9Ck.js.map +0 -1
  87. package/dist/resolver-D2OCtE8F.cjs.map +0 -1
  88. package/dist/schema-C-gU4vj7.cjs.map +0 -1
  89. package/dist/schema-DzlUNk38.js.map +0 -1
  90. package/dist/schemas-Berf1swF.cjs.map +0 -1
  91. package/dist/schemas-D6xOGjqg.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"charmap-aYlefQP9.cjs","names":["EngineError","EngineErrorCode","annotationKey"],"sources":["../src/dto/AnnotationRender.ts","../src/resource/base64.ts","../src/text/charmap.ts"],"sourcesContent":["import { EngineError } from '../errors/EngineError';\nimport { EngineErrorCode } from '../errors/EngineErrorCode';\nimport type {\n PageImageHandle,\n PageNetworkRenderFormat,\n PageRaster,\n PageRenderViewport,\n} from './PageRender';\nimport type { PdfRotation } from '../geometry/primitives';\nimport { annotationKey } from '../identity/annotationKey';\nimport type { AnnotationRef } from '../identity/AnnotationRef';\nimport type { PageRef } from '../identity/PageRef';\nimport type { Coordinates, PageCoordinates } from '../pageSpace/coordinates';\n\n/**\n * Which `/AP` entry an appearance is: Normal (`/N`), shown at rest;\n * Rollover (`/R`), while the pointer is over the annotation; Down (`/D`),\n * while it is pressed (ISO 32000-2 §12.5.5).\n */\nexport type AnnotationAppearanceMode = 'normal' | 'rollover' | 'down';\n\n/** Every appearance mode, in the one order the engine renders and names them. */\nexport const ANNOTATION_APPEARANCE_MODES: readonly AnnotationAppearanceMode[] = [\n 'normal',\n 'rollover',\n 'down',\n];\n\n/**\n * The modes a request asks for, each once and in\n * {@link ANNOTATION_APPEARANCE_MODES} order; `undefined` when it asks for\n * every mode, whether by leaving `modes` out or by naming all three, so both\n * are one request (and one cached image set). An empty list asks for nothing\n * and is refused with `InvalidArg`.\n */\nexport function appearanceModesOf(\n modes: readonly AnnotationAppearanceMode[] | undefined,\n): readonly AnnotationAppearanceMode[] | undefined {\n if (modes === undefined) return undefined;\n if (modes.length === 0) {\n throw new EngineError(EngineErrorCode.InvalidArg, 'modes must name at least one mode', {\n details: { field: 'modes' },\n });\n }\n const wanted = ANNOTATION_APPEARANCE_MODES.filter((mode) => modes.includes(mode));\n return wanted.length === ANNOTATION_APPEARANCE_MODES.length ? undefined : wanted;\n}\n\n/**\n * Options for batch-rendering a page's annotation appearance streams.\n * Narrower than `PageRenderOptions`: each bitmap is sized to its\n * annotation's own `/Rect`, so there is no target.\n */\nexport interface AnnotationAppearanceRenderOptions {\n /**\n * The size, as for a page render: the appearances come out at the scale\n * the page would have at this viewport, so passing a page image's\n * viewport makes them match it. `{ kind: 'width' }` is the width of the\n * whole (turned) page. Default `{ kind: 'scale', scale: 1 }`.\n */\n viewport?: PageRenderViewport;\n /** Page rotation in degrees clockwise. Default `0`. */\n rotation?: PdfRotation;\n /**\n * Which appearance modes to render, of those each annotation stores.\n * Default: every mode it stores, so one call gives the look at rest, under\n * the pointer and pressed. A mode an annotation doesn't store gives no\n * image for it. Pass `['normal']` for the look at rest only. An empty list\n * is refused with `InvalidArg`.\n */\n modes?: AnnotationAppearanceMode[];\n /**\n * Output-pixel budget per appearance — same semantics as\n * `PageRenderOptions.maxOutputPixels`: appearances are sized by\n * `rect × scale` (the viewport's scale), and a page-sized stamp at a high scale is the same\n * memory bomb a full-page render is. Server requests carry the\n * deployment policy's budget; local callers omit it unless configured.\n */\n maxOutputPixels?: number;\n}\n\n/**\n * Encoded-output options for the cacheable cloud HTTP endpoint. Extends\n * the worker render options with image-encoding controls, mirroring the\n * `PageRenderOptions` -> `PageImageOptions` split.\n */\nexport interface AnnotationAppearanceImageOptions extends AnnotationAppearanceRenderOptions {\n format?: PageNetworkRenderFormat;\n /** WebP quality from 0 (smallest) to 1 (best); PNG ignores it. */\n quality?: number;\n}\n\n/**\n * HTTP/token shape: encoded options plus the version field that makes the\n * response content-addressed and CDN-cacheable.\n *\n * Only `annotationVersion` matters here: an appearance bitmap is rendered\n * purely from the annotation's own `/AP` stream, so it changes iff the\n * annotation changes. Page base content (`contentVersion`/`docVersion`) does\n * not affect appearances and is deliberately not part of the cache key —\n * same as the annotation list endpoint. Which state an annotation shows\n * (`/AS`) is not part of it either: every state is rendered, so switching a\n * check box changes no image.\n */\nexport interface AnnotationAppearancesQuery {\n options: AnnotationAppearanceImageOptions;\n annotationVersion?: number;\n}\n\n/**\n * The widget twin of {@link AnnotationAppearancesQuery}: a page's widget\n * images change exactly when its widgets do, so `widgetVersion` keys them.\n */\nexport interface WidgetAppearancesQuery {\n options: AnnotationAppearanceImageOptions;\n widgetVersion?: number;\n}\n\n/**\n * One rendered appearance: the raw RGBA raster plus the metadata needed to\n * position and identify it. `rect` is the placement box in page space, so\n * the consumer can place the bitmap without a second read.\n *\n * Rotation convention (`appearanceTurnOf`): a box kind (square, circle,\n * free text, stamp, caret) drawn turned, whose drawing stays inside the\n * turned box, renders turned back upright and `rect` is its `box`; the\n * consumer re-applies the DTO's `rotation`, degrees clockwise, as a view\n * transform about the box centre (CSS `rotate()` turns the same way), which\n * makes the raster rotation-invariant (rotating never re-renders).\n * Everything else — lines, polygons and ink, drawn with their points turned,\n * a free-text callout, a turned drawing that reaches past its box,\n * and appearances with any other matrix — renders as-is with `rect` =\n * `/Rect` and needs no transform.\n *\n * An annotation with no normal appearance in the file (`hasAppearance`\n * false) renders as the engine draws it in memory, never written: `rect` is\n * the box that drawing takes, which can reach past `/Rect` (a line's\n * arrowhead on a `/Rect` with no height). One the engine can't draw without\n * writing an appearance has no raster.\n */\nexport interface AnnotationAppearanceRaster<C extends Coordinates = PageCoordinates> {\n /** The annotation's name, for every annotation with an appearance. */\n ref: AnnotationRef;\n mode: AnnotationAppearanceMode;\n /**\n * The state this image draws, when the mode stores several (a check box's\n * `Off` and its on state): every state comes back, whatever `/AS` says,\n * and the annotation's `appearanceState` (or its form field's value) says\n * which one shows. `null` when the mode is a single appearance.\n */\n state: string | null;\n rect: C['box'];\n raster: PageRaster;\n}\n\n/**\n * Batch result for one page: the page, and every rendered appearance, keyed\n * implicitly by `ref` on each entry.\n */\nexport interface AnnotationAppearancesResult<C extends Coordinates = PageCoordinates> {\n page: PageRef;\n appearances: AnnotationAppearanceRaster<C>[];\n}\n\n/**\n * Encoded counterpart of {@link AnnotationAppearanceRaster}: the same\n * identity/placement metadata, but the RGBA raster has been run through an\n * image encoder into a lazily-fetched `PageImageHandle` (PNG/WebP). This is\n * what both the local engine's `renderAppearances()` and the cloud\n * client (decoding the multipart parts) produce.\n */\nexport interface AnnotationAppearanceImage<C extends Coordinates = PageCoordinates> {\n ref: AnnotationRef;\n mode: AnnotationAppearanceMode;\n /** The state this image draws — see {@link AnnotationAppearanceRaster}. */\n state: string | null;\n /** Placement box (unrotated for rotation-stripped renders) — see\n * {@link AnnotationAppearanceRaster}. */\n rect: C['box'];\n image: PageImageHandle;\n}\n\n/**\n * Batch encoded result for one page — image-handle analogue of\n * {@link AnnotationAppearancesResult}.\n */\nexport interface AnnotationAppearanceImagesResult<C extends Coordinates = PageCoordinates> {\n page: PageRef;\n appearances: AnnotationAppearanceImage<C>[];\n}\n\n/**\n * One appearance in the cloud appearance endpoint's `body` part: which part\n * carries its image (`resources.image`, the key of a `resource:<key>` part),\n * plus what the client needs to place and identify it without a second\n * round-trip. The client finds the image by its key and the annotation by\n * `ref`, so every annotation with an appearance stream is listed.\n */\nexport interface AnnotationAppearanceBatchEntry<C extends Coordinates = PageCoordinates> {\n /** The image's key: its bytes are the part `resource:<key>`. */\n resources: { image: string };\n ref: AnnotationRef;\n mode: AnnotationAppearanceMode;\n state: string | null;\n rect: C['box'];\n width: number;\n height: number;\n format: PageNetworkRenderFormat;\n contentType: string;\n}\n\n/**\n * The `body` part of the appearance multipart response. The other parts are\n * the encoded images, one per `appearances[i].resources.image`.\n */\nexport interface AnnotationAppearanceBatch<C extends Coordinates = PageCoordinates> {\n page: PageRef;\n appearances: AnnotationAppearanceBatchEntry<C>[];\n}\n\n/**\n * The pictures that show the page as it is now: each annotation's look at\n * rest (`normal`), in the state it shows. An appearance batch holds every\n * look and state an annotation stores; this picks, for each annotation, the\n * one a page shows: the state its `appearanceState` names, or `Off` when it\n * names none, as PDFium draws it. A state with no picture shows nothing.\n */\nexport function shownAppearances<\n Appearance extends {\n readonly ref: AnnotationRef;\n readonly mode: AnnotationAppearanceMode;\n readonly state: string | null;\n },\n>(\n appearances: readonly Appearance[],\n annotations: readonly { readonly ref: AnnotationRef; readonly appearanceState: string | null }[],\n): Appearance[] {\n const shownState = new Map<string, string>();\n for (const annotation of annotations) {\n shownState.set(annotationKey(annotation.ref), annotation.appearanceState ?? 'Off');\n }\n return appearances.filter(\n (appearance) =>\n appearance.mode === 'normal' &&\n (appearance.state === null ||\n appearance.state === (shownState.get(annotationKey(appearance.ref)) ?? 'Off')),\n );\n}\n","/**\n * Standard base64 (RFC 4648 §4), dependency-free: no Buffer, the same in\n * browsers, workers and Node. Encoding and decoding write into one\n * preallocated buffer, so a resource of tens of megabytes costs one pass.\n */\n\nconst BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\nconst PADDING = 0x3d; // '='\n\nconst ENCODE = Uint8Array.from(BASE64_ALPHABET, (character) => character.charCodeAt(0));\nconst DECODE = (() => {\n const table = new Int16Array(128).fill(-1);\n for (let i = 0; i < BASE64_ALPHABET.length; i++) table[BASE64_ALPHABET.charCodeAt(i)] = i;\n return table;\n})();\n\n/** Padded base64 of `bytes`. */\nexport function toBase64(bytes: Uint8Array): string {\n const out = new Uint8Array(4 * Math.ceil(bytes.length / 3));\n let written = 0;\n let i = 0;\n for (; i + 2 < bytes.length; i += 3) {\n const triple = (bytes[i]! << 16) | (bytes[i + 1]! << 8) | bytes[i + 2]!;\n out[written++] = ENCODE[triple >> 18]!;\n out[written++] = ENCODE[(triple >> 12) & 0x3f]!;\n out[written++] = ENCODE[(triple >> 6) & 0x3f]!;\n out[written++] = ENCODE[triple & 0x3f]!;\n }\n const rest = bytes.length - i;\n if (rest > 0) {\n const triple = (bytes[i]! << 16) | (rest === 2 ? bytes[i + 1]! << 8 : 0);\n out[written++] = ENCODE[triple >> 18]!;\n out[written++] = ENCODE[(triple >> 12) & 0x3f]!;\n out[written++] = rest === 2 ? ENCODE[(triple >> 6) & 0x3f]! : PADDING;\n out[written++] = PADDING;\n }\n return new TextDecoder().decode(out);\n}\n\n/**\n * The number of bytes `encoded` decodes to, without decoding it, or `null`\n * when it can't be base64. Padding is optional, as {@link fromBase64} reads it.\n */\nexport function decodedLengthOf(encoded: string): number | null {\n const end = unpaddedLengthOf(encoded);\n if (end % 4 === 1) return null;\n return Math.floor((end * 3) / 4);\n}\n\n/** The bytes padded or unpadded base64 holds; throws on anything else. */\nexport function fromBase64(encoded: string): Uint8Array {\n const end = unpaddedLengthOf(encoded);\n if (end % 4 === 1) throw new Error('malformed base64');\n const out = new Uint8Array(Math.floor((end * 3) / 4));\n let written = 0;\n let buffer = 0;\n let bits = 0;\n for (let i = 0; i < end; i++) {\n const code = encoded.charCodeAt(i);\n const value = code < 128 ? DECODE[code]! : -1;\n if (value < 0) throw new Error('malformed base64');\n buffer = ((buffer << 6) | value) & 0xffffff;\n bits += 6;\n if (bits >= 8) {\n bits -= 8;\n out[written++] = (buffer >> bits) & 0xff;\n }\n }\n return out;\n}\n\n// Trailing padding, scanned backward to avoid regex backtracking on long runs.\nfunction unpaddedLengthOf(encoded: string): number {\n let end = encoded.length;\n while (end > 0 && encoded.charCodeAt(end - 1) === PADDING) end -= 1;\n return end;\n}\n","import type { TextRange } from './TextRange';\nimport type { PageTextSnapshot } from '../dto/PageTextSnapshot';\n\n/**\n * Character↔text index translation — the one place the two text index\n * spaces of a page meet.\n *\n * A page has two index spaces:\n *\n * - character space: PDFium's internal character list, the space geometry\n * runs tile (`PageGeometryRun.start`), hit-testing addresses, and\n * selection ranges live in. Size: `PageTextSnapshot.charCount`.\n * - text space: UTF-16 code-unit offsets into the extracted string\n * `PageTextSnapshot.text` — what search matching and slicing operate on.\n *\n * They diverge in exactly two ways, both encoded by `charMap` anchors:\n * - a non-printing character occupies a character slot but contributes\n * zero text units (PDFium's `char_indices_` skip list);\n * - a supplementary-plane character contributes two text units (a\n * surrogate pair) once extraction is UTF-16-faithful.\n *\n * `charMap` semantics (see {@link CharMapAnchor}): an implicit head anchor\n * `[0, 0]`; between anchors every character advances the text by exactly one\n * unit; an anchor sits at `i + 1` whenever character `i` contributed\n * something other than one unit. Absent/empty map = identity\n * (`charCount === text.length`). `charMapViolation` is the validator both\n * the wire schema and engine builders share — malformed maps are rejected,\n * never silently repaired.\n *\n * Boundary model: a \"character boundary\" `c` ∈ [0, charCount] sits before\n * character `c`; `boundaryTextOffset(s, charCount) === text.length` always.\n * All ranges are half-open in both spaces.\n */\n\n/**\n * One anchor of the character→text mapping: from character index `char`\n * onward (until the next anchor), boundary `c` maps to text offset\n * `text + (c - char)`.\n */\nexport type CharMapAnchor = readonly [char: number, text: number];\n\n/** Direction to resolve a text offset that falls on a zero-width plateau —\n * see {@link charBoundaryAtTextOffset}. */\nexport type CharBoundaryBias = 'start' | 'end';\n\nconst clampInt = (value: number, lo: number, hi: number): number =>\n Math.min(Math.max(Math.floor(value), lo), hi);\n\n/** The greatest anchor with `anchor.char <= boundary` (implicit head [0,0]). */\nfunction anchorAtOrBefore(map: ReadonlyArray<CharMapAnchor>, boundary: number): CharMapAnchor {\n let lo = 0;\n let hi = map.length - 1;\n let best: CharMapAnchor | null = null;\n while (lo <= hi) {\n const mid = (lo + hi) >> 1;\n if (map[mid][0] <= boundary) {\n best = map[mid];\n lo = mid + 1;\n } else {\n hi = mid - 1;\n }\n }\n return best ?? [0, 0];\n}\n\n/**\n * Text offset of character boundary `boundary` (clamped into\n * [0, charCount]). For a dropped character this is the offset of the next\n * real text unit — exactly what half-open slicing needs at either end.\n */\nexport function boundaryTextOffset(snapshot: PageTextSnapshot, boundary: number): number {\n const b = clampInt(boundary, 0, snapshot.charCount);\n const map = snapshot.charMap;\n if (!map || map.length === 0) return Math.min(b, snapshot.text.length);\n const [char, text] = anchorAtOrBefore(map, b);\n return text + (b - char);\n}\n\n/**\n * The character boundary for a text offset, with explicit plateau bias:\n *\n * - `'start'`: the greatest boundary whose text offset is <= `offset` —\n * scans forward past zero-width characters, so a range start never\n * includes a dropped character that precedes its first real unit.\n * - `'end'`: the smallest boundary whose text offset is >= `offset` —\n * stops before zero-width characters, so a range end never swallows a\n * dropped character that follows its last real unit.\n *\n * An offset inside a surrogate pair resolves outward under both biases\n * (start → the pair's character, end → past it), so a clipped astral\n * character is always covered whole. Prefer {@link charRangeForTextOffsets}\n * — the two biases exist to be used as a pair.\n */\nexport function charBoundaryAtTextOffset(\n snapshot: PageTextSnapshot,\n offset: number,\n bias: CharBoundaryBias,\n): number {\n const target = clampInt(offset, 0, snapshot.text.length);\n let lo = 0;\n let hi = snapshot.charCount;\n if (bias === 'start') {\n // Greatest boundary with B(boundary) <= target. B(0) = 0 <= target always.\n while (lo < hi) {\n const mid = lo + Math.ceil((hi - lo) / 2);\n if (boundaryTextOffset(snapshot, mid) <= target) lo = mid;\n else hi = mid - 1;\n }\n return lo;\n }\n // Smallest boundary with B(boundary) >= target. B(charCount) = text.length\n // >= target always (offset is clamped).\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (boundaryTextOffset(snapshot, mid) >= target) hi = mid;\n else lo = mid + 1;\n }\n return lo;\n}\n\n/**\n * The character range whose text projection is exactly the string range\n * `offsets` (`text.slice(offsets.start, offsets.start + offsets.count)`) —\n * the inverse used by search-hit conversion and select-by-text. Start is\n * right-biased and end left-biased (see {@link charBoundaryAtTextOffset}), so\n * zero-width characters adjacent to the range fall outside it on both sides.\n *\n * A non-empty text range can never invert: `end > start` implies\n * `B(end) >= endOffset > startOffset >= B(start)`, which forces\n * `end > start` by monotonicity. An empty input normalizes to the end-rule\n * boundary of its start, with `count` 0.\n */\nexport function charRangeForTextOffsets(snapshot: PageTextSnapshot, offsets: TextRange): TextRange {\n const startOffset = offsets.start;\n const endOffset = offsets.start + offsets.count;\n if (endOffset <= startOffset) {\n return { start: charBoundaryAtTextOffset(snapshot, startOffset, 'end'), count: 0 };\n }\n const start = charBoundaryAtTextOffset(snapshot, startOffset, 'start');\n return { start, count: charBoundaryAtTextOffset(snapshot, endOffset, 'end') - start };\n}\n\n/**\n * The copy primitive: the text of a character range. Dropped characters\n * inside the range contribute nothing; a supplementary character contributes\n * its full surrogate pair.\n * `sliceText(s, { start: 0, count: s.charCount }) === s.text` always.\n */\nexport function sliceText(snapshot: PageTextSnapshot, range: TextRange): string {\n if (range.count <= 0) return '';\n return snapshot.text.slice(\n boundaryTextOffset(snapshot, range.start),\n boundaryTextOffset(snapshot, range.start + range.count),\n );\n}\n\n/**\n * First invariant violation of a snapshot's char map, or null when valid.\n * Shared by the wire schema (rejects malformed bodies) and engine builders\n * (dev-mode assertion). The invariants:\n *\n * 1. absent/empty map ⇒ identity, so `charCount === text.length`;\n * 2. anchor chars strictly increasing, each in [1, charCount];\n * 3. step rule: against the previous anchor (implicit head [0, 0]), the\n * character just before each anchor contributed exactly 0 units\n * (dropped) or 2 (surrogate pair):\n * `t₂ - (t₁ + (c₂ - c₁ - 1)) ∈ {0, 2}`;\n * 4. tail: `lastText + (charCount - lastChar) === text.length`\n * (equivalently `boundaryTextOffset(s, charCount) === text.length`).\n */\nexport function charMapViolation(\n charCount: number,\n textLength: number,\n charMap: ReadonlyArray<CharMapAnchor> | undefined,\n): string | null {\n if (!charMap || charMap.length === 0) {\n return charCount === textLength\n ? null\n : `identity map requires charCount (${charCount}) === text length (${textLength})`;\n }\n let prevChar = 0;\n let prevText = 0;\n for (let i = 0; i < charMap.length; i++) {\n const [char, text] = charMap[i];\n if (!Number.isInteger(char) || !Number.isInteger(text)) {\n return `anchor ${i} is not integer-valued`;\n }\n if (char <= prevChar) {\n return `anchor ${i} char ${char} not strictly after ${prevChar}`;\n }\n if (char > charCount) {\n return `anchor ${i} char ${char} outside [1, ${charCount}]`;\n }\n const step = text - (prevText + (char - prevChar - 1));\n if (step !== 0 && step !== 2) {\n return `anchor ${i} implies a character contributing ${step} text units (expected 0 or 2)`;\n }\n prevChar = char;\n prevText = text;\n }\n const tail = prevText + (charCount - prevChar);\n if (tail !== textLength) {\n return `map projects charCount to ${tail}, but text length is ${textLength}`;\n }\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"charmap-aYlefQP9.cjs","names":["EngineError","EngineErrorCode","annotationKey"],"sources":["../src/dto/AnnotationRender.ts","../src/resource/base64.ts","../src/text/charmap.ts"],"sourcesContent":["import { EngineError } from '../errors/EngineError';\nimport { EngineErrorCode } from '../errors/EngineErrorCode';\nimport type {\n PageImageHandle,\n PageNetworkRenderFormat,\n PageRaster,\n PageScreenViewport,\n} from './PageRender';\nimport type { PdfRotation } from '../geometry/primitives';\nimport { annotationKey } from '../identity/annotationKey';\nimport type { AnnotationRef } from '../identity/AnnotationRef';\nimport type { PageRef } from '../identity/PageRef';\nimport type { Coordinates, PageCoordinates } from '../pageSpace/coordinates';\n\n/**\n * Which `/AP` entry an appearance is: Normal (`/N`), shown at rest;\n * Rollover (`/R`), while the pointer is over the annotation; Down (`/D`),\n * while it is pressed (ISO 32000-2 §12.5.5).\n */\nexport type AnnotationAppearanceMode = 'normal' | 'rollover' | 'down';\n\n/** Every appearance mode, in the one order the engine renders and names them. */\nexport const ANNOTATION_APPEARANCE_MODES: readonly AnnotationAppearanceMode[] = [\n 'normal',\n 'rollover',\n 'down',\n];\n\n/**\n * The modes a request asks for, each once and in\n * {@link ANNOTATION_APPEARANCE_MODES} order; `undefined` when it asks for\n * every mode, whether by leaving `modes` out or by naming all three, so both\n * are one request (and one cached image set). An empty list asks for nothing\n * and is refused with `InvalidArg`.\n */\nexport function appearanceModesOf(\n modes: readonly AnnotationAppearanceMode[] | undefined,\n): readonly AnnotationAppearanceMode[] | undefined {\n if (modes === undefined) return undefined;\n if (modes.length === 0) {\n throw new EngineError(EngineErrorCode.InvalidArg, 'modes must name at least one mode', {\n details: { field: 'modes' },\n });\n }\n const wanted = ANNOTATION_APPEARANCE_MODES.filter((mode) => modes.includes(mode));\n return wanted.length === ANNOTATION_APPEARANCE_MODES.length ? undefined : wanted;\n}\n\n/**\n * Options for batch-rendering a page's annotation appearance streams.\n * Narrower than `PageRenderOptions`: each bitmap is sized to its\n * annotation's own `/Rect`, so there is no target.\n */\nexport interface AnnotationAppearanceRenderOptions {\n /**\n * The size, as for a page render: the appearances come out at the scale\n * the page would have at this viewport, so passing a page image's\n * viewport makes them match it. `{ kind: 'width' }` is the width of the\n * whole (turned) page. Default `{ kind: 'scale', scale: 1 }`.\n */\n viewport?: PageScreenViewport;\n /** Page rotation in degrees clockwise. Default `0`. */\n rotation?: PdfRotation;\n /**\n * Which appearance modes to render, of those each annotation stores.\n * Default: every mode it stores, so one call gives the look at rest, under\n * the pointer and pressed. A mode an annotation doesn't store gives no\n * image for it. Pass `['normal']` for the look at rest only. An empty list\n * is refused with `InvalidArg`.\n */\n modes?: AnnotationAppearanceMode[];\n /**\n * Output-pixel budget per appearance — same semantics as\n * `PageRenderOptions.maxOutputPixels`: appearances are sized by\n * `rect × scale` (the viewport's scale), and a page-sized stamp at a high scale is the same\n * memory bomb a full-page render is. Server requests carry the\n * deployment policy's budget; local callers omit it unless configured.\n */\n maxOutputPixels?: number;\n}\n\n/**\n * Encoded-output options for the cacheable cloud HTTP endpoint. Extends\n * the worker render options with image-encoding controls, mirroring the\n * `PageRenderOptions` -> `PageImageOptions` split.\n */\nexport interface AnnotationAppearanceImageOptions extends AnnotationAppearanceRenderOptions {\n format?: PageNetworkRenderFormat;\n /** WebP quality from 0 (smallest) to 1 (best); PNG ignores it. */\n quality?: number;\n}\n\n/**\n * HTTP/token shape: encoded options plus the version field that makes the\n * response content-addressed and CDN-cacheable.\n *\n * Only `annotationVersion` matters here: an appearance bitmap is rendered\n * purely from the annotation's own `/AP` stream, so it changes iff the\n * annotation changes. Page base content (`contentVersion`/`docVersion`) does\n * not affect appearances and is deliberately not part of the cache key —\n * same as the annotation list endpoint. Which state an annotation shows\n * (`/AS`) is not part of it either: every state is rendered, so switching a\n * check box changes no image.\n */\nexport interface AnnotationAppearancesQuery {\n options: AnnotationAppearanceImageOptions;\n annotationVersion?: number;\n}\n\n/**\n * The widget twin of {@link AnnotationAppearancesQuery}: a page's widget\n * images change exactly when its widgets do, so `widgetVersion` keys them.\n */\nexport interface WidgetAppearancesQuery {\n options: AnnotationAppearanceImageOptions;\n widgetVersion?: number;\n}\n\n/**\n * One rendered appearance: the raw RGBA raster plus the metadata needed to\n * position and identify it. `rect` is the placement box in page space, so\n * the consumer can place the bitmap without a second read.\n *\n * Rotation convention (`appearanceTurnOf`): a box kind (square, circle,\n * free text, stamp, caret) drawn turned, whose drawing stays inside the\n * turned box, renders turned back upright and `rect` is its `box`; the\n * consumer re-applies the DTO's `rotation`, degrees clockwise, as a view\n * transform about the box centre (CSS `rotate()` turns the same way), which\n * makes the raster rotation-invariant (rotating never re-renders).\n * Everything else — lines, polygons and ink, drawn with their points turned,\n * a free-text callout, a turned drawing that reaches past its box,\n * and appearances with any other matrix — renders as-is with `rect` =\n * `/Rect` and needs no transform.\n *\n * An annotation with no normal appearance in the file (`hasAppearance`\n * false) renders as the engine draws it in memory, never written: `rect` is\n * the box that drawing takes, which can reach past `/Rect` (a line's\n * arrowhead on a `/Rect` with no height). One the engine can't draw without\n * writing an appearance has no raster.\n */\nexport interface AnnotationAppearanceRaster<C extends Coordinates = PageCoordinates> {\n /** The annotation's name, for every annotation with an appearance. */\n ref: AnnotationRef;\n mode: AnnotationAppearanceMode;\n /**\n * The state this image draws, when the mode stores several (a check box's\n * `Off` and its on state): every state comes back, whatever `/AS` says,\n * and the annotation's `appearanceState` (or its form field's value) says\n * which one shows. `null` when the mode is a single appearance.\n */\n state: string | null;\n rect: C['box'];\n raster: PageRaster;\n}\n\n/**\n * Batch result for one page: the page, and every rendered appearance, keyed\n * implicitly by `ref` on each entry.\n */\nexport interface AnnotationAppearancesResult<C extends Coordinates = PageCoordinates> {\n page: PageRef;\n appearances: AnnotationAppearanceRaster<C>[];\n}\n\n/**\n * Encoded counterpart of {@link AnnotationAppearanceRaster}: the same\n * identity/placement metadata, but the RGBA raster has been run through an\n * image encoder into a lazily-fetched `PageImageHandle` (PNG/WebP). This is\n * what both the local engine's `renderAppearances()` and the cloud\n * client (decoding the multipart parts) produce.\n */\nexport interface AnnotationAppearanceImage<C extends Coordinates = PageCoordinates> {\n ref: AnnotationRef;\n mode: AnnotationAppearanceMode;\n /** The state this image draws — see {@link AnnotationAppearanceRaster}. */\n state: string | null;\n /** Placement box (unrotated for rotation-stripped renders) — see\n * {@link AnnotationAppearanceRaster}. */\n rect: C['box'];\n image: PageImageHandle;\n}\n\n/**\n * Batch encoded result for one page — image-handle analogue of\n * {@link AnnotationAppearancesResult}.\n */\nexport interface AnnotationAppearanceImagesResult<C extends Coordinates = PageCoordinates> {\n page: PageRef;\n appearances: AnnotationAppearanceImage<C>[];\n}\n\n/**\n * One appearance in the cloud appearance endpoint's `body` part: which part\n * carries its image (`resources.image`, the key of a `resource:<key>` part),\n * plus what the client needs to place and identify it without a second\n * round-trip. The client finds the image by its key and the annotation by\n * `ref`, so every annotation with an appearance stream is listed.\n */\nexport interface AnnotationAppearanceBatchEntry<C extends Coordinates = PageCoordinates> {\n /** The image's key: its bytes are the part `resource:<key>`. */\n resources: { image: string };\n ref: AnnotationRef;\n mode: AnnotationAppearanceMode;\n state: string | null;\n rect: C['box'];\n width: number;\n height: number;\n format: PageNetworkRenderFormat;\n contentType: string;\n}\n\n/**\n * The `body` part of the appearance multipart response. The other parts are\n * the encoded images, one per `appearances[i].resources.image`.\n */\nexport interface AnnotationAppearanceBatch<C extends Coordinates = PageCoordinates> {\n page: PageRef;\n appearances: AnnotationAppearanceBatchEntry<C>[];\n}\n\n/**\n * The pictures that show the page as it is now: each annotation's look at\n * rest (`normal`), in the state it shows. An appearance batch holds every\n * look and state an annotation stores; this picks, for each annotation, the\n * one a page shows: the state its `appearanceState` names, or `Off` when it\n * names none, as PDFium draws it. A state with no picture shows nothing.\n */\nexport function shownAppearances<\n Appearance extends {\n readonly ref: AnnotationRef;\n readonly mode: AnnotationAppearanceMode;\n readonly state: string | null;\n },\n>(\n appearances: readonly Appearance[],\n annotations: readonly { readonly ref: AnnotationRef; readonly appearanceState: string | null }[],\n): Appearance[] {\n const shownState = new Map<string, string>();\n for (const annotation of annotations) {\n shownState.set(annotationKey(annotation.ref), annotation.appearanceState ?? 'Off');\n }\n return appearances.filter(\n (appearance) =>\n appearance.mode === 'normal' &&\n (appearance.state === null ||\n appearance.state === (shownState.get(annotationKey(appearance.ref)) ?? 'Off')),\n );\n}\n","/**\n * Standard base64 (RFC 4648 §4), dependency-free: no Buffer, the same in\n * browsers, workers and Node. Encoding and decoding write into one\n * preallocated buffer, so a resource of tens of megabytes costs one pass.\n */\n\nconst BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\nconst PADDING = 0x3d; // '='\n\nconst ENCODE = Uint8Array.from(BASE64_ALPHABET, (character) => character.charCodeAt(0));\nconst DECODE = (() => {\n const table = new Int16Array(128).fill(-1);\n for (let i = 0; i < BASE64_ALPHABET.length; i++) table[BASE64_ALPHABET.charCodeAt(i)] = i;\n return table;\n})();\n\n/** Padded base64 of `bytes`. */\nexport function toBase64(bytes: Uint8Array): string {\n const out = new Uint8Array(4 * Math.ceil(bytes.length / 3));\n let written = 0;\n let i = 0;\n for (; i + 2 < bytes.length; i += 3) {\n const triple = (bytes[i]! << 16) | (bytes[i + 1]! << 8) | bytes[i + 2]!;\n out[written++] = ENCODE[triple >> 18]!;\n out[written++] = ENCODE[(triple >> 12) & 0x3f]!;\n out[written++] = ENCODE[(triple >> 6) & 0x3f]!;\n out[written++] = ENCODE[triple & 0x3f]!;\n }\n const rest = bytes.length - i;\n if (rest > 0) {\n const triple = (bytes[i]! << 16) | (rest === 2 ? bytes[i + 1]! << 8 : 0);\n out[written++] = ENCODE[triple >> 18]!;\n out[written++] = ENCODE[(triple >> 12) & 0x3f]!;\n out[written++] = rest === 2 ? ENCODE[(triple >> 6) & 0x3f]! : PADDING;\n out[written++] = PADDING;\n }\n return new TextDecoder().decode(out);\n}\n\n/**\n * The number of bytes `encoded` decodes to, without decoding it, or `null`\n * when it can't be base64. Padding is optional, as {@link fromBase64} reads it.\n */\nexport function decodedLengthOf(encoded: string): number | null {\n const end = unpaddedLengthOf(encoded);\n if (end % 4 === 1) return null;\n return Math.floor((end * 3) / 4);\n}\n\n/** The bytes padded or unpadded base64 holds; throws on anything else. */\nexport function fromBase64(encoded: string): Uint8Array {\n const end = unpaddedLengthOf(encoded);\n if (end % 4 === 1) throw new Error('malformed base64');\n const out = new Uint8Array(Math.floor((end * 3) / 4));\n let written = 0;\n let buffer = 0;\n let bits = 0;\n for (let i = 0; i < end; i++) {\n const code = encoded.charCodeAt(i);\n const value = code < 128 ? DECODE[code]! : -1;\n if (value < 0) throw new Error('malformed base64');\n buffer = ((buffer << 6) | value) & 0xffffff;\n bits += 6;\n if (bits >= 8) {\n bits -= 8;\n out[written++] = (buffer >> bits) & 0xff;\n }\n }\n return out;\n}\n\n// Trailing padding, scanned backward to avoid regex backtracking on long runs.\nfunction unpaddedLengthOf(encoded: string): number {\n let end = encoded.length;\n while (end > 0 && encoded.charCodeAt(end - 1) === PADDING) end -= 1;\n return end;\n}\n","import type { TextRange } from './TextRange';\nimport type { PageTextSnapshot } from '../dto/PageTextSnapshot';\n\n/**\n * Character↔text index translation — the one place the two text index\n * spaces of a page meet.\n *\n * A page has two index spaces:\n *\n * - character space: PDFium's internal character list, the space geometry\n * runs tile (`PageGeometryRun.start`), hit-testing addresses, and\n * selection ranges live in. Size: `PageTextSnapshot.charCount`.\n * - text space: UTF-16 code-unit offsets into the extracted string\n * `PageTextSnapshot.text` — what search matching and slicing operate on.\n *\n * They diverge in exactly two ways, both encoded by `charMap` anchors:\n * - a non-printing character occupies a character slot but contributes\n * zero text units (PDFium's `char_indices_` skip list);\n * - a supplementary-plane character contributes two text units (a\n * surrogate pair) once extraction is UTF-16-faithful.\n *\n * `charMap` semantics (see {@link CharMapAnchor}): an implicit head anchor\n * `[0, 0]`; between anchors every character advances the text by exactly one\n * unit; an anchor sits at `i + 1` whenever character `i` contributed\n * something other than one unit. Absent/empty map = identity\n * (`charCount === text.length`). `charMapViolation` is the validator both\n * the wire schema and engine builders share — malformed maps are rejected,\n * never silently repaired.\n *\n * Boundary model: a \"character boundary\" `c` ∈ [0, charCount] sits before\n * character `c`; `boundaryTextOffset(s, charCount) === text.length` always.\n * All ranges are half-open in both spaces.\n */\n\n/**\n * One anchor of the character→text mapping: from character index `char`\n * onward (until the next anchor), boundary `c` maps to text offset\n * `text + (c - char)`.\n */\nexport type CharMapAnchor = readonly [char: number, text: number];\n\n/** Direction to resolve a text offset that falls on a zero-width plateau —\n * see {@link charBoundaryAtTextOffset}. */\nexport type CharBoundaryBias = 'start' | 'end';\n\nconst clampInt = (value: number, lo: number, hi: number): number =>\n Math.min(Math.max(Math.floor(value), lo), hi);\n\n/** The greatest anchor with `anchor.char <= boundary` (implicit head [0,0]). */\nfunction anchorAtOrBefore(map: ReadonlyArray<CharMapAnchor>, boundary: number): CharMapAnchor {\n let lo = 0;\n let hi = map.length - 1;\n let best: CharMapAnchor | null = null;\n while (lo <= hi) {\n const mid = (lo + hi) >> 1;\n if (map[mid][0] <= boundary) {\n best = map[mid];\n lo = mid + 1;\n } else {\n hi = mid - 1;\n }\n }\n return best ?? [0, 0];\n}\n\n/**\n * Text offset of character boundary `boundary` (clamped into\n * [0, charCount]). For a dropped character this is the offset of the next\n * real text unit — exactly what half-open slicing needs at either end.\n */\nexport function boundaryTextOffset(snapshot: PageTextSnapshot, boundary: number): number {\n const b = clampInt(boundary, 0, snapshot.charCount);\n const map = snapshot.charMap;\n if (!map || map.length === 0) return Math.min(b, snapshot.text.length);\n const [char, text] = anchorAtOrBefore(map, b);\n return text + (b - char);\n}\n\n/**\n * The character boundary for a text offset, with explicit plateau bias:\n *\n * - `'start'`: the greatest boundary whose text offset is <= `offset` —\n * scans forward past zero-width characters, so a range start never\n * includes a dropped character that precedes its first real unit.\n * - `'end'`: the smallest boundary whose text offset is >= `offset` —\n * stops before zero-width characters, so a range end never swallows a\n * dropped character that follows its last real unit.\n *\n * An offset inside a surrogate pair resolves outward under both biases\n * (start → the pair's character, end → past it), so a clipped astral\n * character is always covered whole. Prefer {@link charRangeForTextOffsets}\n * — the two biases exist to be used as a pair.\n */\nexport function charBoundaryAtTextOffset(\n snapshot: PageTextSnapshot,\n offset: number,\n bias: CharBoundaryBias,\n): number {\n const target = clampInt(offset, 0, snapshot.text.length);\n let lo = 0;\n let hi = snapshot.charCount;\n if (bias === 'start') {\n // Greatest boundary with B(boundary) <= target. B(0) = 0 <= target always.\n while (lo < hi) {\n const mid = lo + Math.ceil((hi - lo) / 2);\n if (boundaryTextOffset(snapshot, mid) <= target) lo = mid;\n else hi = mid - 1;\n }\n return lo;\n }\n // Smallest boundary with B(boundary) >= target. B(charCount) = text.length\n // >= target always (offset is clamped).\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (boundaryTextOffset(snapshot, mid) >= target) hi = mid;\n else lo = mid + 1;\n }\n return lo;\n}\n\n/**\n * The character range whose text projection is exactly the string range\n * `offsets` (`text.slice(offsets.start, offsets.start + offsets.count)`) —\n * the inverse used by search-hit conversion and select-by-text. Start is\n * right-biased and end left-biased (see {@link charBoundaryAtTextOffset}), so\n * zero-width characters adjacent to the range fall outside it on both sides.\n *\n * A non-empty text range can never invert: `end > start` implies\n * `B(end) >= endOffset > startOffset >= B(start)`, which forces\n * `end > start` by monotonicity. An empty input normalizes to the end-rule\n * boundary of its start, with `count` 0.\n */\nexport function charRangeForTextOffsets(snapshot: PageTextSnapshot, offsets: TextRange): TextRange {\n const startOffset = offsets.start;\n const endOffset = offsets.start + offsets.count;\n if (endOffset <= startOffset) {\n return { start: charBoundaryAtTextOffset(snapshot, startOffset, 'end'), count: 0 };\n }\n const start = charBoundaryAtTextOffset(snapshot, startOffset, 'start');\n return { start, count: charBoundaryAtTextOffset(snapshot, endOffset, 'end') - start };\n}\n\n/**\n * The copy primitive: the text of a character range. Dropped characters\n * inside the range contribute nothing; a supplementary character contributes\n * its full surrogate pair.\n * `sliceText(s, { start: 0, count: s.charCount }) === s.text` always.\n */\nexport function sliceText(snapshot: PageTextSnapshot, range: TextRange): string {\n if (range.count <= 0) return '';\n return snapshot.text.slice(\n boundaryTextOffset(snapshot, range.start),\n boundaryTextOffset(snapshot, range.start + range.count),\n );\n}\n\n/**\n * First invariant violation of a snapshot's char map, or null when valid.\n * Shared by the wire schema (rejects malformed bodies) and engine builders\n * (dev-mode assertion). The invariants:\n *\n * 1. absent/empty map ⇒ identity, so `charCount === text.length`;\n * 2. anchor chars strictly increasing, each in [1, charCount];\n * 3. step rule: against the previous anchor (implicit head [0, 0]), the\n * character just before each anchor contributed exactly 0 units\n * (dropped) or 2 (surrogate pair):\n * `t₂ - (t₁ + (c₂ - c₁ - 1)) ∈ {0, 2}`;\n * 4. tail: `lastText + (charCount - lastChar) === text.length`\n * (equivalently `boundaryTextOffset(s, charCount) === text.length`).\n */\nexport function charMapViolation(\n charCount: number,\n textLength: number,\n charMap: ReadonlyArray<CharMapAnchor> | undefined,\n): string | null {\n if (!charMap || charMap.length === 0) {\n return charCount === textLength\n ? null\n : `identity map requires charCount (${charCount}) === text length (${textLength})`;\n }\n let prevChar = 0;\n let prevText = 0;\n for (let i = 0; i < charMap.length; i++) {\n const [char, text] = charMap[i];\n if (!Number.isInteger(char) || !Number.isInteger(text)) {\n return `anchor ${i} is not integer-valued`;\n }\n if (char <= prevChar) {\n return `anchor ${i} char ${char} not strictly after ${prevChar}`;\n }\n if (char > charCount) {\n return `anchor ${i} char ${char} outside [1, ${charCount}]`;\n }\n const step = text - (prevText + (char - prevChar - 1));\n if (step !== 0 && step !== 2) {\n return `anchor ${i} implies a character contributing ${step} text units (expected 0 or 2)`;\n }\n prevChar = char;\n prevText = text;\n }\n const tail = prevText + (charCount - prevChar);\n if (tail !== textLength) {\n return `map projects charCount to ${tail}, but text length is ${textLength}`;\n }\n return null;\n}\n"],"mappings":"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"}