@embedpdf/engine-services 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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { AbortError, EMPTY_TRANSFER, EdgeResolver, EngineError, EngineErrorCode, NO_ANNOTATION_FLAGS, PAGE_INSERT_BLANK_MAX_COUNT, PDF_SUBTYPE_TO_CODE, PdfAnnotationSubtypeCode, SIGNATURE_POLICY_VERSION, UNKNOWN_WEAK_ANNOTATION_STATE, USAGE_INCOMPLETE, appearanceImpactOf, assessmentOf, buildPageTextLayout, buildSnippet, charMapViolation, charRangeForTextOffsets, combine, decodeSubmitFormFlags, deriveProtection, evaluateStep, fieldLockFor, foldOptionsFor, foldText, invalidatesWeakIndexRefs, isValidPageObjectNumber, knownWeakAnnotationState, matchLiteral, matchRegex, normalizePdfRect, parsePdfValue, pdfQuadBounds, restrictionsFor, revisionTokensEqual, richTextParagraphsFromPlainText, richTextPlainText, sameEffectiveValue, serializeError, shiftsExistingAnnotationIndices, sniffBinaryMetadata, subtypeFromCode, textSegmentsForRange, validateSearchQuery, wirePack } from "@embedpdf/engine-core/runtime";
1
+ import { AbortError, EMPTY_TRANSFER, EdgeResolver, EngineError, EngineErrorCode, NO_ANNOTATION_FLAGS, PAGE_INSERT_BLANK_MAX_COUNT, PDF_SUBTYPE_TO_CODE, PdfAnnotationSubtypeCode, SIGNATURE_POLICY_VERSION, UNKNOWN_WEAK_ANNOTATION_STATE, USAGE_INCOMPLETE, appearanceImpactOf, assertPdfFloat, assertWritableMeasure, assessmentOf, buildPageTextLayout, buildSnippet, charMapViolation, charRangeForTextOffsets, combine, decodeSubmitFormFlags, deriveMeasurementLabel, deriveProtection, evaluateStep, fieldLockFor, foldOptionsFor, foldText, invalidatesWeakIndexRefs, isDimension, isReadout, isValidPageObjectNumber, knownWeakAnnotationState, matchLiteral, matchRegex, measurementReadout, normalizePdfRect, parsePdfValue, pdfQuadBounds, restrictionsFor, revisionTokensEqual, richTextParagraphsFromPlainText, richTextPlainText, sameEffectiveValue, serializeError, shiftsExistingAnnotationIndices, sniffBinaryMetadata, subtypeFromCode, textSegmentsForRange, validateSearchQuery, wirePack } from "@embedpdf/engine-core/runtime";
2
2
  import { NULL_PTR } from "@embedpdf/engine-runtime";
3
3
  //#region src/runtime/lifecycle/bootstrap.ts
4
4
  const initialized = /* @__PURE__ */ new WeakSet();
@@ -3643,6 +3643,181 @@ function readInk(fn, mem, annotPtr, base) {
3643
3643
  };
3644
3644
  }
3645
3645
  //#endregion
3646
+ //#region src/features/measure/internal/measureCodec.ts
3647
+ const MEASURE_AXES = {
3648
+ x: 0,
3649
+ y: 1,
3650
+ distance: 2,
3651
+ area: 3,
3652
+ angle: 4,
3653
+ slope: 5
3654
+ };
3655
+ const FRACTIONS = [
3656
+ "decimal",
3657
+ "fraction",
3658
+ "round",
3659
+ "truncate"
3660
+ ];
3661
+ const TEXT = [
3662
+ "thousands",
3663
+ "decimal",
3664
+ "prefixSpacing",
3665
+ "suffixSpacing"
3666
+ ];
3667
+ /** A native rejection after validated input is an internal invariant, never caller validation. */
3668
+ function requireMeasureWrite(ok) {
3669
+ if (!ok) throw new EngineError(EngineErrorCode.Unknown, "Measurement native write failed after preflight");
3670
+ }
3671
+ function readMeasureNumber(mem, call, type = "f32") {
3672
+ return withScratch(mem, 4, (p) => call(p) ? Number(mem.peek(p, type)) : void 0);
3673
+ }
3674
+ function withMeasurePoint(mem, point, call) {
3675
+ if (!point) return call(NULL_PTR);
3676
+ return withScratch(mem, 8, (p) => {
3677
+ mem.poke(p, "f32", point.x);
3678
+ mem.poke(p, "f32", point.y, 4);
3679
+ return call(p);
3680
+ });
3681
+ }
3682
+ function readMeasure(fn, mem, handle) {
3683
+ if (!handle) return void 0;
3684
+ const subtype = fn.EPDFMeasure_GetSubtype(handle);
3685
+ if (subtype !== 1) return { subtype: subtype === 2 ? "GEO" : "unknown" };
3686
+ const formats = (axis) => Array.from({ length: fn.EPDFMeasure_CountFormats(handle, axis) }, (_, i) => {
3687
+ const f = fn.EPDFMeasure_GetFormat(handle, axis, i);
3688
+ const out = { unit: readUtf16String(mem, (b, n) => fn.EPDFNumberFormat_GetUnit(f, b, n)) ?? "" };
3689
+ const conversion = readMeasureNumber(mem, (p) => fn.EPDFNumberFormat_GetConversion(f, p));
3690
+ if (conversion !== void 0) out.conversion = conversion;
3691
+ const fraction = readMeasureNumber(mem, (p) => fn.EPDFNumberFormat_GetFraction(f, p), "i32");
3692
+ if (fraction !== void 0 && FRACTIONS[fraction]) out.fraction = FRACTIONS[fraction];
3693
+ const precision = readMeasureNumber(mem, (p) => fn.EPDFNumberFormat_GetPrecision(f, p), "i32");
3694
+ if (precision !== void 0) out.precision = precision;
3695
+ const fixed = readMeasureNumber(mem, (p) => fn.EPDFNumberFormat_GetFixedDenominator(f, p), "i32");
3696
+ if (fixed !== void 0) out.fixed = !!fixed;
3697
+ const position = readMeasureNumber(mem, (p) => fn.EPDFNumberFormat_GetLabelPosition(f, p), "i32");
3698
+ if (position !== void 0) out.labelPosition = position === 1 ? "prefix" : "suffix";
3699
+ for (const [code, key] of TEXT.entries()) {
3700
+ const text = readUtf16String(mem, (b, n) => fn.EPDFNumberFormat_GetText(f, code, b, n));
3701
+ if (text !== null) out[key] = text;
3702
+ }
3703
+ return out;
3704
+ });
3705
+ const ratio = readUtf16String(mem, (b, n) => fn.EPDFMeasure_GetRatio(handle, b, n));
3706
+ const origin = withScratch(mem, 8, (p) => fn.EPDFMeasure_GetOrigin(handle, p) ? {
3707
+ x: Number(mem.peek(p, "f32")),
3708
+ y: Number(mem.peek(p, "f32", 4))
3709
+ } : void 0);
3710
+ const cyx = readMeasureNumber(mem, (p) => fn.EPDFMeasure_GetCYX(handle, p));
3711
+ const result = {
3712
+ subtype: "RL",
3713
+ x: formats(0),
3714
+ distance: formats(2),
3715
+ area: formats(3),
3716
+ ...ratio !== null ? { ratio } : {},
3717
+ ...origin ? { origin } : {},
3718
+ ...cyx !== void 0 ? { cyx } : {}
3719
+ };
3720
+ for (const key of [
3721
+ "y",
3722
+ "angle",
3723
+ "slope"
3724
+ ]) {
3725
+ const list = formats(MEASURE_AXES[key]);
3726
+ if (list.length) result[key] = list;
3727
+ }
3728
+ return result;
3729
+ }
3730
+ /** The caller supplies a freshly created/reset handle and has completed preflight. */
3731
+ function writeMeasure(fn, mem, handle, value) {
3732
+ requireMeasureWrite(handle);
3733
+ if (value.ratio !== void 0) requireMeasureWrite(writeUtf16String(mem, value.ratio, (p) => fn.EPDFMeasure_SetRatio(handle, p)));
3734
+ if (value.origin) requireMeasureWrite(withMeasurePoint(mem, value.origin, (p) => fn.EPDFMeasure_SetOrigin(handle, p)));
3735
+ if (value.cyx !== void 0) withScratch(mem, 4, (p) => {
3736
+ mem.poke(p, "f32", value.cyx);
3737
+ requireMeasureWrite(fn.EPDFMeasure_SetCYX(handle, p));
3738
+ });
3739
+ for (const [key, axis] of Object.entries(MEASURE_AXES)) for (const nf of value[key] ?? []) {
3740
+ const text = mem.writeU16String(nf.unit);
3741
+ let f;
3742
+ try {
3743
+ f = fn.EPDFMeasure_AddFormat(handle, axis, text, nf.conversion);
3744
+ } finally {
3745
+ mem.free(text);
3746
+ }
3747
+ requireMeasureWrite(f);
3748
+ if (nf.fraction !== void 0) requireMeasureWrite(fn.EPDFNumberFormat_SetFraction(f, FRACTIONS.indexOf(nf.fraction)));
3749
+ if (nf.precision !== void 0) requireMeasureWrite(fn.EPDFNumberFormat_SetPrecision(f, nf.precision));
3750
+ if (nf.fixed !== void 0) requireMeasureWrite(fn.EPDFNumberFormat_SetFixedDenominator(f, nf.fixed));
3751
+ if (nf.labelPosition !== void 0) requireMeasureWrite(fn.EPDFNumberFormat_SetLabelPosition(f, nf.labelPosition === "prefix" ? 1 : 0));
3752
+ for (const [code, key] of TEXT.entries()) if (nf[key] !== void 0) requireMeasureWrite(writeUtf16String(mem, nf[key], (p) => fn.EPDFNumberFormat_SetText(f, code, p)));
3753
+ }
3754
+ }
3755
+ //#endregion
3756
+ //#region src/features/annotations/internal/read/readMeasurementFields.ts
3757
+ function readAnnotationMeasure(fn, mem, annot) {
3758
+ const measure = readMeasure(fn, mem, fn.EPDFAnnot_GetMeasure(annot));
3759
+ return measure ? { measure } : {};
3760
+ }
3761
+ function readLineCaption(fn, mem, annot) {
3762
+ if (![
3763
+ "Cap",
3764
+ "CP",
3765
+ "CO"
3766
+ ].some((key) => fn.FPDFAnnot_HasKey(annot, key))) return void 0;
3767
+ return withScratchN(mem, [
3768
+ 4,
3769
+ 4,
3770
+ 8
3771
+ ], ([enabled, position, offset]) => {
3772
+ if (!fn.EPDFAnnot_GetLineCaption(annot, enabled, position, offset)) return void 0;
3773
+ return {
3774
+ enabled: !!mem.peek(enabled, "i32"),
3775
+ position: mem.peek(position, "i32") === 1 ? "top" : "inline",
3776
+ ...fn.FPDFAnnot_HasKey(annot, "CO") ? { offset: {
3777
+ along: Number(mem.peek(offset, "f32")),
3778
+ perpendicular: Number(mem.peek(offset, "f32", 4))
3779
+ } } : {}
3780
+ };
3781
+ });
3782
+ }
3783
+ function readShapeCaption(fn, mem, annot) {
3784
+ return withScratchN(mem, [
3785
+ 4,
3786
+ 4,
3787
+ 8
3788
+ ], ([enabled, hasCenter, center]) => {
3789
+ if (!fn.EPDFAnnot_GetShapeCaption(annot, enabled, hasCenter, center)) return void 0;
3790
+ const value = !!mem.peek(enabled, "i32"), manual = !!mem.peek(hasCenter, "i32");
3791
+ if (!fn.EPDFAnnot_GetEmbedMetadataBoolean(annot, "MeasurementCaption", enabled) && !manual) return void 0;
3792
+ return {
3793
+ enabled: value,
3794
+ ...manual ? { center: {
3795
+ x: Number(mem.peek(center, "f32")),
3796
+ y: Number(mem.peek(center, "f32", 4))
3797
+ } } : {}
3798
+ };
3799
+ });
3800
+ }
3801
+ function readLineLeader(fn, mem, annot) {
3802
+ if (![
3803
+ "LL",
3804
+ "LLE",
3805
+ "LLO"
3806
+ ].some((key) => fn.FPDFAnnot_HasKey(annot, key))) return void 0;
3807
+ return withScratchN(mem, [
3808
+ 4,
3809
+ 4,
3810
+ 4
3811
+ ], ([length, extension, offset]) => {
3812
+ if (!fn.EPDFAnnot_GetLineLeader(annot, length, extension, offset)) return void 0;
3813
+ return {
3814
+ length: Number(mem.peek(length, "f32")),
3815
+ extension: Number(mem.peek(extension, "f32")),
3816
+ offset: Number(mem.peek(offset, "f32"))
3817
+ };
3818
+ });
3819
+ }
3820
+ //#endregion
3646
3821
  //#region src/features/annotations/internal/read/readLineAnnotation.ts
3647
3822
  /** Fallback `/L` when the annotation has no line geometry. */
3648
3823
  const ZERO_LINE = {
@@ -3657,8 +3832,15 @@ const ZERO_LINE = {
3657
3832
  };
3658
3833
  function readLine(fn, mem, annotPtr, base) {
3659
3834
  const rotation = readAnnotationRotation(fn, mem, annotPtr);
3835
+ const intent = readIntent(fn, mem, annotPtr);
3836
+ const caption = readLineCaption(fn, mem, annotPtr);
3837
+ const leader = readLineLeader(fn, mem, annotPtr);
3660
3838
  return {
3661
3839
  ...base,
3840
+ ...readAnnotationMeasure(fn, mem, annotPtr),
3841
+ ...intent === "LineDimension" || intent === "LineArrow" ? { intent } : {},
3842
+ ...caption ? { caption } : {},
3843
+ ...leader ? { leader } : {},
3662
3844
  subtype: "line",
3663
3845
  ...readFilledStyleExtras(fn, mem, annotPtr),
3664
3846
  linePoints: readLine$1(fn, mem, annotPtr) ?? ZERO_LINE,
@@ -3997,16 +4179,26 @@ function readVertexExtras(fn, mem, annotPtr) {
3997
4179
  };
3998
4180
  }
3999
4181
  function readPolygon(fn, mem, annotPtr, base) {
4182
+ const caption = readShapeCaption(fn, mem, annotPtr);
4183
+ const intent = readIntent(fn, mem, annotPtr);
4000
4184
  return {
4001
4185
  ...base,
4186
+ ...readAnnotationMeasure(fn, mem, annotPtr),
4187
+ ...caption ? { caption } : {},
4188
+ ...intent === "PolygonDimension" || intent === "PolygonCloud" ? { intent } : {},
4002
4189
  subtype: "polygon",
4003
4190
  ...readVertexExtras(fn, mem, annotPtr),
4004
4191
  cloudyIntensity: readBorderEffect(fn, mem, annotPtr)
4005
4192
  };
4006
4193
  }
4007
4194
  function readPolyline(fn, mem, annotPtr, base) {
4195
+ const caption = readShapeCaption(fn, mem, annotPtr);
4196
+ const intent = readIntent(fn, mem, annotPtr);
4008
4197
  return {
4009
4198
  ...base,
4199
+ ...readAnnotationMeasure(fn, mem, annotPtr),
4200
+ ...caption ? { caption } : {},
4201
+ ...intent === "PolyLineDimension" ? { intent } : {},
4010
4202
  subtype: "polyline",
4011
4203
  ...readVertexExtras(fn, mem, annotPtr),
4012
4204
  lineEndings: readLineEndings(fn, mem, annotPtr)
@@ -5107,6 +5299,151 @@ var RawAnnotationReader = class {
5107
5299
  }
5108
5300
  };
5109
5301
  //#endregion
5302
+ //#region src/features/annotations/internal/mutations/prepareMeasurementMutation.ts
5303
+ const kinds = /* @__PURE__ */ new Set([
5304
+ "line",
5305
+ "polygon",
5306
+ "polyline"
5307
+ ]);
5308
+ const inputs = [
5309
+ "linePoints",
5310
+ "vertices",
5311
+ "measure",
5312
+ "intent",
5313
+ "contents"
5314
+ ];
5315
+ function validate(value) {
5316
+ if (!kinds.has(value.subtype)) return;
5317
+ const v = value;
5318
+ try {
5319
+ if (v.measure !== void 0 && v.measure !== null) assertWritableMeasure(v.measure);
5320
+ if (v.caption != null && (typeof v.caption !== "object" || Array.isArray(v.caption))) throw new RangeError("Invalid caption");
5321
+ if (v.subtype === "line" && v.leader != null && (typeof v.leader !== "object" || Array.isArray(v.leader))) throw new RangeError("Invalid line leader");
5322
+ const intents = v.subtype === "line" ? ["LineDimension", "LineArrow"] : v.subtype === "polygon" ? ["PolygonDimension", "PolygonCloud"] : ["PolyLineDimension"];
5323
+ if (v.intent != null && !intents.includes(v.intent)) throw new RangeError("Invalid measurement intent");
5324
+ if (v.caption) {
5325
+ if (v.caption.enabled !== void 0 && typeof v.caption.enabled !== "boolean") throw new RangeError("Invalid caption visibility");
5326
+ if (v.subtype === "line") {
5327
+ if (v.caption.position != null && !["inline", "top"].includes(v.caption.position)) throw new RangeError("Invalid caption position");
5328
+ if (v.caption.offset) {
5329
+ assertPdfFloat(v.caption.offset.along);
5330
+ assertPdfFloat(v.caption.offset.perpendicular);
5331
+ }
5332
+ } else if (v.caption.center) {
5333
+ assertPdfFloat(v.caption.center.x);
5334
+ assertPdfFloat(v.caption.center.y);
5335
+ }
5336
+ }
5337
+ if (v.subtype === "line" && v.leader) {
5338
+ for (const n of [
5339
+ v.leader.length,
5340
+ v.leader.extension ?? 0,
5341
+ v.leader.offset ?? 0
5342
+ ]) assertPdfFloat(n);
5343
+ if ((v.leader.extension ?? 0) < 0 || (v.leader.offset ?? 0) < 0) throw new RangeError("Leader extension and offset must be nonnegative");
5344
+ }
5345
+ const points = v.subtype === "line" ? v.linePoints ? [v.linePoints.start, v.linePoints.end] : [] : v.vertices ?? [];
5346
+ for (const p of points) {
5347
+ assertPdfFloat(p.x);
5348
+ assertPdfFloat(p.y);
5349
+ }
5350
+ } catch (error) {
5351
+ throw new EngineError(EngineErrorCode.InvalidArg, error.message);
5352
+ }
5353
+ }
5354
+ /** Infer a rigid transform only when ALL corresponding vertices agree. Vertex edits leave a manual center fixed. */
5355
+ function moveCenter(before, after, center) {
5356
+ if (before.length !== after.length || before.length < 2) return void 0;
5357
+ const a = before[0], b = after[0];
5358
+ const index = before.findIndex((p) => Math.hypot(p.x - a.x, p.y - a.y) > 1e-6);
5359
+ if (index < 0) return void 0;
5360
+ const u = {
5361
+ x: before[index].x - a.x,
5362
+ y: before[index].y - a.y
5363
+ };
5364
+ const v = {
5365
+ x: after[index].x - b.x,
5366
+ y: after[index].y - b.y
5367
+ };
5368
+ const length = u.x * u.x + u.y * u.y;
5369
+ const cos = (u.x * v.x + u.y * v.y) / length, sin = (u.x * v.y - u.y * v.x) / length;
5370
+ if (Math.abs(cos * cos + sin * sin - 1) > 1e-5) return void 0;
5371
+ const transform = (p) => ({
5372
+ x: b.x + cos * (p.x - a.x) - sin * (p.y - a.y),
5373
+ y: b.y + sin * (p.x - a.x) + cos * (p.y - a.y)
5374
+ });
5375
+ if (!before.every((p, i) => {
5376
+ const t = transform(p);
5377
+ return Math.hypot(t.x - after[i].x, t.y - after[i].y) <= .001;
5378
+ })) return void 0;
5379
+ return transform(center);
5380
+ }
5381
+ function prepareMeasurementDraft(draft) {
5382
+ validate(draft);
5383
+ if ((draft.subtype === "line" || draft.subtype === "polygon" || draft.subtype === "polyline") && draft.caption && draft.caption.enabled === void 0) throw new EngineError(EngineErrorCode.InvalidArg, "A new caption requires enabled");
5384
+ return deriveMeasurementLabel(draft);
5385
+ }
5386
+ function prepareMeasurementPatch(fn, annot, current, patch) {
5387
+ if (patch.subtype !== current.subtype) throw new EngineError(EngineErrorCode.InvalidArg, "Annotation subtype cannot change");
5388
+ validate(patch);
5389
+ if (patch.subtype !== "line" && patch.subtype !== "polygon" && patch.subtype !== "polyline" || current.subtype !== "line" && current.subtype !== "polygon" && current.subtype !== "polyline") return patch;
5390
+ if (patch.measure && current.measure && current.measure.subtype !== "RL") throw new EngineError(EngineErrorCode.InvalidArg, "Remove the foreign measure explicitly before replacing it");
5391
+ if (patch.subtype !== "line" && patch.caption && fn.FPDFAnnot_HasKey(annot, "EMBD_Metadata") && fn.FPDFAnnot_GetValueType(annot, "EMBD_Metadata") !== 6) throw new EngineError(EngineErrorCode.InvalidArg, "Malformed annotation metadata cannot store a caption");
5392
+ if (patch.subtype !== "line" && current.subtype !== "line" && patch.vertices && patch.caption !== null && patch.caption?.center === void 0 && current.caption?.center) {
5393
+ const center = moveCenter(current.vertices, patch.vertices, current.caption.center);
5394
+ if (center) patch = {
5395
+ ...patch,
5396
+ caption: {
5397
+ ...current.caption,
5398
+ ...patch.caption,
5399
+ center
5400
+ }
5401
+ };
5402
+ }
5403
+ if (patch.caption) {
5404
+ if (patch.subtype === "line" && current.subtype === "line") {
5405
+ const c = {
5406
+ enabled: false,
5407
+ ...current.caption,
5408
+ ...patch.caption
5409
+ };
5410
+ patch = {
5411
+ ...patch,
5412
+ caption: {
5413
+ enabled: c.enabled,
5414
+ position: c.position ?? "inline",
5415
+ ...c.offset ? { offset: c.offset } : {}
5416
+ }
5417
+ };
5418
+ } else if (patch.subtype !== "line" && current.subtype !== "line") {
5419
+ const c = {
5420
+ enabled: false,
5421
+ ...current.caption,
5422
+ ...patch.caption
5423
+ };
5424
+ patch = {
5425
+ ...patch,
5426
+ caption: {
5427
+ enabled: c.enabled,
5428
+ ...c.center ? { center: c.center } : {}
5429
+ }
5430
+ };
5431
+ }
5432
+ }
5433
+ if (inputs.some((key) => patch[key] !== void 0)) {
5434
+ const readout = measurementReadout({
5435
+ ...current,
5436
+ ...patch
5437
+ });
5438
+ if (isReadout(readout)) patch = {
5439
+ ...patch,
5440
+ contents: readout.label
5441
+ };
5442
+ }
5443
+ validate(patch);
5444
+ return patch;
5445
+ }
5446
+ //#endregion
5110
5447
  //#region src/features/annotations/internal/write/annotationWritePrimitives.ts
5111
5448
  /**
5112
5449
  * Write-side twin of `annotationReadPrimitives.ts`. Every annotation
@@ -6201,6 +6538,36 @@ function isInkSubtype(subtype) {
6201
6538
  return subtype === "ink";
6202
6539
  }
6203
6540
  //#endregion
6541
+ //#region src/features/annotations/internal/write/writeMeasurementFields.ts
6542
+ /** Receives normalized complete captions from the mutator, never partial state. */
6543
+ function writeMeasurementFields(fn, mem, annot, value) {
6544
+ if (value.intent !== void 0) if (value.intent === null) fn.EPDFAnnot_RemoveKey(annot, "IT");
6545
+ else requireMeasureWrite(fn.EPDFAnnot_SetIntent(annot, value.intent));
6546
+ if (value.measure !== void 0) if (value.measure === null) requireMeasureWrite(fn.EPDFAnnot_RemoveMeasure(annot));
6547
+ else writeMeasure(fn, mem, fn.EPDFAnnot_AddMeasure(annot), value.measure);
6548
+ if (value.subtype === "line") {
6549
+ if (value.leader !== void 0) requireMeasureWrite(fn.EPDFAnnot_SetLineLeader(annot, value.leader?.length ?? 0, value.leader?.extension ?? 0, value.leader?.offset ?? 0));
6550
+ if (value.caption !== void 0) if (value.caption === null) for (const key of [
6551
+ "Cap",
6552
+ "CP",
6553
+ "CO"
6554
+ ]) fn.EPDFAnnot_RemoveKey(annot, key);
6555
+ else {
6556
+ const c = value.caption;
6557
+ requireMeasureWrite(withMeasurePoint(mem, c.offset ? {
6558
+ x: c.offset.along,
6559
+ y: c.offset.perpendicular
6560
+ } : void 0, (p) => fn.EPDFAnnot_SetLineCaption(annot, c.enabled ?? false, c.position === "top" ? 1 : 0, p)));
6561
+ }
6562
+ } else if (value.caption !== void 0) if (value.caption === null) {
6563
+ fn.EPDFAnnot_ClearEmbedMetadataKey(annot, "MeasurementCaption");
6564
+ fn.EPDFAnnot_ClearEmbedMetadataKey(annot, "MeasurementCaptionCenter");
6565
+ } else {
6566
+ const c = value.caption;
6567
+ requireMeasureWrite(withMeasurePoint(mem, c.center ?? void 0, (p) => fn.EPDFAnnot_SetShapeCaption(annot, c.enabled ?? false, p)));
6568
+ }
6569
+ }
6570
+ //#endregion
6204
6571
  //#region src/features/annotations/internal/write/writeLineAnnotation.ts
6205
6572
  /** Default line endings when a line draft omits them. */
6206
6573
  const DEFAULT_LINE_ENDINGS$1 = {
@@ -6217,6 +6584,7 @@ const DEFAULT_LINE_ENDINGS$1 = {
6217
6584
  */
6218
6585
  function applyLineDraft(fn, mem, annotPtr, draft) {
6219
6586
  applyAnnotationBaseDraft(fn, mem, annotPtr, draft);
6587
+ writeMeasurementFields(fn, mem, annotPtr, draft);
6220
6588
  setAnnotRect(fn, mem, annotPtr, draft.rect);
6221
6589
  applyFilledStyleDraft(fn, mem, annotPtr, draft);
6222
6590
  setLine(fn, mem, annotPtr, draft.linePoints);
@@ -6225,6 +6593,7 @@ function applyLineDraft(fn, mem, annotPtr, draft) {
6225
6593
  }
6226
6594
  function applyLinePatch(fn, mem, annotPtr, patch) {
6227
6595
  applyAnnotationBasePatch(fn, mem, annotPtr, patch);
6596
+ writeMeasurementFields(fn, mem, annotPtr, patch);
6228
6597
  if (patch.rect !== void 0) setAnnotRect(fn, mem, annotPtr, patch.rect);
6229
6598
  applyFilledStylePatch(fn, mem, annotPtr, patch);
6230
6599
  if (patch.linePoints !== void 0) setLine(fn, mem, annotPtr, patch.linePoints);
@@ -6883,6 +7252,7 @@ const DEFAULT_LINE_ENDINGS = {
6883
7252
  */
6884
7253
  function applyPolygonDraft(fn, mem, annotPtr, draft) {
6885
7254
  applyAnnotationBaseDraft(fn, mem, annotPtr, draft);
7255
+ writeMeasurementFields(fn, mem, annotPtr, draft);
6886
7256
  setAnnotRect(fn, mem, annotPtr, draft.rect);
6887
7257
  applyFilledStyleDraft(fn, mem, annotPtr, draft);
6888
7258
  setVertices(fn, mem, annotPtr, draft.vertices);
@@ -6891,6 +7261,7 @@ function applyPolygonDraft(fn, mem, annotPtr, draft) {
6891
7261
  }
6892
7262
  function applyPolygonPatch(fn, mem, annotPtr, patch) {
6893
7263
  applyAnnotationBasePatch(fn, mem, annotPtr, patch);
7264
+ writeMeasurementFields(fn, mem, annotPtr, patch);
6894
7265
  if (patch.rect !== void 0) setAnnotRect(fn, mem, annotPtr, patch.rect);
6895
7266
  applyFilledStylePatch(fn, mem, annotPtr, patch);
6896
7267
  if (patch.vertices !== void 0) setVertices(fn, mem, annotPtr, patch.vertices);
@@ -6908,6 +7279,7 @@ function applyPolygonPatch(fn, mem, annotPtr, patch) {
6908
7279
  */
6909
7280
  function applyPolylineDraft(fn, mem, annotPtr, draft) {
6910
7281
  applyAnnotationBaseDraft(fn, mem, annotPtr, draft);
7282
+ writeMeasurementFields(fn, mem, annotPtr, draft);
6911
7283
  setAnnotRect(fn, mem, annotPtr, draft.rect);
6912
7284
  applyFilledStyleDraft(fn, mem, annotPtr, draft);
6913
7285
  setVertices(fn, mem, annotPtr, draft.vertices);
@@ -6916,6 +7288,7 @@ function applyPolylineDraft(fn, mem, annotPtr, draft) {
6916
7288
  }
6917
7289
  function applyPolylinePatch(fn, mem, annotPtr, patch) {
6918
7290
  applyAnnotationBasePatch(fn, mem, annotPtr, patch);
7291
+ writeMeasurementFields(fn, mem, annotPtr, patch);
6919
7292
  if (patch.rect !== void 0) setAnnotRect(fn, mem, annotPtr, patch.rect);
6920
7293
  applyFilledStylePatch(fn, mem, annotPtr, patch);
6921
7294
  if (patch.vertices !== void 0) setVertices(fn, mem, annotPtr, patch.vertices);
@@ -7218,6 +7591,7 @@ var AnnotationMutator = class {
7218
7591
  const writeCtx = this.writeContext(pagePtr, resources);
7219
7592
  preflightDraft(draft, writeCtx);
7220
7593
  assertRichTextAgreement(draft);
7594
+ draft = prepareMeasurementDraft(draft);
7221
7595
  const pageStateBefore = this.session.pageState(pageObjectNumber);
7222
7596
  throwIfAborted(signal);
7223
7597
  const annotPtr = fn.EPDFPage_CreateAnnot(pagePtr, subtypeCode);
@@ -7278,13 +7652,18 @@ var AnnotationMutator = class {
7278
7652
  assertRichTextAgreement(patch);
7279
7653
  this.ensureKnownWeakStateFromPage(ref.pageObjectNumber, pagePtr);
7280
7654
  const pageStateBefore = this.session.pageState(ref.pageObjectNumber);
7281
- throwIfAborted(signal);
7282
- const stableId = this.captureOrStampStableId(annotPtr);
7283
7655
  const previousBlendMode = blendModeFromCode(fn.EPDFAnnot_GetBlendMode(annotPtr));
7284
7656
  const preIndex = fn.FPDFPage_GetAnnotIndex(pagePtr, annotPtr);
7285
7657
  if (preIndex < 0) throw new EngineError(EngineErrorCode.Unknown, `FPDFPage_GetAnnotIndex returned ${preIndex} before update`);
7286
7658
  const currentDto = readAnnotationFromPtr(fn, mem, annotPtr, ref.pageObjectNumber, preIndex, pageStateBefore.revision, readContextFor(this.session, this.fonts));
7659
+ patch = prepareMeasurementPatch(fn, annotPtr, currentDto, patch);
7660
+ throwIfAborted(signal);
7661
+ const stableId = this.captureOrStampStableId(annotPtr);
7287
7662
  applyPatch(fn, mem, annotPtr, patch, writeCtx);
7663
+ if (patch.contents !== void 0 && patch.contents !== currentDto.contents && isDimension({
7664
+ ...currentDto,
7665
+ ...patch
7666
+ })) fn.EPDFAnnot_RemoveKey(annotPtr, "RC");
7288
7667
  let linkedParentId = null;
7289
7668
  if (patch.inReplyTo !== void 0 || patch.replyType !== void 0) linkedParentId = writeAnnotationRelationship(this.runtime, this.session, pagePtr, annotPtr, ref.pageObjectNumber, {
7290
7669
  inReplyTo: patch.inReplyTo,
@@ -8785,6 +9164,14 @@ function layerSaveError(status) {
8785
9164
  return new EngineError(EngineErrorCode.DocOpenFailed, "failed to save layer artifact");
8786
9165
  }
8787
9166
  //#endregion
9167
+ //#region src/features/save/internal/readSavedBuffer.ts
9168
+ /** Keep the runtime's owned copy when it already occupies an entire ArrayBuffer. */
9169
+ function readSavedBuffer(mem, ptr, size) {
9170
+ const bytes = mem.readBytes(ptr, size);
9171
+ if (bytes.buffer instanceof ArrayBuffer && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength) return bytes.buffer;
9172
+ return new Uint8Array(bytes).buffer;
9173
+ }
9174
+ //#endregion
8788
9175
  //#region src/runtime/memory/u64.ts
8789
9176
  /**
8790
9177
  * 64-bit out-params as two little-endian 32-bit halves: the wasm memory
@@ -9410,7 +9797,7 @@ var DocumentSaver = class {
9410
9797
  saveLayerArtifactImpl(reporting) {
9411
9798
  this.requireLayer();
9412
9799
  const { mem, fn } = this.runtime;
9413
- const sizePtr = mem.alloc(4);
9800
+ const sizePtr = mem.alloc(8);
9414
9801
  const statusPtr = mem.alloc(4);
9415
9802
  const changedPtr = mem.alloc(4);
9416
9803
  let artifactPtr = null;
@@ -9469,7 +9856,7 @@ var DocumentSaver = class {
9469
9856
  saveLayerDeltaImpl(reporting) {
9470
9857
  this.requireLayer();
9471
9858
  const { mem, fn } = this.runtime;
9472
- const sizePtr = mem.alloc(4);
9859
+ const sizePtr = mem.alloc(8);
9473
9860
  const statusPtr = mem.alloc(4);
9474
9861
  const changedPtr = mem.alloc(4);
9475
9862
  let deltaPtr = null;
@@ -9520,18 +9907,15 @@ var DocumentSaver = class {
9520
9907
  }
9521
9908
  saveStandaloneToBuffer(mode) {
9522
9909
  const { mem, fn } = this.runtime;
9523
- const sizePtr = mem.alloc(4);
9910
+ const sizePtr = mem.alloc(8);
9524
9911
  let pdfPtr = null;
9525
9912
  try {
9526
9913
  mem.poke(sizePtr, "i32", 0);
9527
9914
  pdfPtr = fn.EPDF_SaveDocumentToOwnedBuffer(this.session.requireDocPtr(), pdfSaveModeFlags(mode), sizePtr);
9528
9915
  const size = Number(mem.peek(sizePtr, "i32"));
9529
9916
  if (!pdfPtr || size <= 0) throw new EngineError(EngineErrorCode.DocOpenFailed, "failed to save document");
9530
- const bytes = mem.readBytes(pdfPtr, size);
9531
- const buffer = new ArrayBuffer(bytes.byteLength);
9532
- new Uint8Array(buffer).set(bytes);
9533
9917
  return {
9534
- bytes: buffer,
9918
+ bytes: readSavedBuffer(mem, pdfPtr, size),
9535
9919
  size
9536
9920
  };
9537
9921
  } finally {
@@ -9548,7 +9932,7 @@ var DocumentSaver = class {
9548
9932
  */
9549
9933
  saveStandaloneToBufferEx(mode) {
9550
9934
  const { mem, fn } = this.runtime;
9551
- const sizePtr = mem.alloc(4);
9935
+ const sizePtr = mem.alloc(8);
9552
9936
  const statusPtr = mem.alloc(4);
9553
9937
  let pdfPtr = null;
9554
9938
  try {
@@ -9563,11 +9947,8 @@ var DocumentSaver = class {
9563
9947
  };
9564
9948
  const size = Number(mem.peek(sizePtr, "i32"));
9565
9949
  if (!pdfPtr || size <= 0 || status !== EPDF_SAVE_WRITTEN) throw new EngineError(EngineErrorCode.DocOpenFailed, "failed to save document");
9566
- const bytes = mem.readBytes(pdfPtr, size);
9567
- const buffer = new ArrayBuffer(bytes.byteLength);
9568
- new Uint8Array(buffer).set(bytes);
9569
9950
  return {
9570
- bytes: buffer,
9951
+ bytes: readSavedBuffer(mem, pdfPtr, size),
9571
9952
  size,
9572
9953
  unchangedSinceLoad: false
9573
9954
  };
@@ -11979,6 +12360,7 @@ function searchQueryKey(query, mode) {
11979
12360
  query.matchCase ? 1 : 0,
11980
12361
  query.matchDiacritics ? 1 : 0,
11981
12362
  query.wholeWord ? 1 : 0,
12363
+ query.ignoreWhitespace ? 1 : 0,
11982
12364
  mode
11983
12365
  ]);
11984
12366
  }
@@ -12079,7 +12461,7 @@ var SearchReader = class {
12079
12461
  const text = corpus.snapshot.text;
12080
12462
  let ranges;
12081
12463
  if (query.regex) ranges = matchRegex(text, query);
12082
- else if (query.matchCase || query.matchDiacritics) ranges = matchLiteral(foldText(text, foldOptionsFor(query)), query);
12464
+ else if (query.matchCase || query.matchDiacritics || query.ignoreWhitespace) ranges = matchLiteral(foldText(text, foldOptionsFor(query)), query);
12083
12465
  else ranges = matchLiteral(corpus.folded, query);
12084
12466
  if (ranges.length > 0) {
12085
12467
  const layout = buildPageTextLayout(new PageGeometryReader(this.runtime, this.session).read(pon, signal));
@@ -12177,6 +12559,100 @@ function requireAction(action, owner, index) {
12177
12559
  throw new EngineError(EngineErrorCode.MalformedPdf, `failed to read ${owner}`, { details: { index } });
12178
12560
  }
12179
12561
  //#endregion
12562
+ //#region src/features/measure/internal/readViewports.ts
12563
+ const CALIBRATION_NAME = "EmbedPDF";
12564
+ function readViewports(fn, mem, page) {
12565
+ const viewports = [];
12566
+ for (let i = 0; i < fn.EPDFPage_CountViewports(page); i++) {
12567
+ const viewport = fn.EPDFPage_GetViewport(page, i);
12568
+ if (!viewport) continue;
12569
+ const bbox = withScratch(mem, 16, (p) => fn.EPDFViewport_GetBBox(viewport, p) ? normalizePdfRect(readRectF(mem, p)) : void 0);
12570
+ if (!bbox) continue;
12571
+ const name = readUtf16String(mem, (p, n) => fn.EPDFViewport_GetName(viewport, p, n));
12572
+ const measure = readMeasure(fn, mem, fn.EPDFViewport_GetMeasure(viewport));
12573
+ viewports.push({
12574
+ bbox,
12575
+ owned: name === "EmbedPDF" && (!measure || measure.subtype === "RL"),
12576
+ ...name !== null ? { name } : {},
12577
+ ...measure ? { measure } : {}
12578
+ });
12579
+ }
12580
+ return viewports;
12581
+ }
12582
+ //#endregion
12583
+ //#region src/features/measure/MeasureReader.ts
12584
+ var MeasureReader = class {
12585
+ constructor(runtime, session) {
12586
+ this.runtime = runtime;
12587
+ this.session = session;
12588
+ }
12589
+ viewports(pon, signal) {
12590
+ throwIfAborted(signal);
12591
+ const pool = this.session.pagePool(), page = pool.acquire(pon);
12592
+ try {
12593
+ return readViewports(this.runtime.fn, this.runtime.mem, page);
12594
+ } finally {
12595
+ pool.release(pon);
12596
+ }
12597
+ }
12598
+ };
12599
+ //#endregion
12600
+ //#region src/features/measure/MeasureMutator.ts
12601
+ var MeasureMutator = class {
12602
+ constructor(runtime, session) {
12603
+ this.runtime = runtime;
12604
+ this.session = session;
12605
+ }
12606
+ setScale(pon, measure, signal) {
12607
+ throwIfAborted(signal);
12608
+ if (measure !== null) try {
12609
+ assertWritableMeasure(measure);
12610
+ } catch (e) {
12611
+ throw new EngineError(EngineErrorCode.InvalidArg, e.message);
12612
+ }
12613
+ const { fn, mem } = this.runtime, pool = this.session.pagePool(), page = pool.acquire(pon);
12614
+ try {
12615
+ const owned = [];
12616
+ for (let i = 0; i < fn.EPDFPage_CountViewports(page); i++) {
12617
+ const vp = fn.EPDFPage_GetViewport(page, i);
12618
+ if (!vp || readUtf16String(mem, (p, n) => fn.EPDFViewport_GetName(vp, p, n)) !== "EmbedPDF") continue;
12619
+ const m = fn.EPDFViewport_GetMeasure(vp);
12620
+ if (!m || fn.EPDFMeasure_GetSubtype(m) === 1) owned.push(i);
12621
+ }
12622
+ const bbox = withScratch(mem, 16, (p) => {
12623
+ const index = this.session.recordByObjectNumber(pon).pageIndex;
12624
+ const doc = this.session.requireDocPtr();
12625
+ if (!fn.EPDF_GetPageBoxByIndex(doc, index, 1, p) && !fn.EPDF_GetPageBoxByIndex(doc, index, 0, p)) throw new EngineError(EngineErrorCode.InvalidArg, "Page has no valid calibration box");
12626
+ return normalizePdfRect(readRectF(mem, p));
12627
+ });
12628
+ if (![
12629
+ bbox.left,
12630
+ bbox.right,
12631
+ bbox.bottom,
12632
+ bbox.top
12633
+ ].every(Number.isFinite)) throw new EngineError(EngineErrorCode.InvalidArg, "Invalid calibration box");
12634
+ throwIfAborted(signal);
12635
+ if (measure === null) {
12636
+ for (const i of owned.reverse()) requireMeasureWrite(fn.EPDFPage_RemoveViewport(page, i));
12637
+ return;
12638
+ }
12639
+ withScratch(mem, 16, (p) => {
12640
+ mem.poke(p, "f32", bbox.left);
12641
+ mem.poke(p, "f32", bbox.top, 4);
12642
+ mem.poke(p, "f32", bbox.right, 8);
12643
+ mem.poke(p, "f32", bbox.bottom, 12);
12644
+ const vp = owned.length ? fn.EPDFPage_GetViewport(page, owned[owned.length - 1]) : fn.EPDFPage_AddViewport(page, p);
12645
+ requireMeasureWrite(vp);
12646
+ requireMeasureWrite(fn.EPDFViewport_SetBBox(vp, p));
12647
+ requireMeasureWrite(writeUtf16String(mem, CALIBRATION_NAME, (name) => fn.EPDFViewport_SetName(vp, name)));
12648
+ writeMeasure(fn, mem, fn.EPDFViewport_AddMeasure(vp), measure);
12649
+ });
12650
+ } finally {
12651
+ pool.release(pon);
12652
+ }
12653
+ }
12654
+ };
12655
+ //#endregion
12180
12656
  //#region src/features/pieceinfo/PieceInfoAccessor.ts
12181
12657
  const OBJ_BOOLEAN = 1;
12182
12658
  const OBJ_NUMBER = 2;
@@ -12577,6 +13053,12 @@ var WorkerHost = class {
12577
13053
  case "annotations.readFile":
12578
13054
  resultPack = this.handleAnnotationsReadFile(msg, ctrl.signal);
12579
13055
  break;
13056
+ case "measure.viewports":
13057
+ resultPack = this.handleMeasureViewports(msg, ctrl.signal);
13058
+ break;
13059
+ case "measure.setScale":
13060
+ resultPack = this.handleMeasureSetScale(msg, ctrl.signal);
13061
+ break;
12580
13062
  case "pieceInfo.read":
12581
13063
  resultPack = this.handlePieceInfoRead(msg, ctrl.signal);
12582
13064
  break;
@@ -13107,6 +13589,27 @@ var WorkerHost = class {
13107
13589
  content
13108
13590
  }, content.bytes !== void 0 ? [content.bytes] : []);
13109
13591
  }
13592
+ handleMeasureViewports(req, signal) {
13593
+ const session = this.requireSession(req);
13594
+ return wirePack({
13595
+ tag: "measure.viewports",
13596
+ viewports: new MeasureReader(this.runtime, session).viewports(req.pageObjectNumber, signal)
13597
+ });
13598
+ }
13599
+ handleMeasureSetScale(req, signal) {
13600
+ const session = this.requireSession(req);
13601
+ new MeasureMutator(this.runtime, session).setScale(req.pageObjectNumber, req.measure, signal);
13602
+ return this.finishMutation(session, {
13603
+ tag: "measure.setScale",
13604
+ result: {
13605
+ pageObjectNumber: req.pageObjectNumber,
13606
+ meta: {
13607
+ affectedPages: [],
13608
+ cacheDelta: null
13609
+ }
13610
+ }
13611
+ }, req.artifactPath);
13612
+ }
13110
13613
  handlePieceInfoRead(req, signal) {
13111
13614
  const session = this.requireSession(req);
13112
13615
  return wirePack({
@@ -13655,6 +14158,7 @@ const MUTATING_KINDS = /* @__PURE__ */ new Set([
13655
14158
  "redaction.apply",
13656
14159
  "attachments.create",
13657
14160
  "attachments.delete",
14161
+ "measure.setScale",
13658
14162
  "pieceInfo.update",
13659
14163
  "pieceInfo.clear"
13660
14164
  ]);