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