@embedpdf/engine-core 3.0.0-next.13 → 3.0.0-next.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AbortError-DHqPBnYc.js → AbortError-DwuHPK_I.js} +2 -2
- package/dist/{AbortError-DHqPBnYc.js.map → AbortError-DwuHPK_I.js.map} +1 -1
- package/dist/{AbortError-Cq2igpZ6.cjs → AbortError-P0RU0Mgg.cjs} +4 -4
- package/dist/{AbortError-Cq2igpZ6.cjs.map → AbortError-P0RU0Mgg.cjs.map} +1 -1
- package/dist/{AnnotationListSnapshot-D3mFut1e.d.cts → AnnotationListSnapshot-D9R5n2Kf.d.cts} +237 -7
- package/dist/{AnnotationListSnapshot-D3mFut1e.d.ts → AnnotationListSnapshot-D9R5n2Kf.d.ts} +237 -7
- package/dist/{DocumentSecurityService-ClTZc0zO.d.cts → DocumentSecurityService-CIx1MT0T.d.cts} +2 -2
- package/dist/{DocumentSecurityService-_JRjqvoI.d.ts → DocumentSecurityService-Ca6EK3-3.d.ts} +2 -2
- package/dist/{Engine-Bx0NOfmd.d.cts → Engine-EWWHhEIi.d.ts} +17 -4
- package/dist/{Engine-DxtpCYcP.d.ts → Engine-FNaAEjfb.d.cts} +17 -4
- package/dist/conformance.cjs +382 -95
- package/dist/conformance.cjs.map +1 -1
- package/dist/conformance.d.cts +7 -3
- package/dist/conformance.d.ts +7 -3
- package/dist/conformance.js +302 -16
- package/dist/conformance.js.map +1 -1
- package/dist/derive-BbsDk_Kz.cjs +880 -0
- package/dist/derive-BbsDk_Kz.cjs.map +1 -0
- package/dist/derive-DAFJolWu.js +647 -0
- package/dist/derive-DAFJolWu.js.map +1 -0
- package/dist/{PageInsertBlankInput-cHq9-i-z.js → format-Bbphf4zR.js} +59 -2
- package/dist/format-Bbphf4zR.js.map +1 -0
- package/dist/{PageInsertBlankInput-CqXBmFhN.cjs → format-D_epWlob.cjs} +70 -1
- package/dist/format-D_epWlob.cjs.map +1 -0
- package/dist/index.cjs +127 -107
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/runtime.cjs +137 -117
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.cts +29 -5
- package/dist/runtime.d.ts +29 -5
- package/dist/runtime.js +5 -5
- package/dist/{schemas-BLARNCgx.js → schemas-CFQGvWiu.js} +134 -4
- package/dist/schemas-CFQGvWiu.js.map +1 -0
- package/dist/{schemas-DIqFMmrC.cjs → schemas-D0g6tKa9.cjs} +250 -24
- package/dist/schemas-D0g6tKa9.cjs.map +1 -0
- package/dist/{builders-Bqm8yTpi.cjs → viewport-BWeNHZmb.cjs} +71 -132
- package/dist/viewport-BWeNHZmb.cjs.map +1 -0
- package/dist/{builders-Dqo2X-dt.js → viewport-CvMcadfQ.js} +39 -88
- package/dist/viewport-CvMcadfQ.js.map +1 -0
- package/dist/wire.cjs +25 -2
- package/dist/wire.cjs.map +1 -1
- package/dist/wire.d.cts +2739 -8
- package/dist/wire.d.ts +2739 -8
- package/dist/wire.js +10 -3
- package/dist/wire.js.map +1 -1
- package/package.json +1 -1
- package/dist/PageInsertBlankInput-CqXBmFhN.cjs.map +0 -1
- package/dist/PageInsertBlankInput-cHq9-i-z.js.map +0 -1
- package/dist/builders-Bqm8yTpi.cjs.map +0 -1
- package/dist/builders-Dqo2X-dt.js.map +0 -1
- package/dist/fold-2V29cxCV.cjs +0 -447
- package/dist/fold-2V29cxCV.cjs.map +0 -1
- package/dist/fold-CdEJTBTn.js +0 -334
- package/dist/fold-CdEJTBTn.js.map +0 -1
- package/dist/schemas-BLARNCgx.js.map +0 -1
- package/dist/schemas-DIqFMmrC.cjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive-DAFJolWu.js","names":["positive"],"sources":["../src/geometry/convert.ts","../src/dto/PageGeometrySnapshot.ts","../src/errors/EngineError.ts","../src/annotation/relationships.ts","../src/search/fold.ts","../src/measure/validate.ts","../src/measure/units.ts","../src/measure/calibrate.ts","../src/measure/compute.ts","../src/measure/derive.ts"],"sourcesContent":["/**\n * PDF-space-internal geometry helpers.\n *\n * These operate ENTIRELY within PDF user space (y-up, edges). They never\n * produce viewer-local (y-down) geometry — that conversion needs the crop box\n * plus rotation/scale and lives in the v3 viewer layer (the `Mat2D` matrix\n * model), never here.\n */\n\nimport type { PdfPoint, PdfQuad, PdfRect, PdfSize } from './primitives';\n\n/** Origin + size form of a `PdfRect`, still y-up (origin = bottom-left). */\nexport interface PdfOriginSize {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Normalize a `PdfRect` to the y-up invariant (`left <= right`, `bottom <= top`),\n * regardless of how the PDF ordered the corners (a `/Rect` array or `FS_RECTF`\n * may carry them in any order). THE enforcement point for the \"PdfRect is always\n * normalized\" rule: every producer that reads a raw box from PDFium passes it\n * through here, so the invariant holds once, centrally — downstream `width =\n * right - left` is never negative.\n */\nexport function normalizePdfRect(r: PdfRect): PdfRect {\n return {\n left: Math.min(r.left, r.right),\n right: Math.max(r.left, r.right),\n bottom: Math.min(r.top, r.bottom),\n top: Math.max(r.top, r.bottom),\n };\n}\n\nexport function pdfRectWidth(r: PdfRect): number {\n return r.right - r.left;\n}\n\nexport function pdfRectHeight(r: PdfRect): number {\n return r.top - r.bottom;\n}\n\nexport function pdfRectSize(r: PdfRect): PdfSize {\n return { width: r.right - r.left, height: r.top - r.bottom };\n}\n\n/** Convert edges -> origin+size, staying y-up (origin at bottom-left). */\nexport function pdfRectToOriginSize(r: PdfRect): PdfOriginSize {\n return { x: r.left, y: r.bottom, width: r.right - r.left, height: r.top - r.bottom };\n}\n\n/** Convert y-up origin+size back to edges. */\nexport function pdfRectFromOriginSize(o: PdfOriginSize): PdfRect {\n return { left: o.x, bottom: o.y, right: o.x + o.width, top: o.y + o.height };\n}\n\n/**\n * Enclosing axis-aligned box of a quad, in PDF user space. Orientation\n * agnostic (correct for rotated/skewed quads) — this is what highlight\n * rendering and hit-testing want.\n */\nexport function pdfQuadBounds(q: PdfQuad): PdfRect {\n const xs = [q.p1.x, q.p2.x, q.p3.x, q.p4.x];\n const ys = [q.p1.y, q.p2.y, q.p3.y, q.p4.y];\n return {\n left: Math.min(...xs),\n right: Math.max(...xs),\n bottom: Math.min(...ys),\n top: Math.max(...ys),\n };\n}\n\n/** Named corners of a quad, in PDF user space (y-up). */\nexport interface PdfQuadCorners {\n topLeft: PdfPoint;\n topRight: PdfPoint;\n bottomLeft: PdfPoint;\n bottomRight: PdfPoint;\n}\n\n/**\n * Axis-aligned named-corner interpretation of a quad.\n *\n * VALID ONLY for upright (non-rotated, non-skewed) quads: it is derived from\n * the enclosing bounds, so for rotated/skewed quads it returns the bounding\n * box corners, not the true geometric corners. For arbitrary quads use the\n * positional `p1..p4` or `pdfQuadBounds` directly.\n */\nexport function pdfQuadCorners(q: PdfQuad): PdfQuadCorners {\n const b = pdfQuadBounds(q);\n return {\n topLeft: { x: b.left, y: b.top },\n topRight: { x: b.right, y: b.top },\n bottomLeft: { x: b.left, y: b.bottom },\n bottomRight: { x: b.right, y: b.bottom },\n };\n}\n\n/**\n * Build a quad from named corners, in PDFium `FS_QUADPOINTSF` slot order\n * (`p1 = topLeft`, `p2 = topRight`, `p3 = bottomLeft`, `p4 = bottomRight`).\n * The inverse of `pdfQuadCorners` for upright quads.\n */\nexport function pdfQuadFromCorners(c: PdfQuadCorners): PdfQuad {\n return { p1: c.topLeft, p2: c.topRight, p3: c.bottomLeft, p4: c.bottomRight };\n}\n","import { pdfQuadBounds } from '../geometry/convert';\nimport type { PdfQuad, PdfRect } from '../geometry/primitives';\n\n/**\n * One upright glyph's geometry in PDF user space (y-up edges).\n *\n * `looseBox` is the loose char cell (pdfium `FPDFText_GetLooseCharBox`): the\n * font-metric box covering the full glyph cell without regard to the actual\n * glyph shape. Always present (zeroed + flagged empty for degenerate glyphs);\n * it's the box selection envelopes are built from.\n *\n * `tightBox` is the tight char box (pdfium `FPDFText_GetCharBox`): the box\n * hugging the actual glyph shape. Optional — absent for empty/whitespace\n * glyphs that have no real outline.\n *\n * `flags` carries slim per-glyph state (bit 1 = space, bit 2 = empty).\n */\nexport interface PageGeometryGlyph {\n looseBox: PdfRect;\n flags: number;\n tightBox?: PdfRect;\n}\n\n/**\n * One non-upright glyph's geometry: the exact oriented cells, in PDF user\n * space (page coordinates — NOT a local frame). The corner SLOTS are\n * frame-geometric in the glyph's own upright frame:\n * `p1` = upper-start, `p2` = upper-end, `p3` = lower-start, `p4` = lower-end,\n * where \"upper\" is the ascent side and \"start\" is the frame's minimum-x side.\n * Deliberately NOT a bidi/reading-order statement — advance direction is a\n * glyph-sequence concern, carried separately where consumers need it.\n *\n * Degenerate glyphs carry a zeroed `looseQuad` plus the empty flag (bit 2),\n * mirroring the upright variant's zeroed-box convention. `flags` bits match\n * {@link PageGeometryGlyph.flags}.\n */\nexport interface RotatedGeometryGlyph {\n looseQuad: PdfQuad;\n flags: number;\n tightQuad?: PdfQuad;\n}\n\n/**\n * A run whose char matrix is upright — byte-identical to the wire shape that\n * predates orientation support, so upright documents never pay for it.\n */\nexport interface UprightGeometryRun {\n rect: PdfRect;\n charStart: number;\n glyphs: PageGeometryGlyph[];\n fontSize?: number;\n}\n\n/**\n * A run whose char matrix is NOT upright (rotated, sheared, or mirrored).\n *\n * `rect` stays a page-space AABB (culling), like every other wire rect.\n * `rotation` is the baseline angle in radians, CCW in PDF y-up space\n * (`atan2(m.b, m.a)` of the run's char matrix). Note a shear-only run has\n * `rotation === 0` and still uses this variant — its cells are\n * parallelograms an AABB would misrepresent. `ascentFlip` is true when the\n * ascent vector maps opposite the rotated frame's +y (mirrored /\n * negative-determinant content).\n */\nexport interface RotatedGeometryRun {\n rect: PdfRect;\n charStart: number;\n glyphs: RotatedGeometryGlyph[];\n rotation: number;\n ascentFlip: boolean;\n fontSize?: number;\n}\n\n/**\n * One text run (contiguous glyphs sharing a text object and orientation).\n * A discriminated union so consumers CANNOT read axis-aligned boxes off\n * rotated text by accident — handling orientation is a compile-time\n * obligation, not a runtime discovery. Narrow with\n * {@link isRotatedGeometryRun}, or use the uniform {@link glyphLooseQuad} /\n * {@link glyphLooseBounds} views.\n */\nexport type PageGeometryRun = UprightGeometryRun | RotatedGeometryRun;\n\n/**\n * Geometry-only text layout for one page, in PDF user space (y-up). The v3\n * viewer converts to content/view space via the page geometry matrix.\n *\n * Pure content, addressed and cached by `contentVersion`. Carries NO\n * annotation liveness envelope (`PageState`) — see `PageTextSnapshot` for\n * the rationale; liveness lives on annotation reads.\n */\nexport interface PageGeometrySnapshot {\n runs: PageGeometryRun[];\n}\n\n/** Narrowing guard: is this run the rotated (non-upright) variant? */\nexport function isRotatedGeometryRun(run: PageGeometryRun): run is RotatedGeometryRun {\n return 'rotation' in run;\n}\n\n/**\n * Uniform oriented-cell view over either run variant: the glyph's loose cell\n * as a quad (synthesized from the box corners when the run is upright, in\n * the same frame-geometric slot order).\n */\nexport function glyphLooseQuad(run: PageGeometryRun, index: number): PdfQuad {\n if (isRotatedGeometryRun(run)) return run.glyphs[index].looseQuad;\n const b = run.glyphs[index].looseBox;\n return {\n p1: { x: b.left, y: b.top },\n p2: { x: b.right, y: b.top },\n p3: { x: b.left, y: b.bottom },\n p4: { x: b.right, y: b.bottom },\n };\n}\n\n/**\n * Uniform AABB view over either run variant: the glyph's loose box, or the\n * enclosing bounds of its oriented cell when the run is rotated. The\n * conservative envelope for consumers that genuinely want a box (culling,\n * scroll targets) — never a substitute for handling orientation in geometry\n * that gets DRAWN.\n */\nexport function glyphLooseBounds(run: PageGeometryRun, index: number): PdfRect {\n if (isRotatedGeometryRun(run)) return pdfQuadBounds(run.glyphs[index].looseQuad);\n return run.glyphs[index].looseBox;\n}\n","import { EngineErrorCode } from './EngineErrorCode';\n\nexport interface EngineErrorOptions {\n cause?: unknown;\n details?: Record<string, unknown>;\n}\n\n/**\n * Single error class shared across engine implementations. Has a stable\n * machine-readable `code` (see EngineErrorCode) so callers can match without\n * sniffing message strings.\n */\nexport class EngineError extends Error {\n override readonly name = 'EngineError';\n readonly code: EngineErrorCode;\n readonly details?: Record<string, unknown>;\n\n constructor(code: EngineErrorCode, message?: string, opts: EngineErrorOptions = {}) {\n super(message ?? code, opts.cause === undefined ? undefined : { cause: opts.cause });\n this.code = code;\n this.details = opts.details;\n }\n\n static is(value: unknown, code?: EngineErrorCode): value is EngineError {\n if (\n !(value instanceof EngineError) &&\n (value as { name?: string } | null)?.name !== 'EngineError'\n ) {\n return false;\n }\n if (code === undefined) return true;\n return (value as EngineError).code === code;\n }\n\n toJSON(): {\n name: 'EngineError';\n code: EngineErrorCode;\n message: string;\n details?: Record<string, unknown>;\n } {\n return {\n name: 'EngineError',\n code: this.code,\n message: this.message,\n details: this.details,\n };\n }\n}\n\nexport interface SerializedEngineError {\n name: 'EngineError';\n code: EngineErrorCode;\n message: string;\n details?: Record<string, unknown>;\n}\n\nexport function serializeError(err: unknown): SerializedEngineError {\n if (EngineError.is(err)) {\n return err.toJSON();\n }\n if (err instanceof Error) {\n const code: EngineErrorCode =\n err.name === 'AbortError' ? EngineErrorCode.Aborted : EngineErrorCode.Unknown;\n return { name: 'EngineError', code, message: err.message };\n }\n return {\n name: 'EngineError',\n code: EngineErrorCode.Unknown,\n message: String(err ?? 'unknown error'),\n };\n}\n\nexport function deserializeError(payload: SerializedEngineError): EngineError {\n return new EngineError(payload.code, payload.message, { details: payload.details });\n}\n","/**\n * Pure, dependency-free composition of the flat annotation list into\n * reply/group threads. No PDFium, no browser, no zod — safe to import from\n * the cloud SDK, the local engine, and any UI plugin.\n *\n * The engine surfaces `/IRT` + `/RT` as flat edges on every DTO\n * ({@link AnnotationBase.inReplyTo} / {@link AnnotationBase.replyType}); it\n * deliberately does NOT nest replies/group members, because each of those\n * is itself a first-class annotation in the page list. This module turns\n * those edges into the shape a comments sidebar wants.\n *\n * See ISO 32000 §12.5.6.2:\n * - `/RT /R` -> a comment-thread reply (default when `/RT` is absent)\n * - `/RT /Group` -> a subordinate part of one logical annotation group\n */\n\nimport type { AnnotationBase } from './base';\nimport type { AnnotationDTO } from './kinds';\nimport type { AnnotationRef } from '../identity/AnnotationRef';\n\n/**\n * Where a single annotation sits in the reply/group taxonomy.\n *\n * 'top-level' no `/IRT` — a primary annotation / thread root.\n * 'reply' `/IRT` + `/RT /R` (or `/RT` absent) — a comment.\n * 'grouped-subordinate' `/IRT` + `/RT /Group` — a visual part of a group.\n */\nexport type AnnotationRelationKind = 'top-level' | 'reply' | 'grouped-subordinate';\n\n/**\n * Classify one annotation by its relationship edge alone. Mirrors the\n * spec's \"practical parser rule\": no `/IRT` is top-level; `/RT /Group` is\n * a grouped subordinate; anything else with an `/IRT` is a reply (the\n * engine has already normalized a missing `/RT` to `'reply'`).\n */\nexport function classifyRelation(\n a: Pick<AnnotationBase, 'inReplyTo' | 'replyType'>,\n): AnnotationRelationKind {\n if (!a.inReplyTo) return 'top-level';\n return a.replyType === 'group' ? 'grouped-subordinate' : 'reply';\n}\n\n/**\n * One composed thread: a primary annotation plus the annotations that\n * point at it.\n *\n * - `groupedParts` (`/RT /Group`) are visual/helper parts of one logical\n * annotation — a sidebar should fold them into the primary, NOT list\n * them as separate comments. Group-level fields (Contents, T, Subj, …)\n * come from the primary; the subordinate's copies are ignored.\n * - `replies` (`/RT /R`) are real comment-thread entries shown threaded\n * under the primary.\n *\n * A primary may legitimately have BOTH (e.g. a StrikeOut with a Caret\n * group part and a Text reply).\n */\nexport interface AnnotationThread<T extends AnnotationDTO = AnnotationDTO> {\n primary: T;\n /** `/RT /Group` children — merge into the primary, don't list separately. */\n groupedParts: T[];\n /** `/RT /R` (or default) children — show as threaded comments. */\n replies: T[];\n}\n\n/**\n * Stable string key for an {@link AnnotationRef}, used to match a child's\n * `inReplyTo` against a candidate parent's `ref`. Both sides are produced\n * by the engine with the same identity precedence (objectNumber, then nm,\n * then index), so equal keys mean \"same annotation\".\n */\nexport function refKey(ref: AnnotationRef): string {\n switch (ref.kind) {\n case 'objectNumber':\n return `obj:${ref.pageObjectNumber}:${ref.annotObjectNumber}`;\n case 'nm':\n return `nm:${ref.pageObjectNumber}:${ref.nm}`;\n case 'index':\n return `idx:${ref.pageObjectNumber}:${ref.index}`;\n }\n}\n\n/**\n * Compose a flat annotation list (one page, or a whole document via\n * `listRawAll()` flattened) into {@link AnnotationThread}s in primary\n * order.\n *\n * Rules (ISO 32000 §12.5.6.2 + the spec's UI rule):\n * - Annotations with no `/IRT` become thread primaries, in input order.\n * - A `/RT /Group` child is attached to its primary's `groupedParts`.\n * - A `/RT /R` (or default) child is attached to its primary's `replies`.\n * - A child whose parent is not in the input set is treated as an\n * orphan primary (defensive — e.g. a partial/filtered page slice).\n *\n * Limitations (v1, matches the legacy stack): threading is one level deep\n * — a reply-to-a-reply is keyed to its direct parent, so it only appears\n * nested if that direct parent is itself a primary. Callers that want a\n * sidebar of \"most annotations, not widgets/popups\" should pre-filter the\n * input; this helper is intentionally unopinionated about eligibility.\n */\nexport function buildThreads(annotations: readonly AnnotationDTO[]): AnnotationThread[] {\n const byKey = new Map<string, AnnotationDTO>();\n for (const a of annotations) {\n byKey.set(refKey(a.ref), a);\n // Index under /NM too, so a child that points at the parent by name\n // still resolves when the parent's own ref is objectNumber-form.\n if (a.nm && a.nm.length > 0) {\n byKey.set(`nm:${a.ref.pageObjectNumber}:${a.nm}`, a);\n }\n }\n\n const threads: AnnotationThread[] = [];\n const threadByPrimaryKey = new Map<string, AnnotationThread>();\n\n const primaryThread = (primary: AnnotationDTO): AnnotationThread => {\n const key = refKey(primary.ref);\n let thread = threadByPrimaryKey.get(key);\n if (!thread) {\n thread = { primary, groupedParts: [], replies: [] };\n threadByPrimaryKey.set(key, thread);\n threads.push(thread);\n }\n return thread;\n };\n\n // Pass 1: every top-level annotation seeds a thread, preserving order.\n for (const a of annotations) {\n if (!a.inReplyTo) primaryThread(a);\n }\n\n // Pass 2: attach children to their primary; orphans become primaries.\n for (const a of annotations) {\n if (!a.inReplyTo) continue;\n const parent = byKey.get(refKey(a.inReplyTo));\n if (!parent || parent.inReplyTo) {\n // Parent missing from the set, or itself a child (one-level-deep\n // limitation): surface the annotation as its own primary so it is\n // never silently dropped.\n primaryThread(a);\n continue;\n }\n const thread = primaryThread(parent);\n if (a.replyType === 'group') thread.groupedParts.push(a);\n else thread.replies.push(a);\n }\n\n return threads;\n}\n","/**\n * Text folding for literal search: the deterministic normalization applied\n * to BOTH the page text and the needle so that \"Café\" finds \"cafe\" and a\n * line-wrapped \"hello\\n world\" finds \"hello world\".\n *\n * Fold version 1:\n * - whitespace runs collapse to a single space (any `\\s`, including the\n * spaces some compatibility decompositions emit) — or are dropped\n * entirely with `dropWhitespace` (ignoreWhitespace),\n * - each code point is NFKD-decomposed (ligatures split: \"fi\" → \"fi\",\n * \"²\" → \"2\"),\n * - combining marks are stripped unless `keepMarks`,\n * - case is folded via upper→lower round-trip unless `keepCase` (this\n * poor-man's full fold catches \"ß\"→\"ss\" and \"ς\"→\"σ\", which a plain\n * `toLowerCase` misses).\n *\n * Every folded code unit remembers which original code point produced it\n * (`map`), so match ranges found in folded space translate back to exact\n * original-text ranges — the property the whole anchor stage rests on.\n *\n * Pre-folded corpus artifacts store this fold's output; bump\n * `SEARCH_FOLD_VERSION` on ANY semantic change so stored corpora are\n * rebuilt instead of silently mismatching fresh needles.\n */\n\n/** Version stamp for persisted pre-folded corpus artifacts. */\nexport const SEARCH_FOLD_VERSION = 1;\n\nexport interface FoldOptions {\n /** Preserve case (matchCase). */\n keepCase?: boolean;\n /** Preserve combining marks (matchDiacritics). */\n keepMarks?: boolean;\n /** Drop whitespace instead of collapsing it to one space (ignoreWhitespace). */\n dropWhitespace?: boolean;\n}\n\nexport interface FoldedText {\n folded: string;\n /** Folded code unit i → code-unit index of the original code point it came from. */\n map: Uint32Array;\n original: string;\n}\n\n/** A half-open match range in ORIGINAL code-unit space. */\nexport interface SearchMatchRange {\n start: number;\n length: number;\n}\n\nconst WHITESPACE = /\\s/;\nconst MARKS = /\\p{M}/gu;\n\nexport function foldText(original: string, options: FoldOptions = {}): FoldedText {\n const units: string[] = [];\n const map: number[] = [];\n let lastWasSpace = false;\n\n let index = 0;\n for (const cp of original) {\n let piece: string;\n if (WHITESPACE.test(cp)) {\n piece = ' ';\n } else {\n piece = cp.normalize('NFKD');\n if (!options.keepMarks) piece = piece.replace(MARKS, '');\n if (!options.keepCase) piece = piece.toUpperCase().toLowerCase();\n }\n // A decomposition can itself contain whitespace (U+00A8 → space +\n // combining diaeresis), so collapse (or drop) runs at the unit level,\n // not just for source whitespace.\n for (let u = 0; u < piece.length; u++) {\n const unit = piece[u];\n if (WHITESPACE.test(unit)) {\n if (options.dropWhitespace || lastWasSpace) continue;\n units.push(' ');\n map.push(index);\n lastWasSpace = true;\n } else {\n units.push(unit);\n map.push(index);\n lastWasSpace = false;\n }\n }\n index += cp.length;\n }\n\n return { folded: units.join(''), map: Uint32Array.from(map), original };\n}\n\n/** Code-unit length of the code point starting at `index` (1 or 2). */\nfunction codePointLengthAt(text: string, index: number): number {\n const unit = text.charCodeAt(index);\n if (unit >= 0xd800 && unit <= 0xdbff && index + 1 < text.length) {\n const next = text.charCodeAt(index + 1);\n if (next >= 0xdc00 && next <= 0xdfff) return 2;\n }\n return 1;\n}\n\n/**\n * Translate a match found in folded space back to the original text.\n * The range covers whole original code points: a hit on either folded\n * half of \"fi\" spans the full ligature, and a hit ending on a collapsed\n * space extends only through the first whitespace char of the run (the\n * remainder is presentation, not match).\n */\nexport function toOriginalRange(\n folded: FoldedText,\n foldedStart: number,\n foldedLength: number,\n): SearchMatchRange {\n const start = folded.map[foldedStart];\n const lastOriginal = folded.map[foldedStart + foldedLength - 1];\n const end = lastOriginal + codePointLengthAt(folded.original, lastOriginal);\n return { start, length: end - start };\n}\n","import type { PdfMeasure } from '../dto/Measure';\nimport { validNumberFormat } from './format';\n\n/** Shared preflight for HTTP, workers and calibration helpers. Does not write. */\nexport function assertWritableMeasure(value: PdfMeasure): void {\n if (value.subtype !== 'RL') throw new RangeError('Only rectilinear measures can be authored');\n if (value.ratio !== undefined && (typeof value.ratio !== 'string' || value.ratio.includes('\\0')))\n throw new RangeError('Invalid scale ratio');\n for (const key of ['x', 'y', 'distance', 'area', 'angle', 'slope'] as const) {\n const list = value[key];\n if (list === undefined && (key === 'y' || key === 'angle' || key === 'slope')) continue;\n if (!Array.isArray(list)) throw new RangeError(`Missing ${key} formats`);\n for (const nf of list) {\n if (!nf || typeof nf.unit !== 'string' || !validNumberFormat(nf))\n throw new RangeError('Invalid number format');\n if (\n nf.fraction !== undefined &&\n !['decimal', 'fraction', 'round', 'truncate'].includes(nf.fraction)\n )\n throw new RangeError('Invalid fraction mode');\n if (nf.labelPosition !== undefined && !['prefix', 'suffix'].includes(nf.labelPosition))\n throw new RangeError('Invalid label position');\n if (nf.fixed !== undefined && typeof nf.fixed !== 'boolean')\n throw new RangeError('Invalid fixed precision');\n for (const key of [\n 'unit',\n 'thousands',\n 'decimal',\n 'prefixSpacing',\n 'suffixSpacing',\n ] as const) {\n const text = nf[key];\n if (text !== undefined && (typeof text !== 'string' || text.includes('\\0')))\n throw new RangeError('Invalid measurement text');\n }\n }\n }\n if (value.origin !== undefined)\n for (const n of [value.origin.x, value.origin.y]) assertPdfFloat(n);\n if (value.cyx !== undefined) {\n assertPdfFloat(value.cyx);\n if (Math.fround(value.cyx) <= 0) throw new RangeError('Invalid CYX');\n }\n}\nexport function assertPdfFloat(n: number): void {\n if (typeof n !== 'number' || !Number.isFinite(Math.fround(n)))\n throw new RangeError('Value is outside PDF float range');\n}\n","export const METRES = {\n pt: 0.0254 / 72,\n mm: 0.001,\n cm: 0.01,\n m: 1,\n km: 1000,\n in: 0.0254,\n ft: 0.3048,\n yd: 0.9144,\n mi: 1609.344,\n} as const;\nexport type LengthUnit = keyof typeof METRES;\nexport type AreaUnit = `${LengthUnit}2` | 'ha' | 'acre';\nexport const squareOf = (unit: LengthUnit): AreaUnit => `${unit}2`;\nexport function squareMetres(unit: AreaUnit): number {\n return unit === 'ha'\n ? 10000\n : unit === 'acre'\n ? 4046.8564224\n : METRES[unit.slice(0, -1) as LengthUnit] ** 2;\n}\nexport const areaUnitLabel = (unit: AreaUnit): string =>\n unit.endsWith('2') ? unit.slice(0, -1) + '²' : unit;\n","import type { PdfMeasure, PdfNumberFormat } from '../dto/Measure';\nimport { assertWritableMeasure } from './validate';\nimport {\n areaUnitLabel,\n METRES,\n squareMetres,\n squareOf,\n type AreaUnit,\n type LengthUnit,\n} from './units';\n\nexport interface MeasurementFormatOptions {\n /** Decimal precision denominator: 1, 10, 100, ... */\n precision: number;\n fixed?: boolean;\n areaUnit?: AreaUnit;\n imperialFeetInches?: boolean;\n /** Inch fraction denominator when imperialFeetInches is true. Default 16. */\n fractionalPrecision?: number;\n}\nfunction positive(value: number): void {\n if (!Number.isFinite(value) || value <= 0)\n throw new RangeError('Calibration requires finite positive lengths and scales');\n}\nfunction build(\n perPoint: number,\n unit: LengthUnit,\n ratio: string,\n format: MeasurementFormatOptions,\n): PdfMeasure {\n positive(Math.fround(perPoint));\n if (\n !Number.isInteger(Math.log10(format.precision)) ||\n format.precision < 1 ||\n format.precision > 1e9\n )\n throw new RangeError('Invalid decimal precision');\n const areaUnit = format.areaUnit ?? squareOf(unit);\n const common = {\n precision: format.precision,\n ...(format.fixed === undefined ? {} : { fixed: format.fixed }),\n };\n const distance: PdfNumberFormat[] = format.imperialFeetInches\n ? [\n { unit: 'ft', conversion: Math.fround(METRES[unit] / METRES.ft) },\n {\n unit: 'in',\n conversion: 12,\n fraction: 'fraction',\n precision: format.fractionalPrecision ?? 16,\n ...('fixed' in common ? { fixed: common.fixed } : {}),\n },\n ]\n : [{ unit, conversion: 1, ...common }];\n if (\n format.imperialFeetInches &&\n (!Number.isInteger(distance[1].precision) ||\n distance[1].precision! < 1 ||\n distance[1].precision! > 2147483647)\n )\n throw new RangeError('Invalid fraction denominator');\n const measure: PdfMeasure = {\n subtype: 'RL',\n ratio,\n x: [{ unit, conversion: Math.fround(perPoint), ...common }],\n distance,\n area: [\n {\n unit: areaUnitLabel(areaUnit),\n conversion: Math.fround(METRES[unit] ** 2 / squareMetres(areaUnit)),\n ...common,\n },\n ],\n };\n assertWritableMeasure(measure);\n return measure;\n}\nexport function measureFromKnownLength(\n userSpaceLength: number,\n real: { value: number; unit: LengthUnit },\n format: MeasurementFormatOptions = { precision: 100 },\n): PdfMeasure {\n positive(userSpaceLength);\n positive(real.value);\n const perPoint = real.value / userSpaceLength;\n return build(\n perPoint,\n real.unit,\n `1 in = ${Number((perPoint * 72).toPrecision(8))} ${real.unit}`,\n format,\n );\n}\nexport function measureFromRatio(\n paper: number,\n real: number,\n unit: LengthUnit,\n userUnit = 1,\n format: MeasurementFormatOptions = { precision: 100 },\n): PdfMeasure {\n positive(paper);\n positive(real);\n positive(userUnit);\n return build(\n ((userUnit / 72) * 0.0254 * (real / paper)) / METRES[unit],\n unit,\n `${paper}:${real}`,\n format,\n );\n}\n","import type { PdfMeasure } from '../dto/Measure';\nimport type { PdfPoint } from '../geometry/primitives';\n\nexport interface MeasuredScale {\n sx: number;\n sy: number;\n}\nconst positive = (n: number): boolean => Number.isFinite(n) && n > 0;\nexport function scaleOf(measure: PdfMeasure): MeasuredScale | null {\n const sx = Math.fround(measure.x[0]?.conversion ?? NaN);\n if (!positive(sx)) return null;\n if (!measure.y) return { sx, sy: sx };\n const cy = Math.fround(measure.y[0]?.conversion ?? NaN);\n const cyx = Math.fround(measure.cyx ?? NaN);\n const sy = cy * cyx;\n return positive(cy) && positive(cyx) && positive(sy) ? { sx, sy } : null;\n}\n\nexport const measurementPoint = (p: PdfPoint): PdfPoint => ({\n x: Math.fround(p.x),\n y: Math.fround(p.y),\n});\n\nexport function pathLength(\n points: readonly PdfPoint[],\n closed: boolean,\n scale: MeasuredScale,\n): number {\n let total = 0;\n const normalized = points.map(measurementPoint);\n for (let i = 0; i < (closed ? normalized.length : normalized.length - 1); i++) {\n const a = normalized[i],\n b = normalized[(i + 1) % normalized.length];\n total += Math.hypot((b.x - a.x) * scale.sx, (b.y - a.y) * scale.sy);\n }\n return total;\n}\n\nexport function polygonArea(points: readonly PdfPoint[], scale: MeasuredScale): number {\n if (points.length < 3) return 0;\n const normalized = points.map(measurementPoint);\n const origin = normalized[0];\n let twice = 0;\n // Relative coordinates avoid cancellation at a large nonzero page origin.\n for (let i = 1; i < normalized.length - 1; i++) {\n const a = normalized[i],\n b = normalized[i + 1];\n twice += (a.x - origin.x) * (b.y - origin.y) - (b.x - origin.x) * (a.y - origin.y);\n }\n return Math.abs(twice * scale.sx * scale.sy) / 2;\n}\n\n/** A measured area must have one simple, nonzero boundary. Repeated closing\n * vertices are allowed in imported PDFs; crossings and overlapping edges are not. */\nexport function validAreaBoundary(points: readonly PdfPoint[]): boolean {\n const boundary = points.map(measurementPoint);\n const same = (a: PdfPoint, b: PdfPoint) => a.x === b.x && a.y === b.y;\n if (boundary.length > 1 && same(boundary[0], boundary[boundary.length - 1])) {\n boundary.pop();\n }\n if (boundary.length < 3 || polygonArea(boundary, { sx: 1, sy: 1 }) === 0) return false;\n\n const cross = (a: PdfPoint, b: PdfPoint, c: PdfPoint) =>\n (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);\n const onSegment = (a: PdfPoint, b: PdfPoint, p: PdfPoint) =>\n cross(a, b, p) === 0 &&\n p.x >= Math.min(a.x, b.x) &&\n p.x <= Math.max(a.x, b.x) &&\n p.y >= Math.min(a.y, b.y) &&\n p.y <= Math.max(a.y, b.y);\n\n for (let i = 0; i < boundary.length; i++) {\n const a = boundary[i];\n const b = boundary[(i + 1) % boundary.length];\n if (same(a, b)) return false;\n const next = boundary[(i + 2) % boundary.length];\n // Adjacent collinear edges may continue forward, but must not double back.\n const doublesBack = (b.x - a.x) * (next.x - b.x) + (b.y - a.y) * (next.y - b.y) < 0;\n if (cross(a, b, next) === 0 && doublesBack) {\n return false;\n }\n\n for (let j = i + 1; j < boundary.length; j++) {\n if (j === i + 1 || (i === 0 && j === boundary.length - 1)) continue;\n const c = boundary[j];\n const d = boundary[(j + 1) % boundary.length];\n if (onSegment(a, b, c) || onSegment(a, b, d) || onSegment(c, d, a) || onSegment(c, d, b)) {\n return false;\n }\n if (\n Math.sign(cross(a, b, c)) !== Math.sign(cross(a, b, d)) &&\n Math.sign(cross(c, d, a)) !== Math.sign(cross(c, d, b))\n ) {\n return false;\n }\n }\n }\n return true;\n}\n","import type { PdfMeasurement } from '../dto/Measure';\nimport type { LinePoints, PdfPoint } from '../geometry/primitives';\nimport { pathLength, polygonArea, scaleOf, validAreaBoundary } from './compute';\nimport { formatMeasurement, validNumberFormat } from './format';\n\nexport interface DimensionInput {\n subtype: string;\n intent?: string | null;\n measure?: PdfMeasurement | null;\n linePoints?: LinePoints;\n vertices?: PdfPoint[];\n contents?: string | null;\n}\nexport interface MeasurementReadout {\n kind: 'distance' | 'perimeter' | 'area';\n label: string;\n /** Value in the first display format's unit. */\n value: number;\n perimeter?: string;\n}\nexport interface MeasurementUnavailable {\n unavailable:\n | 'not-dimension'\n | 'no-measure'\n | 'foreign-measure'\n | 'no-scale'\n | 'no-distance-format'\n | 'no-area-format'\n | 'invalid-format'\n | 'invalid-geometry';\n}\nexport function isDimension(dto: { subtype: string; intent?: string | null }): boolean {\n return (\n (dto.subtype === 'line' && dto.intent === 'LineDimension') ||\n (dto.subtype === 'polyline' && dto.intent === 'PolyLineDimension') ||\n (dto.subtype === 'polygon' && dto.intent === 'PolygonDimension')\n );\n}\nexport const isReadout = (\n r: MeasurementReadout | MeasurementUnavailable,\n): r is MeasurementReadout => !('unavailable' in r);\n\nexport function measurementReadout(\n dto: DimensionInput,\n): MeasurementReadout | MeasurementUnavailable {\n if (!isDimension(dto)) return { unavailable: 'not-dimension' };\n if (!dto.measure) return { unavailable: 'no-measure' };\n if (dto.measure.subtype !== 'RL') return { unavailable: 'foreign-measure' };\n const m = dto.measure,\n scale = scaleOf(m);\n if (!scale) return { unavailable: 'no-scale' };\n const points =\n dto.subtype === 'line'\n ? dto.linePoints\n ? [dto.linePoints.start, dto.linePoints.end]\n : []\n : (dto.vertices ?? []);\n if (\n points.length < (dto.subtype === 'polygon' ? 3 : 2) ||\n points.some((p) => !Number.isFinite(Math.fround(p.x)) || !Number.isFinite(Math.fround(p.y)))\n ) {\n return { unavailable: 'invalid-geometry' };\n }\n const area = dto.subtype === 'polygon';\n if (area && !validAreaBoundary(points)) return { unavailable: 'invalid-geometry' };\n const formats = area ? m.area : m.distance;\n if (!formats.length) return { unavailable: area ? 'no-area-format' : 'no-distance-format' };\n if (!formats.every(validNumberFormat)) return { unavailable: 'invalid-format' };\n const value = area ? polygonArea(points, scale) : pathLength(points, false, scale);\n try {\n return {\n kind: area ? 'area' : dto.subtype === 'line' ? 'distance' : 'perimeter',\n value: value * Math.fround(formats[0].conversion!),\n label: formatMeasurement(value, formats),\n ...(area && m.distance.length && m.distance.every(validNumberFormat)\n ? { perimeter: formatMeasurement(pathLength(points, true, scale), m.distance) }\n : {}),\n };\n } catch {\n return { unavailable: 'invalid-format' };\n }\n}\n\nexport function deriveMeasurementLabel<T extends DimensionInput>(dto: T): T {\n const readout = measurementReadout(dto);\n return isReadout(readout) && dto.contents !== readout.label\n ? { ...dto, contents: readout.label }\n : dto;\n}\n"],"mappings":"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"}
|
|
@@ -373,6 +373,63 @@ function charMapViolation(charCount, textLength, charMap) {
|
|
|
373
373
|
*/
|
|
374
374
|
const PAGE_INSERT_BLANK_MAX_COUNT = 100;
|
|
375
375
|
//#endregion
|
|
376
|
-
|
|
376
|
+
//#region src/measure/format.ts
|
|
377
|
+
const group = (value, separator) => value.replace(/\B(?=(\d{3})+(?!\d))/g, () => separator);
|
|
378
|
+
const gcd = (a, b) => b ? gcd(b, a % b) : a;
|
|
379
|
+
function validNumberFormat(nf) {
|
|
380
|
+
const c = Math.fround(nf.conversion ?? NaN);
|
|
381
|
+
if (!Number.isFinite(c) || c <= 0) return false;
|
|
382
|
+
const d = nf.precision ?? (nf.fraction === "fraction" ? 16 : 100);
|
|
383
|
+
if (!Number.isInteger(d) || d < 1 || d > 2147483647) return false;
|
|
384
|
+
if ((nf.fraction ?? "decimal") === "decimal" && !Number.isInteger(Math.log10(d))) return false;
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
function last(value, nf) {
|
|
388
|
+
const mode = nf.fraction ?? "decimal";
|
|
389
|
+
const sep = nf.thousands ?? ",";
|
|
390
|
+
if (mode === "round") return group(String(Math.round(value)), sep);
|
|
391
|
+
if (mode === "truncate") return group(String(Math.trunc(value)), sep);
|
|
392
|
+
if (mode === "fraction") {
|
|
393
|
+
const denominator = nf.precision ?? 16;
|
|
394
|
+
let whole = Math.floor(value);
|
|
395
|
+
let numerator = Math.round((value - whole) * denominator);
|
|
396
|
+
if (numerator === denominator) {
|
|
397
|
+
whole++;
|
|
398
|
+
numerator = 0;
|
|
399
|
+
}
|
|
400
|
+
if (!numerator) return group(String(whole), sep);
|
|
401
|
+
const divisor = nf.fixed ? 1 : gcd(numerator, denominator);
|
|
402
|
+
return `${whole ? `${group(String(whole), sep)} ` : ""}${numerator / divisor}/${denominator / divisor}`;
|
|
403
|
+
}
|
|
404
|
+
const digits = Math.log10(nf.precision ?? 100);
|
|
405
|
+
const [integer, decimal = ""] = value.toFixed(digits).split(".");
|
|
406
|
+
const kept = nf.fixed ? decimal : decimal.replace(/0+$/, "");
|
|
407
|
+
return group(integer, sep) + (kept ? (nf.decimal || ".") + kept : "");
|
|
408
|
+
}
|
|
409
|
+
/** ISO 32000-2 §12.9.2 cascade. PDF float conversions are normalized before use.
|
|
410
|
+
* Unit text and explicit spacing are preserved, including trailing unit spaces. */
|
|
411
|
+
function formatMeasurement(value, formats) {
|
|
412
|
+
if (!Number.isFinite(value) || !formats.length || !formats.every(validNumberFormat)) throw new RangeError("Measurement requires a finite value and valid number formats");
|
|
413
|
+
let remainder = Math.abs(value);
|
|
414
|
+
const parts = [];
|
|
415
|
+
for (const [index, nf] of formats.entries()) {
|
|
416
|
+
remainder *= Math.fround(nf.conversion);
|
|
417
|
+
if (!Number.isFinite(remainder)) throw new RangeError("Measurement overflow");
|
|
418
|
+
const whole = Math.floor(remainder);
|
|
419
|
+
const fraction = remainder - whole;
|
|
420
|
+
const terminal = index === formats.length - 1 || fraction === 0;
|
|
421
|
+
const number = terminal ? last(remainder, nf) : group(String(whole), nf.thousands ?? ",");
|
|
422
|
+
const prefix = nf.labelPosition === "prefix";
|
|
423
|
+
const ps = nf.prefixSpacing ?? (prefix && index === 0 ? "" : " ");
|
|
424
|
+
const ss = nf.suffixSpacing ?? (!prefix && terminal ? "" : " ");
|
|
425
|
+
const label = ps + nf.unit + ss;
|
|
426
|
+
parts.push(prefix ? label + number : number + label);
|
|
427
|
+
if (terminal) break;
|
|
428
|
+
remainder = fraction;
|
|
429
|
+
}
|
|
430
|
+
return (value < 0 ? "-" : "") + parts.join("");
|
|
431
|
+
}
|
|
432
|
+
//#endregion
|
|
433
|
+
export { charBoundaryAtTextOffset as a, sliceTextByChars as c, PDF_SUBTYPE_TO_CODE as d, PdfAnnotationSubtypeCode as f, boundaryTextOffset as i, ANNOTATION_SUBTYPES as l, EngineErrorCode as m, validNumberFormat as n, charMapViolation as o, subtypeFromCode as p, PAGE_INSERT_BLANK_MAX_COUNT as r, charRangeForTextOffsets as s, formatMeasurement as t, PDF_CODE_TO_SUBTYPE as u };
|
|
377
434
|
|
|
378
|
-
//# sourceMappingURL=
|
|
435
|
+
//# sourceMappingURL=format-Bbphf4zR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-Bbphf4zR.js","names":[],"sources":["../src/errors/EngineErrorCode.ts","../src/annotation/subtype.ts","../src/text/charmap.ts","../src/mutation/PageInsertBlankInput.ts","../src/measure/format.ts"],"sourcesContent":["export const EngineErrorCode = {\n Unknown: 'Unknown',\n InvalidArg: 'InvalidArg',\n DocNotOpen: 'DocNotOpen',\n DocOpenFailed: 'DocOpenFailed',\n DocPasswordRequired: 'DocPasswordRequired',\n DocPasswordIncorrect: 'DocPasswordIncorrect',\n /**\n * A share-token exchange (`open({ kind: 'share' })`) needs the grant's\n * passphrase: none was supplied, or the supplied one was rejected —\n * indistinguishable by design, mirroring the server. Prompt and retry\n * with `sharePassword`. Distinct from `DocPasswordRequired`, which is\n * about the PDF's own encryption password.\n */\n SharePasswordRequired: 'SharePasswordRequired',\n Aborted: 'Aborted',\n Network: 'Network',\n Unauthenticated: 'Unauthenticated',\n Forbidden: 'Forbidden',\n NotFound: 'NotFound',\n WireFormat: 'WireFormat',\n RuntimeUnavailable: 'RuntimeUnavailable',\n /**\n * Annotation/page reference could not be resolved. Surfaced when:\n * - `AnnotationRef.kind === 'objectNumber'` but the document has no\n * annotation with that indirect object number on the addressed page\n * - `AnnotationRef.kind === 'nm'` but no annotation on the page has\n * that `/NM`\n * - `AnnotationRef.kind === 'index'` but the supplied `RevisionToken`\n * does not match the current per-page generation\n * - `AnnotationRef.kind === 'index'` but `index` is out of range for\n * the page's current annotation count\n */\n InvalidReference: 'InvalidReference',\n /**\n * A weak-annotation structural edit (delete/move) was attempted while the\n * caller does not have sole active edit presence for that page. This is a\n * collaboration guard for index-addressed annotations; stable pages and\n * non-shifting annotation updates do not use it.\n */\n WeakAnnotationSessionConflict: 'WeakAnnotationSessionConflict',\n /**\n * A layer write lost its optimistic-concurrency check: the layer's\n * durable version advanced (another writer — typically another server\n * replica — committed) between the operation's prepare and its commit,\n * and the server exhausted its rebase-and-retry budget. Retryable: the\n * operation was NOT applied; re-issue it against the new state.\n */\n LayerVersionConflict: 'LayerVersionConflict',\n /**\n * The requested operation is typed but not yet wired in this engine\n * version. The wire shape stays valid; clients can detect this with\n * `EngineError.is(err, EngineErrorCode.NotImplemented)` and degrade\n * gracefully.\n */\n NotImplemented: 'NotImplemented',\n /**\n * The PDF was loadable enough for PDFium to parse, but violates a\n * structural invariant the engine relies on. Surfaced for the rare\n * spec-violating PDF that ships a direct (non-indirect) page\n * dictionary in the /Pages tree — see `EPDFPage_GetObjectNumber`\n * returning `0`. PDFium creation paths (`FPDFPage_New`,\n * `EPDFPage_CreateAnnot`) always produce indirect objects, so this\n * error code is intentionally not used for engine-produced state;\n * it specifically signals \"this input file is broken in a way we\n * cannot work around without compromising stable identity\".\n *\n * Distinct from `DocOpenFailed` (PDFium refused to load the bytes\n * at all) and `NotFound` (a well-formed page/annotation/etc. simply\n * doesn't exist).\n */\n MalformedPdf: 'MalformedPdf',\n /**\n * A signing candidate is parked on this session: every mutation is\n * refused until `signatures.complete` or `signatures.abort`.\n */\n SigningPending: 'SigningPending',\n /** The candidate's TTL elapsed; prepare again. */\n SigningExpired: 'SigningExpired',\n /**\n * `complete` presented a version the candidate was not built on (the\n * document or the layer moved since `prepare`). The CMS cannot be\n * reused: it signs a digest of bytes that will never be the head.\n * Prepare again on the current version.\n */\n SigningVersionMismatch: 'SigningVersionMismatch',\n /**\n * The engine refused to author the signature: the field is signed,\n * read-only, or locked by an earlier signature; a certification is\n * requested after a signature exists; the seed value requires what the\n * engine does not implement; the request is inconsistent. The message\n * names the reason.\n */\n SignatureRefused: 'SignatureRefused',\n /**\n * A signature already in the document forbids this change (a\n * certification's permission, a FieldMDP or `/Lock`, or the approval\n * baseline). The message names the signature and the restriction. The\n * engine option `signedDocumentPolicy: 'permit'` disables the guard.\n */\n ProtectedDocument: 'ProtectedDocument',\n /**\n * The layer is built on a base version that is no longer the\n * document's head (another signing completed); it can be read and\n * edited but not signed from.\n */\n StaleBase: 'StaleBase',\n} as const;\n\nexport type EngineErrorCode = (typeof EngineErrorCode)[keyof typeof EngineErrorCode];\n","/**\n * Closed-world string catalog of annotation subtypes the v3 wire format knows\n * about. The TypeScript discriminated union `AnnotationDTO` is keyed on this\n * literal type. PDFium's integer subtype code is mapped onto these strings\n * by the reader registry.\n *\n * `unsupported` is the forward-compat fallback for any PDF subtype that\n * the engine has not implemented a reader for yet. Unknown subtypes never\n * silently disappear from the wire — they round-trip as\n * `{ subtype: 'unsupported', rawSubtypeCode }`.\n */\nexport const ANNOTATION_SUBTYPES = [\n 'text',\n 'link',\n 'free-text',\n 'line',\n 'square',\n 'circle',\n 'polygon',\n 'polyline',\n 'highlight',\n 'underline',\n 'squiggly',\n 'strikeout',\n 'stamp',\n 'caret',\n 'ink',\n 'popup',\n 'file-attachment',\n 'redact',\n 'widget',\n 'unsupported',\n] as const;\n\nexport type AnnotationSubtype = (typeof ANNOTATION_SUBTYPES)[number];\n\n/**\n * PDFium subtype codes from `FPDFAnnot_GetSubtype`. Mirrors\n * `FPDF_ANNOTATION_SUBTYPE` in `public/fpdf_annot.h`. We keep this here\n * (rather than importing from @embedpdf/engine-runtime) so the engine-core\n * package has zero PDFium dependency.\n */\nexport const PdfAnnotationSubtypeCode = {\n UNKNOWN: 0,\n TEXT: 1,\n LINK: 2,\n FREETEXT: 3,\n LINE: 4,\n SQUARE: 5,\n CIRCLE: 6,\n POLYGON: 7,\n POLYLINE: 8,\n HIGHLIGHT: 9,\n UNDERLINE: 10,\n SQUIGGLY: 11,\n STRIKEOUT: 12,\n STAMP: 13,\n CARET: 14,\n INK: 15,\n POPUP: 16,\n FILEATTACHMENT: 17,\n SOUND: 18,\n MOVIE: 19,\n WIDGET: 20,\n SCREEN: 21,\n PRINTERMARK: 22,\n TRAPNET: 23,\n WATERMARK: 24,\n THREED: 25,\n RICHMEDIA: 26,\n XFAWIDGET: 27,\n REDACT: 28,\n} as const;\n\nexport type PdfAnnotationSubtypeCode =\n (typeof PdfAnnotationSubtypeCode)[keyof typeof PdfAnnotationSubtypeCode];\n\n/**\n * Map from PDFium codes to wire-stable string subtypes. Codes the engine\n * recognises as a v3 subtype (or maps to 'unsupported' if not yet wired).\n */\nexport const PDF_CODE_TO_SUBTYPE: Readonly<Record<number, AnnotationSubtype>> = Object.freeze({\n [PdfAnnotationSubtypeCode.TEXT]: 'text',\n [PdfAnnotationSubtypeCode.LINK]: 'link',\n [PdfAnnotationSubtypeCode.FREETEXT]: 'free-text',\n [PdfAnnotationSubtypeCode.LINE]: 'line',\n [PdfAnnotationSubtypeCode.SQUARE]: 'square',\n [PdfAnnotationSubtypeCode.CIRCLE]: 'circle',\n [PdfAnnotationSubtypeCode.POLYGON]: 'polygon',\n [PdfAnnotationSubtypeCode.POLYLINE]: 'polyline',\n [PdfAnnotationSubtypeCode.HIGHLIGHT]: 'highlight',\n [PdfAnnotationSubtypeCode.UNDERLINE]: 'underline',\n [PdfAnnotationSubtypeCode.SQUIGGLY]: 'squiggly',\n [PdfAnnotationSubtypeCode.STRIKEOUT]: 'strikeout',\n [PdfAnnotationSubtypeCode.STAMP]: 'stamp',\n [PdfAnnotationSubtypeCode.CARET]: 'caret',\n [PdfAnnotationSubtypeCode.INK]: 'ink',\n [PdfAnnotationSubtypeCode.POPUP]: 'popup',\n [PdfAnnotationSubtypeCode.FILEATTACHMENT]: 'file-attachment',\n [PdfAnnotationSubtypeCode.WIDGET]: 'widget',\n [PdfAnnotationSubtypeCode.REDACT]: 'redact',\n});\n\n/**\n * Reverse map for write paths that need to turn a wire subtype back into the\n * PDFium integer code without importing the zod-backed annotation registry.\n */\nexport const PDF_SUBTYPE_TO_CODE: Readonly<Record<AnnotationSubtype, PdfAnnotationSubtypeCode>> =\n Object.freeze({\n text: PdfAnnotationSubtypeCode.TEXT,\n link: PdfAnnotationSubtypeCode.LINK,\n 'free-text': PdfAnnotationSubtypeCode.FREETEXT,\n line: PdfAnnotationSubtypeCode.LINE,\n square: PdfAnnotationSubtypeCode.SQUARE,\n circle: PdfAnnotationSubtypeCode.CIRCLE,\n polygon: PdfAnnotationSubtypeCode.POLYGON,\n polyline: PdfAnnotationSubtypeCode.POLYLINE,\n highlight: PdfAnnotationSubtypeCode.HIGHLIGHT,\n underline: PdfAnnotationSubtypeCode.UNDERLINE,\n squiggly: PdfAnnotationSubtypeCode.SQUIGGLY,\n strikeout: PdfAnnotationSubtypeCode.STRIKEOUT,\n stamp: PdfAnnotationSubtypeCode.STAMP,\n caret: PdfAnnotationSubtypeCode.CARET,\n ink: PdfAnnotationSubtypeCode.INK,\n popup: PdfAnnotationSubtypeCode.POPUP,\n 'file-attachment': PdfAnnotationSubtypeCode.FILEATTACHMENT,\n redact: PdfAnnotationSubtypeCode.REDACT,\n widget: PdfAnnotationSubtypeCode.WIDGET,\n unsupported: PdfAnnotationSubtypeCode.UNKNOWN,\n });\n\nexport function subtypeFromCode(code: number): AnnotationSubtype {\n return PDF_CODE_TO_SUBTYPE[code] ?? 'unsupported';\n}\n","import 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.charStart`), 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\n * `text.slice(startOffset, endOffset)` — THE inverse used by search-hit\n * conversion and select-by-text. Start is right-biased and end left-biased\n * (see {@link charBoundaryAtTextOffset}), so zero-width characters adjacent\n * 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. Empty input (`endOffset <= startOffset`)\n * normalizes to the end-rule boundary of `startOffset` on both sides.\n */\nexport function charRangeForTextOffsets(\n snapshot: PageTextSnapshot,\n startOffset: number,\n endOffset: number,\n): { start: number; end: number } {\n if (endOffset <= startOffset) {\n const b = charBoundaryAtTextOffset(snapshot, startOffset, 'end');\n return { start: b, end: b };\n }\n return {\n start: charBoundaryAtTextOffset(snapshot, startOffset, 'start'),\n end: charBoundaryAtTextOffset(snapshot, endOffset, 'end'),\n };\n}\n\n/**\n * THE copy primitive: the text projection of the half-open character range\n * `[charStart, charEnd)`. Dropped characters inside the range contribute\n * nothing; a supplementary character contributes its full surrogate pair.\n * `sliceTextByChars(s, 0, s.charCount) === s.text` always.\n */\nexport function sliceTextByChars(\n snapshot: PageTextSnapshot,\n charStart: number,\n charEnd: number,\n): string {\n if (charEnd <= charStart) return '';\n return snapshot.text.slice(\n boundaryTextOffset(snapshot, charStart),\n boundaryTextOffset(snapshot, charEnd),\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","import type { PdfSize } from '../geometry/primitives';\n\n/**\n * Upper bound on `count` for a single `pages.insertBlank()` call. A CONTRACT\n * cap, enforced identically by every engine (the cloud input schema carries\n * the same number), so a UI never discovers a lower server limit by surprise.\n * Larger batches are a loop at the call site.\n */\nexport const PAGE_INSERT_BLANK_MAX_COUNT = 100;\n\n/**\n * Spec for `pages.insertBlank()`: create `count` blank pages of `size`.\n *\n * `size` is explicit and in PDF points, un-rotated — the same convention as\n * `PageLayout.size`. The \"match my neighbour\" default developers expect is a\n * registry read, so it lives in the page-edit capability, not here: the\n * engine wire never guesses (the `rotateBy` law).\n */\nexport interface PageInsertBlankSpec {\n /** Page size in PDF points. Both dimensions must be finite and > 0. */\n size: PdfSize;\n /** Pages to create, in `[1, PAGE_INSERT_BLANK_MAX_COUNT]`. Default 1. */\n count?: number;\n}\n","import type { PdfNumberFormat } from '../dto/Measure';\n\nconst group = (value: string, separator: string): string =>\n value.replace(/\\B(?=(\\d{3})+(?!\\d))/g, () => separator);\nconst gcd = (a: number, b: number): number => (b ? gcd(b, a % b) : a);\n\nexport function validNumberFormat(nf: PdfNumberFormat): boolean {\n const c = Math.fround(nf.conversion ?? NaN);\n if (!Number.isFinite(c) || c <= 0) return false;\n const d = nf.precision ?? (nf.fraction === 'fraction' ? 16 : 100);\n if (!Number.isInteger(d) || d < 1 || d > 2147483647) return false;\n if ((nf.fraction ?? 'decimal') === 'decimal' && !Number.isInteger(Math.log10(d))) return false;\n return true;\n}\n\nfunction last(value: number, nf: PdfNumberFormat): string {\n const mode = nf.fraction ?? 'decimal';\n const sep = nf.thousands ?? ',';\n if (mode === 'round') return group(String(Math.round(value)), sep);\n if (mode === 'truncate') return group(String(Math.trunc(value)), sep);\n if (mode === 'fraction') {\n const denominator = nf.precision ?? 16;\n let whole = Math.floor(value);\n let numerator = Math.round((value - whole) * denominator);\n if (numerator === denominator) {\n whole++;\n numerator = 0;\n }\n if (!numerator) return group(String(whole), sep);\n const divisor = nf.fixed ? 1 : gcd(numerator, denominator);\n return `${whole ? `${group(String(whole), sep)} ` : ''}${numerator / divisor}/${denominator / divisor}`;\n }\n const digits = Math.log10(nf.precision ?? 100);\n const [integer, decimal = ''] = value.toFixed(digits).split('.');\n const kept = nf.fixed ? decimal : decimal.replace(/0+$/, '');\n return group(integer, sep) + (kept ? (nf.decimal || '.') + kept : '');\n}\n\n/** ISO 32000-2 §12.9.2 cascade. PDF float conversions are normalized before use.\n * Unit text and explicit spacing are preserved, including trailing unit spaces. */\nexport function formatMeasurement(value: number, formats: readonly PdfNumberFormat[]): string {\n if (!Number.isFinite(value) || !formats.length || !formats.every(validNumberFormat)) {\n throw new RangeError('Measurement requires a finite value and valid number formats');\n }\n let remainder = Math.abs(value);\n const parts: string[] = [];\n for (const [index, nf] of formats.entries()) {\n remainder *= Math.fround(nf.conversion!);\n if (!Number.isFinite(remainder)) throw new RangeError('Measurement overflow');\n const whole = Math.floor(remainder);\n const fraction = remainder - whole;\n const terminal = index === formats.length - 1 || fraction === 0;\n const number = terminal ? last(remainder, nf) : group(String(whole), nf.thousands ?? ',');\n // Omit only the implicit outer padding, never trim the unit or explicit separators.\n const prefix = nf.labelPosition === 'prefix';\n const ps = nf.prefixSpacing ?? (prefix && index === 0 ? '' : ' ');\n const ss = nf.suffixSpacing ?? (!prefix && terminal ? '' : ' ');\n const label = ps + nf.unit + ss;\n parts.push(prefix ? label + number : number + label);\n if (terminal) break;\n remainder = fraction;\n }\n return (value < 0 ? '-' : '') + parts.join('');\n}\n"],"mappings":"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"}
|
|
@@ -373,6 +373,63 @@ function charMapViolation(charCount, textLength, charMap) {
|
|
|
373
373
|
*/
|
|
374
374
|
const PAGE_INSERT_BLANK_MAX_COUNT = 100;
|
|
375
375
|
//#endregion
|
|
376
|
+
//#region src/measure/format.ts
|
|
377
|
+
const group = (value, separator) => value.replace(/\B(?=(\d{3})+(?!\d))/g, () => separator);
|
|
378
|
+
const gcd = (a, b) => b ? gcd(b, a % b) : a;
|
|
379
|
+
function validNumberFormat(nf) {
|
|
380
|
+
const c = Math.fround(nf.conversion ?? NaN);
|
|
381
|
+
if (!Number.isFinite(c) || c <= 0) return false;
|
|
382
|
+
const d = nf.precision ?? (nf.fraction === "fraction" ? 16 : 100);
|
|
383
|
+
if (!Number.isInteger(d) || d < 1 || d > 2147483647) return false;
|
|
384
|
+
if ((nf.fraction ?? "decimal") === "decimal" && !Number.isInteger(Math.log10(d))) return false;
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
function last(value, nf) {
|
|
388
|
+
const mode = nf.fraction ?? "decimal";
|
|
389
|
+
const sep = nf.thousands ?? ",";
|
|
390
|
+
if (mode === "round") return group(String(Math.round(value)), sep);
|
|
391
|
+
if (mode === "truncate") return group(String(Math.trunc(value)), sep);
|
|
392
|
+
if (mode === "fraction") {
|
|
393
|
+
const denominator = nf.precision ?? 16;
|
|
394
|
+
let whole = Math.floor(value);
|
|
395
|
+
let numerator = Math.round((value - whole) * denominator);
|
|
396
|
+
if (numerator === denominator) {
|
|
397
|
+
whole++;
|
|
398
|
+
numerator = 0;
|
|
399
|
+
}
|
|
400
|
+
if (!numerator) return group(String(whole), sep);
|
|
401
|
+
const divisor = nf.fixed ? 1 : gcd(numerator, denominator);
|
|
402
|
+
return `${whole ? `${group(String(whole), sep)} ` : ""}${numerator / divisor}/${denominator / divisor}`;
|
|
403
|
+
}
|
|
404
|
+
const digits = Math.log10(nf.precision ?? 100);
|
|
405
|
+
const [integer, decimal = ""] = value.toFixed(digits).split(".");
|
|
406
|
+
const kept = nf.fixed ? decimal : decimal.replace(/0+$/, "");
|
|
407
|
+
return group(integer, sep) + (kept ? (nf.decimal || ".") + kept : "");
|
|
408
|
+
}
|
|
409
|
+
/** ISO 32000-2 §12.9.2 cascade. PDF float conversions are normalized before use.
|
|
410
|
+
* Unit text and explicit spacing are preserved, including trailing unit spaces. */
|
|
411
|
+
function formatMeasurement(value, formats) {
|
|
412
|
+
if (!Number.isFinite(value) || !formats.length || !formats.every(validNumberFormat)) throw new RangeError("Measurement requires a finite value and valid number formats");
|
|
413
|
+
let remainder = Math.abs(value);
|
|
414
|
+
const parts = [];
|
|
415
|
+
for (const [index, nf] of formats.entries()) {
|
|
416
|
+
remainder *= Math.fround(nf.conversion);
|
|
417
|
+
if (!Number.isFinite(remainder)) throw new RangeError("Measurement overflow");
|
|
418
|
+
const whole = Math.floor(remainder);
|
|
419
|
+
const fraction = remainder - whole;
|
|
420
|
+
const terminal = index === formats.length - 1 || fraction === 0;
|
|
421
|
+
const number = terminal ? last(remainder, nf) : group(String(whole), nf.thousands ?? ",");
|
|
422
|
+
const prefix = nf.labelPosition === "prefix";
|
|
423
|
+
const ps = nf.prefixSpacing ?? (prefix && index === 0 ? "" : " ");
|
|
424
|
+
const ss = nf.suffixSpacing ?? (!prefix && terminal ? "" : " ");
|
|
425
|
+
const label = ps + nf.unit + ss;
|
|
426
|
+
parts.push(prefix ? label + number : number + label);
|
|
427
|
+
if (terminal) break;
|
|
428
|
+
remainder = fraction;
|
|
429
|
+
}
|
|
430
|
+
return (value < 0 ? "-" : "") + parts.join("");
|
|
431
|
+
}
|
|
432
|
+
//#endregion
|
|
376
433
|
Object.defineProperty(exports, "ANNOTATION_SUBTYPES", {
|
|
377
434
|
enumerable: true,
|
|
378
435
|
get: function() {
|
|
@@ -433,6 +490,12 @@ Object.defineProperty(exports, "charRangeForTextOffsets", {
|
|
|
433
490
|
return charRangeForTextOffsets;
|
|
434
491
|
}
|
|
435
492
|
});
|
|
493
|
+
Object.defineProperty(exports, "formatMeasurement", {
|
|
494
|
+
enumerable: true,
|
|
495
|
+
get: function() {
|
|
496
|
+
return formatMeasurement;
|
|
497
|
+
}
|
|
498
|
+
});
|
|
436
499
|
Object.defineProperty(exports, "sliceTextByChars", {
|
|
437
500
|
enumerable: true,
|
|
438
501
|
get: function() {
|
|
@@ -445,5 +508,11 @@ Object.defineProperty(exports, "subtypeFromCode", {
|
|
|
445
508
|
return subtypeFromCode;
|
|
446
509
|
}
|
|
447
510
|
});
|
|
511
|
+
Object.defineProperty(exports, "validNumberFormat", {
|
|
512
|
+
enumerable: true,
|
|
513
|
+
get: function() {
|
|
514
|
+
return validNumberFormat;
|
|
515
|
+
}
|
|
516
|
+
});
|
|
448
517
|
|
|
449
|
-
//# sourceMappingURL=
|
|
518
|
+
//# sourceMappingURL=format-D_epWlob.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-D_epWlob.cjs","names":[],"sources":["../src/errors/EngineErrorCode.ts","../src/annotation/subtype.ts","../src/text/charmap.ts","../src/mutation/PageInsertBlankInput.ts","../src/measure/format.ts"],"sourcesContent":["export const EngineErrorCode = {\n Unknown: 'Unknown',\n InvalidArg: 'InvalidArg',\n DocNotOpen: 'DocNotOpen',\n DocOpenFailed: 'DocOpenFailed',\n DocPasswordRequired: 'DocPasswordRequired',\n DocPasswordIncorrect: 'DocPasswordIncorrect',\n /**\n * A share-token exchange (`open({ kind: 'share' })`) needs the grant's\n * passphrase: none was supplied, or the supplied one was rejected —\n * indistinguishable by design, mirroring the server. Prompt and retry\n * with `sharePassword`. Distinct from `DocPasswordRequired`, which is\n * about the PDF's own encryption password.\n */\n SharePasswordRequired: 'SharePasswordRequired',\n Aborted: 'Aborted',\n Network: 'Network',\n Unauthenticated: 'Unauthenticated',\n Forbidden: 'Forbidden',\n NotFound: 'NotFound',\n WireFormat: 'WireFormat',\n RuntimeUnavailable: 'RuntimeUnavailable',\n /**\n * Annotation/page reference could not be resolved. Surfaced when:\n * - `AnnotationRef.kind === 'objectNumber'` but the document has no\n * annotation with that indirect object number on the addressed page\n * - `AnnotationRef.kind === 'nm'` but no annotation on the page has\n * that `/NM`\n * - `AnnotationRef.kind === 'index'` but the supplied `RevisionToken`\n * does not match the current per-page generation\n * - `AnnotationRef.kind === 'index'` but `index` is out of range for\n * the page's current annotation count\n */\n InvalidReference: 'InvalidReference',\n /**\n * A weak-annotation structural edit (delete/move) was attempted while the\n * caller does not have sole active edit presence for that page. This is a\n * collaboration guard for index-addressed annotations; stable pages and\n * non-shifting annotation updates do not use it.\n */\n WeakAnnotationSessionConflict: 'WeakAnnotationSessionConflict',\n /**\n * A layer write lost its optimistic-concurrency check: the layer's\n * durable version advanced (another writer — typically another server\n * replica — committed) between the operation's prepare and its commit,\n * and the server exhausted its rebase-and-retry budget. Retryable: the\n * operation was NOT applied; re-issue it against the new state.\n */\n LayerVersionConflict: 'LayerVersionConflict',\n /**\n * The requested operation is typed but not yet wired in this engine\n * version. The wire shape stays valid; clients can detect this with\n * `EngineError.is(err, EngineErrorCode.NotImplemented)` and degrade\n * gracefully.\n */\n NotImplemented: 'NotImplemented',\n /**\n * The PDF was loadable enough for PDFium to parse, but violates a\n * structural invariant the engine relies on. Surfaced for the rare\n * spec-violating PDF that ships a direct (non-indirect) page\n * dictionary in the /Pages tree — see `EPDFPage_GetObjectNumber`\n * returning `0`. PDFium creation paths (`FPDFPage_New`,\n * `EPDFPage_CreateAnnot`) always produce indirect objects, so this\n * error code is intentionally not used for engine-produced state;\n * it specifically signals \"this input file is broken in a way we\n * cannot work around without compromising stable identity\".\n *\n * Distinct from `DocOpenFailed` (PDFium refused to load the bytes\n * at all) and `NotFound` (a well-formed page/annotation/etc. simply\n * doesn't exist).\n */\n MalformedPdf: 'MalformedPdf',\n /**\n * A signing candidate is parked on this session: every mutation is\n * refused until `signatures.complete` or `signatures.abort`.\n */\n SigningPending: 'SigningPending',\n /** The candidate's TTL elapsed; prepare again. */\n SigningExpired: 'SigningExpired',\n /**\n * `complete` presented a version the candidate was not built on (the\n * document or the layer moved since `prepare`). The CMS cannot be\n * reused: it signs a digest of bytes that will never be the head.\n * Prepare again on the current version.\n */\n SigningVersionMismatch: 'SigningVersionMismatch',\n /**\n * The engine refused to author the signature: the field is signed,\n * read-only, or locked by an earlier signature; a certification is\n * requested after a signature exists; the seed value requires what the\n * engine does not implement; the request is inconsistent. The message\n * names the reason.\n */\n SignatureRefused: 'SignatureRefused',\n /**\n * A signature already in the document forbids this change (a\n * certification's permission, a FieldMDP or `/Lock`, or the approval\n * baseline). The message names the signature and the restriction. The\n * engine option `signedDocumentPolicy: 'permit'` disables the guard.\n */\n ProtectedDocument: 'ProtectedDocument',\n /**\n * The layer is built on a base version that is no longer the\n * document's head (another signing completed); it can be read and\n * edited but not signed from.\n */\n StaleBase: 'StaleBase',\n} as const;\n\nexport type EngineErrorCode = (typeof EngineErrorCode)[keyof typeof EngineErrorCode];\n","/**\n * Closed-world string catalog of annotation subtypes the v3 wire format knows\n * about. The TypeScript discriminated union `AnnotationDTO` is keyed on this\n * literal type. PDFium's integer subtype code is mapped onto these strings\n * by the reader registry.\n *\n * `unsupported` is the forward-compat fallback for any PDF subtype that\n * the engine has not implemented a reader for yet. Unknown subtypes never\n * silently disappear from the wire — they round-trip as\n * `{ subtype: 'unsupported', rawSubtypeCode }`.\n */\nexport const ANNOTATION_SUBTYPES = [\n 'text',\n 'link',\n 'free-text',\n 'line',\n 'square',\n 'circle',\n 'polygon',\n 'polyline',\n 'highlight',\n 'underline',\n 'squiggly',\n 'strikeout',\n 'stamp',\n 'caret',\n 'ink',\n 'popup',\n 'file-attachment',\n 'redact',\n 'widget',\n 'unsupported',\n] as const;\n\nexport type AnnotationSubtype = (typeof ANNOTATION_SUBTYPES)[number];\n\n/**\n * PDFium subtype codes from `FPDFAnnot_GetSubtype`. Mirrors\n * `FPDF_ANNOTATION_SUBTYPE` in `public/fpdf_annot.h`. We keep this here\n * (rather than importing from @embedpdf/engine-runtime) so the engine-core\n * package has zero PDFium dependency.\n */\nexport const PdfAnnotationSubtypeCode = {\n UNKNOWN: 0,\n TEXT: 1,\n LINK: 2,\n FREETEXT: 3,\n LINE: 4,\n SQUARE: 5,\n CIRCLE: 6,\n POLYGON: 7,\n POLYLINE: 8,\n HIGHLIGHT: 9,\n UNDERLINE: 10,\n SQUIGGLY: 11,\n STRIKEOUT: 12,\n STAMP: 13,\n CARET: 14,\n INK: 15,\n POPUP: 16,\n FILEATTACHMENT: 17,\n SOUND: 18,\n MOVIE: 19,\n WIDGET: 20,\n SCREEN: 21,\n PRINTERMARK: 22,\n TRAPNET: 23,\n WATERMARK: 24,\n THREED: 25,\n RICHMEDIA: 26,\n XFAWIDGET: 27,\n REDACT: 28,\n} as const;\n\nexport type PdfAnnotationSubtypeCode =\n (typeof PdfAnnotationSubtypeCode)[keyof typeof PdfAnnotationSubtypeCode];\n\n/**\n * Map from PDFium codes to wire-stable string subtypes. Codes the engine\n * recognises as a v3 subtype (or maps to 'unsupported' if not yet wired).\n */\nexport const PDF_CODE_TO_SUBTYPE: Readonly<Record<number, AnnotationSubtype>> = Object.freeze({\n [PdfAnnotationSubtypeCode.TEXT]: 'text',\n [PdfAnnotationSubtypeCode.LINK]: 'link',\n [PdfAnnotationSubtypeCode.FREETEXT]: 'free-text',\n [PdfAnnotationSubtypeCode.LINE]: 'line',\n [PdfAnnotationSubtypeCode.SQUARE]: 'square',\n [PdfAnnotationSubtypeCode.CIRCLE]: 'circle',\n [PdfAnnotationSubtypeCode.POLYGON]: 'polygon',\n [PdfAnnotationSubtypeCode.POLYLINE]: 'polyline',\n [PdfAnnotationSubtypeCode.HIGHLIGHT]: 'highlight',\n [PdfAnnotationSubtypeCode.UNDERLINE]: 'underline',\n [PdfAnnotationSubtypeCode.SQUIGGLY]: 'squiggly',\n [PdfAnnotationSubtypeCode.STRIKEOUT]: 'strikeout',\n [PdfAnnotationSubtypeCode.STAMP]: 'stamp',\n [PdfAnnotationSubtypeCode.CARET]: 'caret',\n [PdfAnnotationSubtypeCode.INK]: 'ink',\n [PdfAnnotationSubtypeCode.POPUP]: 'popup',\n [PdfAnnotationSubtypeCode.FILEATTACHMENT]: 'file-attachment',\n [PdfAnnotationSubtypeCode.WIDGET]: 'widget',\n [PdfAnnotationSubtypeCode.REDACT]: 'redact',\n});\n\n/**\n * Reverse map for write paths that need to turn a wire subtype back into the\n * PDFium integer code without importing the zod-backed annotation registry.\n */\nexport const PDF_SUBTYPE_TO_CODE: Readonly<Record<AnnotationSubtype, PdfAnnotationSubtypeCode>> =\n Object.freeze({\n text: PdfAnnotationSubtypeCode.TEXT,\n link: PdfAnnotationSubtypeCode.LINK,\n 'free-text': PdfAnnotationSubtypeCode.FREETEXT,\n line: PdfAnnotationSubtypeCode.LINE,\n square: PdfAnnotationSubtypeCode.SQUARE,\n circle: PdfAnnotationSubtypeCode.CIRCLE,\n polygon: PdfAnnotationSubtypeCode.POLYGON,\n polyline: PdfAnnotationSubtypeCode.POLYLINE,\n highlight: PdfAnnotationSubtypeCode.HIGHLIGHT,\n underline: PdfAnnotationSubtypeCode.UNDERLINE,\n squiggly: PdfAnnotationSubtypeCode.SQUIGGLY,\n strikeout: PdfAnnotationSubtypeCode.STRIKEOUT,\n stamp: PdfAnnotationSubtypeCode.STAMP,\n caret: PdfAnnotationSubtypeCode.CARET,\n ink: PdfAnnotationSubtypeCode.INK,\n popup: PdfAnnotationSubtypeCode.POPUP,\n 'file-attachment': PdfAnnotationSubtypeCode.FILEATTACHMENT,\n redact: PdfAnnotationSubtypeCode.REDACT,\n widget: PdfAnnotationSubtypeCode.WIDGET,\n unsupported: PdfAnnotationSubtypeCode.UNKNOWN,\n });\n\nexport function subtypeFromCode(code: number): AnnotationSubtype {\n return PDF_CODE_TO_SUBTYPE[code] ?? 'unsupported';\n}\n","import 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.charStart`), 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\n * `text.slice(startOffset, endOffset)` — THE inverse used by search-hit\n * conversion and select-by-text. Start is right-biased and end left-biased\n * (see {@link charBoundaryAtTextOffset}), so zero-width characters adjacent\n * 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. Empty input (`endOffset <= startOffset`)\n * normalizes to the end-rule boundary of `startOffset` on both sides.\n */\nexport function charRangeForTextOffsets(\n snapshot: PageTextSnapshot,\n startOffset: number,\n endOffset: number,\n): { start: number; end: number } {\n if (endOffset <= startOffset) {\n const b = charBoundaryAtTextOffset(snapshot, startOffset, 'end');\n return { start: b, end: b };\n }\n return {\n start: charBoundaryAtTextOffset(snapshot, startOffset, 'start'),\n end: charBoundaryAtTextOffset(snapshot, endOffset, 'end'),\n };\n}\n\n/**\n * THE copy primitive: the text projection of the half-open character range\n * `[charStart, charEnd)`. Dropped characters inside the range contribute\n * nothing; a supplementary character contributes its full surrogate pair.\n * `sliceTextByChars(s, 0, s.charCount) === s.text` always.\n */\nexport function sliceTextByChars(\n snapshot: PageTextSnapshot,\n charStart: number,\n charEnd: number,\n): string {\n if (charEnd <= charStart) return '';\n return snapshot.text.slice(\n boundaryTextOffset(snapshot, charStart),\n boundaryTextOffset(snapshot, charEnd),\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","import type { PdfSize } from '../geometry/primitives';\n\n/**\n * Upper bound on `count` for a single `pages.insertBlank()` call. A CONTRACT\n * cap, enforced identically by every engine (the cloud input schema carries\n * the same number), so a UI never discovers a lower server limit by surprise.\n * Larger batches are a loop at the call site.\n */\nexport const PAGE_INSERT_BLANK_MAX_COUNT = 100;\n\n/**\n * Spec for `pages.insertBlank()`: create `count` blank pages of `size`.\n *\n * `size` is explicit and in PDF points, un-rotated — the same convention as\n * `PageLayout.size`. The \"match my neighbour\" default developers expect is a\n * registry read, so it lives in the page-edit capability, not here: the\n * engine wire never guesses (the `rotateBy` law).\n */\nexport interface PageInsertBlankSpec {\n /** Page size in PDF points. Both dimensions must be finite and > 0. */\n size: PdfSize;\n /** Pages to create, in `[1, PAGE_INSERT_BLANK_MAX_COUNT]`. Default 1. */\n count?: number;\n}\n","import type { PdfNumberFormat } from '../dto/Measure';\n\nconst group = (value: string, separator: string): string =>\n value.replace(/\\B(?=(\\d{3})+(?!\\d))/g, () => separator);\nconst gcd = (a: number, b: number): number => (b ? gcd(b, a % b) : a);\n\nexport function validNumberFormat(nf: PdfNumberFormat): boolean {\n const c = Math.fround(nf.conversion ?? NaN);\n if (!Number.isFinite(c) || c <= 0) return false;\n const d = nf.precision ?? (nf.fraction === 'fraction' ? 16 : 100);\n if (!Number.isInteger(d) || d < 1 || d > 2147483647) return false;\n if ((nf.fraction ?? 'decimal') === 'decimal' && !Number.isInteger(Math.log10(d))) return false;\n return true;\n}\n\nfunction last(value: number, nf: PdfNumberFormat): string {\n const mode = nf.fraction ?? 'decimal';\n const sep = nf.thousands ?? ',';\n if (mode === 'round') return group(String(Math.round(value)), sep);\n if (mode === 'truncate') return group(String(Math.trunc(value)), sep);\n if (mode === 'fraction') {\n const denominator = nf.precision ?? 16;\n let whole = Math.floor(value);\n let numerator = Math.round((value - whole) * denominator);\n if (numerator === denominator) {\n whole++;\n numerator = 0;\n }\n if (!numerator) return group(String(whole), sep);\n const divisor = nf.fixed ? 1 : gcd(numerator, denominator);\n return `${whole ? `${group(String(whole), sep)} ` : ''}${numerator / divisor}/${denominator / divisor}`;\n }\n const digits = Math.log10(nf.precision ?? 100);\n const [integer, decimal = ''] = value.toFixed(digits).split('.');\n const kept = nf.fixed ? decimal : decimal.replace(/0+$/, '');\n return group(integer, sep) + (kept ? (nf.decimal || '.') + kept : '');\n}\n\n/** ISO 32000-2 §12.9.2 cascade. PDF float conversions are normalized before use.\n * Unit text and explicit spacing are preserved, including trailing unit spaces. */\nexport function formatMeasurement(value: number, formats: readonly PdfNumberFormat[]): string {\n if (!Number.isFinite(value) || !formats.length || !formats.every(validNumberFormat)) {\n throw new RangeError('Measurement requires a finite value and valid number formats');\n }\n let remainder = Math.abs(value);\n const parts: string[] = [];\n for (const [index, nf] of formats.entries()) {\n remainder *= Math.fround(nf.conversion!);\n if (!Number.isFinite(remainder)) throw new RangeError('Measurement overflow');\n const whole = Math.floor(remainder);\n const fraction = remainder - whole;\n const terminal = index === formats.length - 1 || fraction === 0;\n const number = terminal ? last(remainder, nf) : group(String(whole), nf.thousands ?? ',');\n // Omit only the implicit outer padding, never trim the unit or explicit separators.\n const prefix = nf.labelPosition === 'prefix';\n const ps = nf.prefixSpacing ?? (prefix && index === 0 ? '' : ' ');\n const ss = nf.suffixSpacing ?? (!prefix && terminal ? '' : ' ');\n const label = ps + nf.unit + ss;\n parts.push(prefix ? label + number : number + label);\n if (terminal) break;\n remainder = fraction;\n }\n return (value < 0 ? '-' : '') + parts.join('');\n}\n"],"mappings":";AAAA,MAAa,kBAAkB;CAC7B,SAAS;CACT,YAAY;CACZ,YAAY;CACZ,eAAe;CACf,qBAAqB;CACrB,sBAAsB;;;;;;;;CAQtB,uBAAuB;CACvB,SAAS;CACT,SAAS;CACT,iBAAiB;CACjB,WAAW;CACX,UAAU;CACV,YAAY;CACZ,oBAAoB;;;;;;;;;;;;CAYpB,kBAAkB;;;;;;;CAOlB,+BAA+B;;;;;;;;CAQ/B,sBAAsB;;;;;;;CAOtB,gBAAgB;;;;;;;;;;;;;;;;CAgBhB,cAAc;;;;;CAKd,gBAAgB;;CAEhB,gBAAgB;;;;;;;CAOhB,wBAAwB;;;;;;;;CAQxB,kBAAkB;;;;;;;CAOlB,mBAAmB;;;;;;CAMnB,WAAW;AACb;;;;;;;;;;;;;;AChGA,MAAa,sBAAsB;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;AAUA,MAAa,2BAA2B;CACtC,SAAS;CACT,MAAM;CACN,MAAM;CACN,UAAU;CACV,MAAM;CACN,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,UAAU;CACV,WAAW;CACX,WAAW;CACX,UAAU;CACV,WAAW;CACX,OAAO;CACP,OAAO;CACP,KAAK;CACL,OAAO;CACP,gBAAgB;CAChB,OAAO;CACP,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,aAAa;CACb,SAAS;CACT,WAAW;CACX,QAAQ;CACR,WAAW;CACX,WAAW;CACX,QAAQ;AACV;;;;;AASA,MAAa,sBAAmE,OAAO,OAAO;EAC3F,yBAAyB,OAAO;EAChC,yBAAyB,OAAO;EAChC,yBAAyB,WAAW;EACpC,yBAAyB,OAAO;EAChC,yBAAyB,SAAS;EAClC,yBAAyB,SAAS;EAClC,yBAAyB,UAAU;EACnC,yBAAyB,WAAW;EACpC,yBAAyB,YAAY;EACrC,yBAAyB,YAAY;EACrC,yBAAyB,WAAW;EACpC,yBAAyB,YAAY;EACrC,yBAAyB,QAAQ;EACjC,yBAAyB,QAAQ;EACjC,yBAAyB,MAAM;EAC/B,yBAAyB,QAAQ;EACjC,yBAAyB,iBAAiB;EAC1C,yBAAyB,SAAS;EAClC,yBAAyB,SAAS;AACrC,CAAC;;;;;AAMD,MAAa,sBACX,OAAO,OAAO;CACZ,MAAM,yBAAyB;CAC/B,MAAM,yBAAyB;CAC/B,aAAa,yBAAyB;CACtC,MAAM,yBAAyB;CAC/B,QAAQ,yBAAyB;CACjC,QAAQ,yBAAyB;CACjC,SAAS,yBAAyB;CAClC,UAAU,yBAAyB;CACnC,WAAW,yBAAyB;CACpC,WAAW,yBAAyB;CACpC,UAAU,yBAAyB;CACnC,WAAW,yBAAyB;CACpC,OAAO,yBAAyB;CAChC,OAAO,yBAAyB;CAChC,KAAK,yBAAyB;CAC9B,OAAO,yBAAyB;CAChC,mBAAmB,yBAAyB;CAC5C,QAAQ,yBAAyB;CACjC,QAAQ,yBAAyB;CACjC,aAAa,yBAAyB;AACxC,CAAC;AAEH,SAAgB,gBAAgB,MAAiC;CAC/D,OAAO,oBAAoB,SAAS;AACtC;;;ACzFA,MAAM,YAAY,OAAe,IAAY,OAC3C,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,KAAK,GAAG,EAAE,GAAG,EAAE;;AAG9C,SAAS,iBAAiB,KAAmC,UAAiC;CAC5F,IAAI,KAAK;CACT,IAAI,KAAK,IAAI,SAAS;CACtB,IAAI,OAA6B;CACjC,OAAO,MAAM,IAAI;EACf,MAAM,MAAO,KAAK,MAAO;EACzB,IAAI,IAAI,IAAI,CAAC,MAAM,UAAU;GAC3B,OAAO,IAAI;GACX,KAAK,MAAM;EACb,OACE,KAAK,MAAM;CAEf;CACA,OAAO,QAAQ,CAAC,GAAG,CAAC;AACtB;;;;;;AAOA,SAAgB,mBAAmB,UAA4B,UAA0B;CACvF,MAAM,IAAI,SAAS,UAAU,GAAG,SAAS,SAAS;CAClD,MAAM,MAAM,SAAS;CACrB,IAAI,CAAC,OAAO,IAAI,WAAW,GAAG,OAAO,KAAK,IAAI,GAAG,SAAS,KAAK,MAAM;CACrE,MAAM,CAAC,MAAM,QAAQ,iBAAiB,KAAK,CAAC;CAC5C,OAAO,QAAQ,IAAI;AACrB;;;;;;;;;;;;;;;;AAiBA,SAAgB,yBACd,UACA,QACA,MACQ;CACR,MAAM,SAAS,SAAS,QAAQ,GAAG,SAAS,KAAK,MAAM;CACvD,IAAI,KAAK;CACT,IAAI,KAAK,SAAS;CAClB,IAAI,SAAS,SAAS;EAEpB,OAAO,KAAK,IAAI;GACd,MAAM,MAAM,KAAK,KAAK,MAAM,KAAK,MAAM,CAAC;GACxC,IAAI,mBAAmB,UAAU,GAAG,KAAK,QAAQ,KAAK;QACjD,KAAK,MAAM;EAClB;EACA,OAAO;CACT;CAGA,OAAO,KAAK,IAAI;EACd,MAAM,MAAO,KAAK,MAAO;EACzB,IAAI,mBAAmB,UAAU,GAAG,KAAK,QAAQ,KAAK;OACjD,KAAK,MAAM;CAClB;CACA,OAAO;AACT;;;;;;;;;;;;;AAcA,SAAgB,wBACd,UACA,aACA,WACgC;CAChC,IAAI,aAAa,aAAa;EAC5B,MAAM,IAAI,yBAAyB,UAAU,aAAa,KAAK;EAC/D,OAAO;GAAE,OAAO;GAAG,KAAK;EAAE;CAC5B;CACA,OAAO;EACL,OAAO,yBAAyB,UAAU,aAAa,OAAO;EAC9D,KAAK,yBAAyB,UAAU,WAAW,KAAK;CAC1D;AACF;;;;;;;AAQA,SAAgB,iBACd,UACA,WACA,SACQ;CACR,IAAI,WAAW,WAAW,OAAO;CACjC,OAAO,SAAS,KAAK,MACnB,mBAAmB,UAAU,SAAS,GACtC,mBAAmB,UAAU,OAAO,CACtC;AACF;;;;;;;;;;;;;;;AAgBA,SAAgB,iBACd,WACA,YACA,SACe;CACf,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC,OAAO,cAAc,aACjB,OACA,oCAAoC,UAAU,qBAAqB,WAAW;CAEpF,IAAI,WAAW;CACf,IAAI,WAAW;CACf,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,CAAC,MAAM,QAAQ,QAAQ;EAC7B,IAAI,CAAC,OAAO,UAAU,IAAI,KAAK,CAAC,OAAO,UAAU,IAAI,GACnD,OAAO,UAAU,EAAE;EAErB,IAAI,QAAQ,UACV,OAAO,UAAU,EAAE,QAAQ,KAAK,sBAAsB;EAExD,IAAI,OAAO,WACT,OAAO,UAAU,EAAE,QAAQ,KAAK,eAAe,UAAU;EAE3D,MAAM,OAAO,QAAQ,YAAY,OAAO,WAAW;EACnD,IAAI,SAAS,KAAK,SAAS,GACzB,OAAO,UAAU,EAAE,oCAAoC,KAAK;EAE9D,WAAW;EACX,WAAW;CACb;CACA,MAAM,OAAO,YAAY,YAAY;CACrC,IAAI,SAAS,YACX,OAAO,6BAA6B,KAAK,uBAAuB;CAElE,OAAO;AACT;;;;;;;;;AC7MA,MAAa,8BAA8B;;;ACN3C,MAAM,SAAS,OAAe,cAC5B,MAAM,QAAQ,+BAA+B,SAAS;AACxD,MAAM,OAAO,GAAW,MAAuB,IAAI,IAAI,GAAG,IAAI,CAAC,IAAI;AAEnE,SAAgB,kBAAkB,IAA8B;CAC9D,MAAM,IAAI,KAAK,OAAO,GAAG,cAAc,GAAG;CAC1C,IAAI,CAAC,OAAO,SAAS,CAAC,KAAK,KAAK,GAAG,OAAO;CAC1C,MAAM,IAAI,GAAG,cAAc,GAAG,aAAa,aAAa,KAAK;CAC7D,IAAI,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,KAAK,IAAI,YAAY,OAAO;CAC5D,KAAK,GAAG,YAAY,eAAe,aAAa,CAAC,OAAO,UAAU,KAAK,MAAM,CAAC,CAAC,GAAG,OAAO;CACzF,OAAO;AACT;AAEA,SAAS,KAAK,OAAe,IAA6B;CACxD,MAAM,OAAO,GAAG,YAAY;CAC5B,MAAM,MAAM,GAAG,aAAa;CAC5B,IAAI,SAAS,SAAS,OAAO,MAAM,OAAO,KAAK,MAAM,KAAK,CAAC,GAAG,GAAG;CACjE,IAAI,SAAS,YAAY,OAAO,MAAM,OAAO,KAAK,MAAM,KAAK,CAAC,GAAG,GAAG;CACpE,IAAI,SAAS,YAAY;EACvB,MAAM,cAAc,GAAG,aAAa;EACpC,IAAI,QAAQ,KAAK,MAAM,KAAK;EAC5B,IAAI,YAAY,KAAK,OAAO,QAAQ,SAAS,WAAW;EACxD,IAAI,cAAc,aAAa;GAC7B;GACA,YAAY;EACd;EACA,IAAI,CAAC,WAAW,OAAO,MAAM,OAAO,KAAK,GAAG,GAAG;EAC/C,MAAM,UAAU,GAAG,QAAQ,IAAI,IAAI,WAAW,WAAW;EACzD,OAAO,GAAG,QAAQ,GAAG,MAAM,OAAO,KAAK,GAAG,GAAG,EAAE,KAAK,KAAK,YAAY,QAAQ,GAAG,cAAc;CAChG;CACA,MAAM,SAAS,KAAK,MAAM,GAAG,aAAa,GAAG;CAC7C,MAAM,CAAC,SAAS,UAAU,MAAM,MAAM,QAAQ,MAAM,CAAC,CAAC,MAAM,GAAG;CAC/D,MAAM,OAAO,GAAG,QAAQ,UAAU,QAAQ,QAAQ,OAAO,EAAE;CAC3D,OAAO,MAAM,SAAS,GAAG,KAAK,QAAQ,GAAG,WAAW,OAAO,OAAO;AACpE;;;AAIA,SAAgB,kBAAkB,OAAe,SAA6C;CAC5F,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,CAAC,QAAQ,UAAU,CAAC,QAAQ,MAAM,iBAAiB,GAChF,MAAM,IAAI,WAAW,8DAA8D;CAErF,IAAI,YAAY,KAAK,IAAI,KAAK;CAC9B,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,CAAC,OAAO,OAAO,QAAQ,QAAQ,GAAG;EAC3C,aAAa,KAAK,OAAO,GAAG,UAAW;EACvC,IAAI,CAAC,OAAO,SAAS,SAAS,GAAG,MAAM,IAAI,WAAW,sBAAsB;EAC5E,MAAM,QAAQ,KAAK,MAAM,SAAS;EAClC,MAAM,WAAW,YAAY;EAC7B,MAAM,WAAW,UAAU,QAAQ,SAAS,KAAK,aAAa;EAC9D,MAAM,SAAS,WAAW,KAAK,WAAW,EAAE,IAAI,MAAM,OAAO,KAAK,GAAG,GAAG,aAAa,GAAG;EAExF,MAAM,SAAS,GAAG,kBAAkB;EACpC,MAAM,KAAK,GAAG,kBAAkB,UAAU,UAAU,IAAI,KAAK;EAC7D,MAAM,KAAK,GAAG,kBAAkB,CAAC,UAAU,WAAW,KAAK;EAC3D,MAAM,QAAQ,KAAK,GAAG,OAAO;EAC7B,MAAM,KAAK,SAAS,QAAQ,SAAS,SAAS,KAAK;EACnD,IAAI,UAAU;EACd,YAAY;CACd;CACA,QAAQ,QAAQ,IAAI,MAAM,MAAM,MAAM,KAAK,EAAE;AAC/C"}
|