@extend-ai/react-docx 0.7.0-alpha.1 → 0.7.0-alpha.3

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,14 +1,16 @@
1
1
  import {
2
2
  createMinimalDocxPackage,
3
3
  getPart,
4
+ initWasm,
4
5
  mapsToWasmPackage,
5
6
  packageToArrayBuffer,
6
7
  parseDocx,
8
+ setWasmSource,
7
9
  wasmBuildDocModelFromPackage,
8
10
  wasmModelToDocumentXml,
9
11
  wasmSerializeDocx,
10
12
  withPart
11
- } from "./chunk-AWFSO3IU.js";
13
+ } from "./chunk-BGCGPO6Q.js";
12
14
 
13
15
  // src/index.tsx
14
16
  import * as React2 from "react";
@@ -636,7 +638,7 @@ async function buildDocModel(pkg) {
636
638
  return normalizeDocModel(model);
637
639
  }
638
640
  async function buildDocModelFromBytes(bytes) {
639
- const { parseDocx: parseDocx2 } = await import("./src-JDYUD5TO.js");
641
+ const { parseDocx: parseDocx2 } = await import("./src-XA2NEWYM.js");
640
642
  const payload = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
641
643
  const buffer = payload.buffer.slice(payload.byteOffset, payload.byteOffset + payload.byteLength);
642
644
  const pkg = await parseDocx2(buffer);
@@ -2007,7 +2009,7 @@ async function serializeDocModel(model, basePackage) {
2007
2009
  model,
2008
2010
  basePackage ? mapsToWasmPackage(basePackage) : void 0
2009
2011
  );
2010
- const { parseDocx: parseDocx2 } = await import("./src-JDYUD5TO.js");
2012
+ const { parseDocx: parseDocx2 } = await import("./src-XA2NEWYM.js");
2011
2013
  return parseDocx2(bytes);
2012
2014
  }
2013
2015
  async function serializeDocx(model, basePackage) {
@@ -2176,16 +2178,17 @@ function shouldAllowStoredPageCountReduction(options) {
2176
2178
  if (targetPageCount >= estimatedPageCount) {
2177
2179
  return true;
2178
2180
  }
2181
+ const renderedBreakHintPageCount = Number.isFinite(
2182
+ options.renderedBreakHintPageCount
2183
+ ) ? Math.max(1, Math.round(options.renderedBreakHintPageCount)) : void 0;
2184
+ const renderedBreakHintsSupportTarget = options.hasLastRenderedPageBreakHints === true && renderedBreakHintPageCount !== void 0 && targetPageCount >= renderedBreakHintPageCount;
2179
2185
  if (options.hasMeasuredBodyFooterOverlap === true) {
2180
- return false;
2186
+ return renderedBreakHintsSupportTarget;
2181
2187
  }
2182
2188
  if (options.hasLastRenderedPageBreakHints !== true) {
2183
2189
  return true;
2184
2190
  }
2185
- const renderedBreakHintPageCount = Number.isFinite(
2186
- options.renderedBreakHintPageCount
2187
- ) ? Math.max(1, Math.round(options.renderedBreakHintPageCount)) : void 0;
2188
- return renderedBreakHintPageCount !== void 0 && targetPageCount >= renderedBreakHintPageCount;
2191
+ return renderedBreakHintsSupportTarget;
2189
2192
  }
2190
2193
  function shouldLatchMeasuredBodyFooterOverlap(options) {
2191
2194
  if (options.measuredBodyFooterOverlap !== true) {
@@ -3295,6 +3298,423 @@ function sliceLayoutToLineRange(layout, startLineIndex, endLineIndex) {
3295
3298
  };
3296
3299
  }
3297
3300
 
3301
+ // src/content-signature.ts
3302
+ var FNV_OFFSET_BASIS = 2166136261;
3303
+ var FNV_PRIME = 16777619;
3304
+ var LONG_STRING_SAMPLE_LENGTH = 64;
3305
+ var LONG_STRING_THRESHOLD = 256;
3306
+ function fnv1aAppend(hash, text) {
3307
+ let next = hash;
3308
+ for (let index = 0; index < text.length; index += 1) {
3309
+ next ^= text.charCodeAt(index);
3310
+ next = Math.imul(next, FNV_PRIME);
3311
+ }
3312
+ return next >>> 0;
3313
+ }
3314
+ function fnv1aAppendString(hash, value) {
3315
+ if (value.length <= LONG_STRING_THRESHOLD) {
3316
+ return fnv1aAppend(fnv1aAppend(hash, `${value.length}:`), value);
3317
+ }
3318
+ const head = value.slice(0, LONG_STRING_SAMPLE_LENGTH);
3319
+ const middleIndex = Math.floor(value.length / 2);
3320
+ const middle = value.slice(
3321
+ middleIndex,
3322
+ middleIndex + LONG_STRING_SAMPLE_LENGTH
3323
+ );
3324
+ const tail = value.slice(value.length - LONG_STRING_SAMPLE_LENGTH);
3325
+ let next = fnv1aAppend(hash, `${value.length}:`);
3326
+ next = fnv1aAppend(next, head);
3327
+ next = fnv1aAppend(next, middle);
3328
+ return fnv1aAppend(next, tail);
3329
+ }
3330
+ function fnv1aAppendValue(hash, value) {
3331
+ if (value === null) {
3332
+ return fnv1aAppend(hash, "~n");
3333
+ }
3334
+ switch (typeof value) {
3335
+ case "undefined":
3336
+ return fnv1aAppend(hash, "~u");
3337
+ case "string":
3338
+ return fnv1aAppendString(fnv1aAppend(hash, "~s"), value);
3339
+ case "number":
3340
+ return fnv1aAppend(hash, `~#${value}`);
3341
+ case "boolean":
3342
+ return fnv1aAppend(hash, value ? "~t" : "~f");
3343
+ case "object":
3344
+ break;
3345
+ default:
3346
+ return fnv1aAppend(hash, "~x");
3347
+ }
3348
+ if (Array.isArray(value)) {
3349
+ let next2 = fnv1aAppend(hash, `~a${value.length}`);
3350
+ for (const entry of value) {
3351
+ next2 = fnv1aAppendValue(next2, entry);
3352
+ }
3353
+ return next2;
3354
+ }
3355
+ if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
3356
+ const byteLength = value instanceof ArrayBuffer ? value.byteLength : value.byteLength;
3357
+ return fnv1aAppend(hash, `~b${byteLength}`);
3358
+ }
3359
+ let next = fnv1aAppend(hash, "~o");
3360
+ for (const key of Object.keys(value)) {
3361
+ const entry = value[key];
3362
+ if (entry === void 0) {
3363
+ continue;
3364
+ }
3365
+ next = fnv1aAppend(next, key);
3366
+ next = fnv1aAppendValue(next, entry);
3367
+ }
3368
+ return next;
3369
+ }
3370
+ function contentHash(value) {
3371
+ return fnv1aAppendValue(FNV_OFFSET_BASIS, value).toString(36);
3372
+ }
3373
+ var nodeSignatureCache = /* @__PURE__ */ new WeakMap();
3374
+ function docNodeContentSignature(node) {
3375
+ if (typeof node !== "object" || node === null) {
3376
+ return contentHash(node);
3377
+ }
3378
+ const cached = nodeSignatureCache.get(node);
3379
+ if (cached !== void 0) {
3380
+ return cached;
3381
+ }
3382
+ const signature = contentHash(node);
3383
+ nodeSignatureCache.set(node, signature);
3384
+ return signature;
3385
+ }
3386
+ var metadataSignatureCache = /* @__PURE__ */ new WeakMap();
3387
+ function docModelThumbnailMetadataSignature(metadata) {
3388
+ const cached = metadataSignatureCache.get(metadata);
3389
+ if (cached !== void 0) {
3390
+ return cached;
3391
+ }
3392
+ const relevant = metadata;
3393
+ let hash = FNV_OFFSET_BASIS;
3394
+ hash = fnv1aAppendValue(hash, relevant.sections);
3395
+ hash = fnv1aAppendValue(hash, relevant.headerSections);
3396
+ hash = fnv1aAppendValue(hash, relevant.footerSections);
3397
+ hash = fnv1aAppendValue(hash, relevant.numberingDefinitions);
3398
+ hash = fnv1aAppendValue(hash, relevant.footnotes);
3399
+ hash = fnv1aAppendValue(hash, relevant.endnotes);
3400
+ hash = fnv1aAppendValue(hash, relevant.documentBackgroundColor);
3401
+ hash = fnv1aAppendValue(hash, relevant.compatibility);
3402
+ const signature = (hash >>> 0).toString(36);
3403
+ metadataSignatureCache.set(metadata, signature);
3404
+ return signature;
3405
+ }
3406
+
3407
+ // src/thumbnail-raster.ts
3408
+ var DOCX_THUMBNAIL_EXCLUDE_ATTRIBUTE = "data-docx-thumbnail-exclude";
3409
+ var THUMBNAIL_EXCLUDED_CLONE_SELECTOR = [
3410
+ `[${DOCX_THUMBNAIL_EXCLUDE_ATTRIBUTE}="true"]`,
3411
+ "textarea",
3412
+ '[data-image-resize-handle="true"]',
3413
+ '[data-docx-table-move-handle="true"]'
3414
+ ].join(",");
3415
+ var THUMBNAIL_IMAGE_DOWNSCALE_MIN_DATA_URI_LENGTH = 32768;
3416
+ var THUMBNAIL_IMAGE_DOWNSCALE_MAX_DIMENSION_PX = 512;
3417
+ var THUMBNAIL_IMAGE_JPEG_QUALITY = 0.78;
3418
+ function thumbnailSvgDataUri(svg) {
3419
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
3420
+ }
3421
+ function thumbnailImageSourceQualifiesForDownscale(src) {
3422
+ return src.length >= THUMBNAIL_IMAGE_DOWNSCALE_MIN_DATA_URI_LENGTH && src.startsWith("data:image/") && !src.startsWith("data:image/svg");
3423
+ }
3424
+ async function loadThumbnailImage(src) {
3425
+ const image = new Image();
3426
+ image.decoding = "async";
3427
+ const loaded = new Promise((resolve, reject) => {
3428
+ image.onload = () => resolve(image);
3429
+ image.onerror = () => {
3430
+ reject(new Error("Failed to decode DOCX thumbnail image."));
3431
+ };
3432
+ });
3433
+ image.src = src;
3434
+ if (typeof image.decode === "function") {
3435
+ try {
3436
+ await image.decode();
3437
+ return image;
3438
+ } catch {
3439
+ }
3440
+ }
3441
+ return loaded;
3442
+ }
3443
+ var downscaledThumbnailImageCache = /* @__PURE__ */ new Map();
3444
+ async function downscaleThumbnailImageDataUri(src) {
3445
+ if (typeof document === "undefined") {
3446
+ return void 0;
3447
+ }
3448
+ const image = await loadThumbnailImage(src);
3449
+ const naturalWidth = image.naturalWidth || image.width;
3450
+ const naturalHeight = image.naturalHeight || image.height;
3451
+ if (!naturalWidth || !naturalHeight) {
3452
+ return void 0;
3453
+ }
3454
+ const scale = THUMBNAIL_IMAGE_DOWNSCALE_MAX_DIMENSION_PX / Math.max(naturalWidth, naturalHeight);
3455
+ if (scale >= 1) {
3456
+ return void 0;
3457
+ }
3458
+ const canvas = document.createElement("canvas");
3459
+ canvas.width = Math.max(1, Math.round(naturalWidth * scale));
3460
+ canvas.height = Math.max(1, Math.round(naturalHeight * scale));
3461
+ const context = canvas.getContext("2d");
3462
+ if (!context) {
3463
+ return void 0;
3464
+ }
3465
+ context.imageSmoothingEnabled = true;
3466
+ context.imageSmoothingQuality = "high";
3467
+ context.drawImage(image, 0, 0, canvas.width, canvas.height);
3468
+ const sourceIsJpeg = src.startsWith("data:image/jpeg") || src.startsWith("data:image/jpg");
3469
+ const downscaled = sourceIsJpeg ? canvas.toDataURL("image/jpeg", THUMBNAIL_IMAGE_JPEG_QUALITY) : canvas.toDataURL("image/png");
3470
+ return downscaled.length < src.length ? downscaled : void 0;
3471
+ }
3472
+ function getDownscaledThumbnailImageDataUri(src) {
3473
+ const cached = downscaledThumbnailImageCache.get(src);
3474
+ if (cached) {
3475
+ return cached;
3476
+ }
3477
+ const pending = downscaleThumbnailImageDataUri(src).catch(() => void 0);
3478
+ downscaledThumbnailImageCache.set(src, pending);
3479
+ return pending;
3480
+ }
3481
+ async function buildDocxThumbnailSvgMarkup(params) {
3482
+ const { pageElement, sourceWidthPx, sourceHeightPx, widthPx, heightPx } = params;
3483
+ const clone = pageElement.cloneNode(true);
3484
+ clone.querySelectorAll(THUMBNAIL_EXCLUDED_CLONE_SELECTOR).forEach((excluded) => {
3485
+ excluded.remove();
3486
+ });
3487
+ const cloneImages = Array.from(clone.querySelectorAll("img"));
3488
+ await Promise.all(
3489
+ cloneImages.map(async (cloneImage) => {
3490
+ const src = cloneImage.getAttribute("src");
3491
+ if (!src || !thumbnailImageSourceQualifiesForDownscale(src)) {
3492
+ return;
3493
+ }
3494
+ const downscaled = await getDownscaledThumbnailImageDataUri(src);
3495
+ if (downscaled) {
3496
+ cloneImage.setAttribute("src", downscaled);
3497
+ }
3498
+ })
3499
+ );
3500
+ const scaleX = widthPx / sourceWidthPx;
3501
+ const scaleY = heightPx / sourceHeightPx;
3502
+ const serializedPage = new XMLSerializer().serializeToString(clone);
3503
+ return `
3504
+ <svg xmlns="http://www.w3.org/2000/svg" width="${widthPx}" height="${heightPx}" viewBox="0 0 ${widthPx} ${heightPx}">
3505
+ <foreignObject x="0" y="0" width="100%" height="100%">
3506
+ <div xmlns="http://www.w3.org/1999/xhtml" style="width:${widthPx}px;height:${heightPx}px;overflow:hidden;">
3507
+ <div style="width:${sourceWidthPx}px;height:${sourceHeightPx}px;transform-origin:top left;transform:scale(${scaleX}, ${scaleY});">
3508
+ ${serializedPage}
3509
+ </div>
3510
+ </div>
3511
+ </foreignObject>
3512
+ </svg>
3513
+ `;
3514
+ }
3515
+ async function rasterizeDocxThumbnailSurface(params) {
3516
+ if (typeof window === "undefined" || typeof XMLSerializer === "undefined") {
3517
+ throw new Error("DOCX thumbnails require a browser environment.");
3518
+ }
3519
+ const safeSourceWidthPx = Math.max(1, Math.round(params.sourceWidthPx));
3520
+ const safeSourceHeightPx = Math.max(1, Math.round(params.sourceHeightPx));
3521
+ const svgMarkup = await buildDocxThumbnailSvgMarkup({
3522
+ pageElement: params.pageElement,
3523
+ sourceWidthPx: safeSourceWidthPx,
3524
+ sourceHeightPx: safeSourceHeightPx,
3525
+ widthPx: params.widthPx,
3526
+ heightPx: params.heightPx
3527
+ });
3528
+ const image = await loadThumbnailImage(thumbnailSvgDataUri(svgMarkup));
3529
+ const surface = document.createElement("canvas");
3530
+ surface.width = Math.max(1, Math.round(params.pixelWidthPx));
3531
+ surface.height = Math.max(1, Math.round(params.pixelHeightPx));
3532
+ const context = surface.getContext("2d");
3533
+ if (!context) {
3534
+ throw new Error("2D canvas context is unavailable for DOCX thumbnails.");
3535
+ }
3536
+ context.imageSmoothingEnabled = true;
3537
+ context.imageSmoothingQuality = "high";
3538
+ context.drawImage(image, 0, 0, surface.width, surface.height);
3539
+ return surface;
3540
+ }
3541
+ function blitDocxThumbnailSurface(surface, canvas, resolution) {
3542
+ canvas.width = Math.max(1, Math.round(resolution.pixelWidthPx));
3543
+ canvas.height = Math.max(1, Math.round(resolution.pixelHeightPx));
3544
+ canvas.style.width = `${Math.max(1, Math.round(resolution.widthPx))}px`;
3545
+ canvas.style.height = `${Math.max(1, Math.round(resolution.heightPx))}px`;
3546
+ const context = canvas.getContext("2d");
3547
+ if (!context) {
3548
+ throw new Error("2D canvas context is unavailable for DOCX thumbnails.");
3549
+ }
3550
+ context.setTransform(1, 0, 0, 1, 0, 0);
3551
+ context.clearRect(0, 0, canvas.width, canvas.height);
3552
+ context.drawImage(surface, 0, 0, canvas.width, canvas.height);
3553
+ }
3554
+ var DocxThumbnailSurfaceCache = class {
3555
+ constructor(maxEntries) {
3556
+ this.maxEntries = maxEntries;
3557
+ }
3558
+ entries = /* @__PURE__ */ new Map();
3559
+ get size() {
3560
+ return this.entries.size;
3561
+ }
3562
+ get(key) {
3563
+ const value = this.entries.get(key);
3564
+ if (value === void 0) {
3565
+ return void 0;
3566
+ }
3567
+ this.entries.delete(key);
3568
+ this.entries.set(key, value);
3569
+ return value;
3570
+ }
3571
+ set(key, value) {
3572
+ this.entries.delete(key);
3573
+ this.entries.set(key, value);
3574
+ while (this.entries.size > this.maxEntries) {
3575
+ const oldestKey = this.entries.keys().next().value;
3576
+ if (oldestKey === void 0) {
3577
+ break;
3578
+ }
3579
+ this.entries.delete(oldestKey);
3580
+ }
3581
+ }
3582
+ clear() {
3583
+ this.entries.clear();
3584
+ }
3585
+ };
3586
+ var IDLE_TASK_TIMEOUT_MS = 300;
3587
+ function defaultScheduleTask(callback) {
3588
+ const idleWindow = typeof window === "undefined" ? void 0 : window;
3589
+ if (!idleWindow || typeof idleWindow.requestIdleCallback !== "function") {
3590
+ setTimeout(callback, 16);
3591
+ return;
3592
+ }
3593
+ let invoked = false;
3594
+ const runOnce = () => {
3595
+ if (invoked) {
3596
+ return;
3597
+ }
3598
+ invoked = true;
3599
+ callback();
3600
+ };
3601
+ const idleHandle = idleWindow.requestIdleCallback(runOnce, {
3602
+ timeout: IDLE_TASK_TIMEOUT_MS
3603
+ });
3604
+ setTimeout(() => {
3605
+ if (invoked) {
3606
+ return;
3607
+ }
3608
+ if (typeof idleWindow.cancelIdleCallback === "function") {
3609
+ idleWindow.cancelIdleCallback(idleHandle);
3610
+ }
3611
+ runOnce();
3612
+ }, IDLE_TASK_TIMEOUT_MS + 50);
3613
+ }
3614
+ function defaultScheduleDelayed(callback, delayMs) {
3615
+ setTimeout(callback, delayMs);
3616
+ }
3617
+ var SerialIdleTaskQueue = class {
3618
+ pending = [];
3619
+ lastRunAtByKey = /* @__PURE__ */ new Map();
3620
+ scheduleTask;
3621
+ scheduleDelayed;
3622
+ minTaskIntervalMs;
3623
+ now;
3624
+ pumpScheduled = false;
3625
+ running = false;
3626
+ constructor(options) {
3627
+ this.scheduleTask = options?.scheduleTask ?? defaultScheduleTask;
3628
+ this.scheduleDelayed = options?.scheduleDelayed ?? defaultScheduleDelayed;
3629
+ this.minTaskIntervalMs = Math.max(0, options?.minTaskIntervalMs ?? 0);
3630
+ this.now = options?.now ?? (() => Date.now());
3631
+ }
3632
+ get pendingCount() {
3633
+ return this.pending.length;
3634
+ }
3635
+ enqueue(key, run) {
3636
+ return new Promise((resolve) => {
3637
+ const existing = this.pending.find((entry) => entry.key === key);
3638
+ if (existing) {
3639
+ existing.run = run;
3640
+ existing.resolvers.push(resolve);
3641
+ } else {
3642
+ this.pending.push({ key, run, resolvers: [resolve] });
3643
+ }
3644
+ this.schedulePump();
3645
+ });
3646
+ }
3647
+ /** Drops all queued tasks, resolving their waiters without running them. */
3648
+ clear() {
3649
+ const dropped = this.pending.splice(0, this.pending.length);
3650
+ this.lastRunAtByKey.clear();
3651
+ dropped.forEach((entry) => {
3652
+ entry.resolvers.forEach((resolveEntry) => {
3653
+ resolveEntry();
3654
+ });
3655
+ });
3656
+ }
3657
+ schedulePump() {
3658
+ if (this.pumpScheduled || this.running || this.pending.length === 0) {
3659
+ return;
3660
+ }
3661
+ this.pumpScheduled = true;
3662
+ this.scheduleTask(() => {
3663
+ this.pumpScheduled = false;
3664
+ void this.runNext();
3665
+ });
3666
+ }
3667
+ takeNextEligibleEntry() {
3668
+ if (this.pending.length === 0) {
3669
+ return void 0;
3670
+ }
3671
+ const now = this.now();
3672
+ let earliestWaitMs;
3673
+ for (let index = 0; index < this.pending.length; index += 1) {
3674
+ const candidate = this.pending[index];
3675
+ if (!candidate) {
3676
+ continue;
3677
+ }
3678
+ const lastRunAt = this.lastRunAtByKey.get(candidate.key);
3679
+ const waitMs = lastRunAt === void 0 ? 0 : lastRunAt + this.minTaskIntervalMs - now;
3680
+ if (waitMs <= 0) {
3681
+ this.pending.splice(index, 1);
3682
+ return { entry: candidate };
3683
+ }
3684
+ earliestWaitMs = earliestWaitMs === void 0 ? waitMs : Math.min(earliestWaitMs, waitMs);
3685
+ }
3686
+ return earliestWaitMs === void 0 ? void 0 : { retryDelayMs: earliestWaitMs };
3687
+ }
3688
+ async runNext() {
3689
+ if (this.running) {
3690
+ return;
3691
+ }
3692
+ const next = this.takeNextEligibleEntry();
3693
+ if (!next) {
3694
+ return;
3695
+ }
3696
+ if (!("entry" in next)) {
3697
+ this.scheduleDelayed(() => {
3698
+ this.schedulePump();
3699
+ }, next.retryDelayMs);
3700
+ return;
3701
+ }
3702
+ this.running = true;
3703
+ const { entry } = next;
3704
+ try {
3705
+ await entry.run();
3706
+ } catch {
3707
+ } finally {
3708
+ this.lastRunAtByKey.set(entry.key, this.now());
3709
+ this.running = false;
3710
+ entry.resolvers.forEach((resolveEntry) => {
3711
+ resolveEntry();
3712
+ });
3713
+ this.schedulePump();
3714
+ }
3715
+ }
3716
+ };
3717
+
3298
3718
  // src/editor.tsx
3299
3719
  import { Fragment as Fragment2, jsx, jsxs } from "react/jsx-runtime";
3300
3720
  var HIGHLIGHT_TO_CSS = {
@@ -3391,6 +3811,9 @@ var DEFAULT_PARAGRAPH_LINE_MULTIPLE = 1;
3391
3811
  var WORD_SINGLE_LINE_AUTO_SCALE = 0.88;
3392
3812
  var WORD_SINGLE_LINE_AUTO_SCALE_SANS = 0.9;
3393
3813
  var WORD_SINGLE_LINE_AUTO_SCALE_SERIF = 1.08;
3814
+ var WORD_EMPTY_PARAGRAPH_LINE_SCALE = 1.21;
3815
+ var WORD_EMPTY_PARAGRAPH_LINE_SCALE_SERIF = 1.15;
3816
+ var WORD_EMPTY_PARAGRAPH_LINE_SCALE_SANS = 1.15;
3394
3817
  var WORD_AUTO_LINE_SCALE_BLEND_END_MULTIPLE = 1.08;
3395
3818
  var MIN_AUTO_LINE_MULTIPLE = 0.1;
3396
3819
  var MIN_PARAGRAPH_LINE_HEIGHT_PX = 14;
@@ -3435,7 +3858,6 @@ var TEXT_MEASURE_CACHE_MAX_ENTRIES = 12e3;
3435
3858
  var DEFAULT_TAB_STOP_PX = 48;
3436
3859
  var TAB_LEADER_ZONE_GAP_PX = 20;
3437
3860
  var EMPTY_PARAGRAPH_EXTRA_HEIGHT_PX = 0;
3438
- var LEADING_COVER_SPACER_EXTRA_HEIGHT_PX = 2;
3439
3861
  var PARAGRAPH_SEGMENT_TOP_BLEED_PX = 22;
3440
3862
  var PARAGRAPH_SEGMENT_DESCENDER_BLEED_PX = 6;
3441
3863
  var PARAGRAPH_SEGMENT_VISUAL_SAFETY_PX = 24;
@@ -4302,22 +4724,46 @@ function parseSectionColumns(sectionPropertiesXml) {
4302
4724
  if (!columnsTag) {
4303
4725
  return void 0;
4304
4726
  }
4305
- const numberOfColumnsRaw = columnsTag.match(/w:num="(\d+)"/i)?.[1];
4306
- const numberOfColumns = numberOfColumnsRaw ? Number(numberOfColumnsRaw) : 1;
4727
+ const numberOfColumnsRaw = columnsTag.match(/w:num="([\d.]+)"/i)?.[1];
4728
+ const numberOfColumns = numberOfColumnsRaw ? Math.round(Number(numberOfColumnsRaw)) : 1;
4307
4729
  if (!Number.isFinite(numberOfColumns) || numberOfColumns <= 1) {
4308
4730
  return void 0;
4309
4731
  }
4310
- const columnGapTwipsRaw = columnsTag.match(/w:space="(\d+)"/i)?.[1];
4311
- const columnGapTwips = columnGapTwipsRaw ? Number(columnGapTwipsRaw) : 720;
4732
+ const columnCount = Math.max(2, numberOfColumns);
4733
+ const columnTags = [...sectionPropertiesXml.matchAll(/<w:col\b[^>]*\/>/gi)];
4734
+ const widthsTwips = columnTags.map((match) => Number(match[0].match(/w:w="([\d.]+)"/i)?.[1])).filter((value) => Number.isFinite(value) && value > 0);
4735
+ const widthsPx = widthsTwips.map(
4736
+ (value) => Math.max(1, Math.round(twipsToPixels(value) ?? 0))
4737
+ );
4738
+ const colsTagSpaceRaw = columnsTag.match(/w:space="([\d.]+)"/i)?.[1];
4739
+ const firstColSpaceRaw = columnTags.map((match) => match[0].match(/w:space="([\d.]+)"/i)?.[1]).find((value) => value !== void 0);
4740
+ let columnGapTwips = colsTagSpaceRaw !== void 0 ? Number(colsTagSpaceRaw) : firstColSpaceRaw !== void 0 ? Number(firstColSpaceRaw) : void 0;
4741
+ if (columnGapTwips === void 0 && widthsTwips.length === columnCount) {
4742
+ const pageWidthTwips = Number(
4743
+ sectionPropertiesXml.match(/<w:pgSz\b[^>]*w:w="([\d.]+)"/i)?.[1]
4744
+ );
4745
+ const marginTag = sectionPropertiesXml.match(/<w:pgMar\b[^>]*\/?>/i)?.[0];
4746
+ const marginLeftTwips = Number(marginTag?.match(/w:left="([\d.-]+)"/i)?.[1]);
4747
+ const marginRightTwips = Number(
4748
+ marginTag?.match(/w:right="([\d.-]+)"/i)?.[1]
4749
+ );
4750
+ if (Number.isFinite(pageWidthTwips) && Number.isFinite(marginLeftTwips) && Number.isFinite(marginRightTwips)) {
4751
+ const bodyTwips = pageWidthTwips - marginLeftTwips - marginRightTwips;
4752
+ const totalWidthTwips = widthsTwips.reduce((sum, value) => sum + value, 0);
4753
+ columnGapTwips = Math.max(
4754
+ 0,
4755
+ (bodyTwips - totalWidthTwips) / Math.max(1, columnCount - 1)
4756
+ );
4757
+ }
4758
+ }
4759
+ if (columnGapTwips === void 0 || !Number.isFinite(columnGapTwips)) {
4760
+ columnGapTwips = 720;
4761
+ }
4312
4762
  const columnGapPx = twipsToPixels(columnGapTwips) ?? 24;
4313
- const columnWidthTags = [
4314
- ...sectionPropertiesXml.matchAll(/<w:col\b[^>]*w:w="(\d+)"[^>]*\/>/gi)
4315
- ];
4316
- const widthsPx = columnWidthTags.map((match) => Number(match[1])).filter((value) => Number.isFinite(value) && value > 0).map((value) => Math.max(1, Math.round(twipsToPixels(value) ?? 0)));
4317
4763
  return {
4318
- count: Math.max(2, Math.round(numberOfColumns)),
4764
+ count: columnCount,
4319
4765
  gapPx: Math.max(0, columnGapPx),
4320
- ...widthsPx.length === Math.max(2, Math.round(numberOfColumns)) ? { widthsPx } : void 0
4766
+ ...widthsPx.length === columnCount ? { widthsPx } : void 0
4321
4767
  };
4322
4768
  }
4323
4769
  function resolveSectionPaginationContentWidthPx(layout, sectionPropertiesXml) {
@@ -5931,19 +6377,6 @@ function floatingTextBoxVisibleTextFromImage(image) {
5931
6377
  const normalized = normalizeFloatingTextBoxComparisonText(text);
5932
6378
  return normalized.length > 0 ? normalized : void 0;
5933
6379
  }
5934
- function absoluteFloatingTextBearingTextBoxFootprintPx(image) {
5935
- const floating = image.floating;
5936
- if (!shouldRenderAbsoluteFloatingImage(image) || !floating || image.syntheticTextBox !== true || syntheticTextBoxContainsPictureLayer(image) || !floatingTextBoxVisibleTextFromImage(image)) {
5937
- return 0;
5938
- }
5939
- const imageHeightPx = Number.isFinite(image.heightPx) && image.heightPx > 0 ? Math.round(image.heightPx) : Number.isFinite(image.widthPx) && image.widthPx > 0 ? Math.round(image.widthPx) : MIN_PARAGRAPH_LINE_HEIGHT_PX;
5940
- const distTPx = Math.max(0, Math.round(floating.distTPx ?? 0));
5941
- const distBPx = Math.max(0, Math.round(floating.distBPx ?? 0));
5942
- return Math.max(
5943
- MIN_PARAGRAPH_LINE_HEIGHT_PX,
5944
- imageHeightPx + distTPx + distBPx
5945
- );
5946
- }
5947
6380
  function paragraphVisibleTextIsOnlyAbsoluteFloatingTextBoxContent(paragraph) {
5948
6381
  if (!paragraphHasVisibleText2(paragraph) || paragraphHasFormField2(paragraph)) {
5949
6382
  return false;
@@ -5976,29 +6409,9 @@ function paragraphHasOnlyWhitespaceText(paragraph) {
5976
6409
  return child.text.replace(/[\s\u00a0]+/g, "").length === 0;
5977
6410
  });
5978
6411
  }
5979
- function paragraphHasActiveNumbering(paragraph) {
5980
- const numbering = paragraph.style?.numbering;
5981
- return Boolean(
5982
- numbering && Number.isFinite(numbering.numId) && Math.round(numbering.numId) > 0
5983
- );
5984
- }
5985
6412
  function paragraphContainsSectionBreakProperties(paragraph) {
5986
6413
  return /<w:sectPr\b/i.test(paragraph.sourceXml ?? "");
5987
6414
  }
5988
- function paragraphTextBearingAbsoluteFloatingTextBoxFootprintPx(paragraph) {
5989
- if (paragraphHasVisibleText2(paragraph) || paragraphHasFormField2(paragraph) || paragraphContainsSectionBreakProperties(paragraph)) {
5990
- return 0;
5991
- }
5992
- return paragraph.children.reduce((largest, child) => {
5993
- if (child.type !== "image") {
5994
- return largest;
5995
- }
5996
- return Math.max(
5997
- largest,
5998
- absoluteFloatingTextBearingTextBoxFootprintPx(child)
5999
- );
6000
- }, 0);
6001
- }
6002
6415
  function paragraphAbsoluteFloatingAnchorsDependOnParagraphFlow(paragraph) {
6003
6416
  return paragraph.children.some((child) => {
6004
6417
  if (child.type !== "image" || !shouldRenderAbsoluteFloatingImage(child) || child.syntheticTextBox !== true || !floatingTextBoxVisibleTextFromImage(child) || child.floating?.behindDocument !== true) {
@@ -6090,21 +6503,6 @@ function paragraphParticipatesInLeadingCoverLayout(model, nodeIndex, pageContent
6090
6503
  }
6091
6504
  return sawLikelyCoverArtAnchor;
6092
6505
  }
6093
- function paragraphLikelyFullPageCoverFootprintPx(model, nodeIndex, paragraph, pageContentWidthPx, pageContentHeightPx) {
6094
- if (!paragraphParticipatesInLeadingCoverLayout(
6095
- model,
6096
- nodeIndex,
6097
- pageContentWidthPx,
6098
- pageContentHeightPx
6099
- ) || !paragraphIsLikelyFullPageCoverArtAnchor(
6100
- paragraph,
6101
- pageContentWidthPx,
6102
- pageContentHeightPx
6103
- )) {
6104
- return 0;
6105
- }
6106
- return Math.max(1, Math.round(pageContentHeightPx));
6107
- }
6108
6506
  function fullPageCoverImageRenderKey(nodeIndex, childIndex) {
6109
6507
  return `${nodeIndex}:${childIndex}`;
6110
6508
  }
@@ -6143,71 +6541,6 @@ function fullPageCoverAbsoluteFloatingImageStyle(image, layout, options) {
6143
6541
  zIndex: floating?.behindDocument === true ? 0 : normalizedZIndex
6144
6542
  };
6145
6543
  }
6146
- function paragraphActsAsLeadingCoverLayoutSpacer(model, nodeIndex, paragraph, pageContentWidthPx, pageContentHeightPx) {
6147
- if (nodeIndex <= 0 || !paragraphHasOnlyWhitespaceText(paragraph) || paragraphHasExplicitPageBreak2(paragraph)) {
6148
- return false;
6149
- }
6150
- if (paragraphHasActiveNumbering(paragraph)) {
6151
- return false;
6152
- }
6153
- return paragraphParticipatesInLeadingCoverLayout(
6154
- model,
6155
- nodeIndex,
6156
- pageContentWidthPx,
6157
- pageContentHeightPx
6158
- );
6159
- }
6160
- function paragraphActsAsLeadingCoverLayoutPreambleSpacer(model, nodeIndex, paragraph, pageContentWidthPx, pageContentHeightPx) {
6161
- if (!paragraphHasOnlyWhitespaceText(paragraph) || paragraphHasExplicitPageBreak2(paragraph) || paragraphHasActiveNumbering(paragraph)) {
6162
- return false;
6163
- }
6164
- const nextNode = model.nodes[nodeIndex + 1];
6165
- if (!nextNode || nextNode.type !== "paragraph") {
6166
- return false;
6167
- }
6168
- if (!paragraphParticipatesInLeadingCoverLayout(
6169
- model,
6170
- nodeIndex + 1,
6171
- pageContentWidthPx,
6172
- pageContentHeightPx
6173
- )) {
6174
- return false;
6175
- }
6176
- for (let probeIndex = nodeIndex - 1; probeIndex >= 0; probeIndex -= 1) {
6177
- const previousNode = model.nodes[probeIndex];
6178
- if (!previousNode || previousNode.type !== "paragraph") {
6179
- return false;
6180
- }
6181
- if (paragraphHasOnlyWhitespaceText(previousNode)) {
6182
- continue;
6183
- }
6184
- if (paragraphHasAbsoluteFloatingImage(previousNode)) {
6185
- return false;
6186
- }
6187
- return !paragraphIsLikelyFullPageCoverArtAnchor(
6188
- previousNode,
6189
- pageContentWidthPx,
6190
- pageContentHeightPx
6191
- );
6192
- }
6193
- return true;
6194
- }
6195
- function paragraphStartsNewPageAfterLeadingCoverLayout(model, nodeIndex, paragraph, pageContentWidthPx, pageContentHeightPx) {
6196
- if (nodeIndex <= 0 || !paragraphStartsNormalFlowContent(paragraph) || paragraphHasExplicitPageBreak2(paragraph) || paragraphHasPageBreakBefore2(paragraph)) {
6197
- return false;
6198
- }
6199
- const previousNode = model.nodes[nodeIndex - 1];
6200
- if (!previousNode || previousNode.type !== "paragraph") {
6201
- return false;
6202
- }
6203
- return paragraphActsAsLeadingCoverLayoutSpacer(
6204
- model,
6205
- nodeIndex - 1,
6206
- previousNode,
6207
- pageContentWidthPx,
6208
- pageContentHeightPx
6209
- );
6210
- }
6211
6544
  function paragraphLooksLikeCheckboxChoiceRow(paragraph) {
6212
6545
  if (paragraph.children.some((child) => child.type === "image")) {
6213
6546
  return false;
@@ -6725,7 +7058,8 @@ function buildParagraphPretextLayoutSource(paragraph, options) {
6725
7058
  let combinedText = "";
6726
7059
  const paragraphBaseFontPx = paragraphBaseFontSizePx(paragraph);
6727
7060
  const tabStopPositionsPx = options?.expandTabsForLayout ? resolveParagraphTabStopsPx(paragraph) : [];
6728
- const fallbackTabWidthPx = paragraphLooksLikeCheckboxChoiceRow(paragraph) ? checkboxChoiceRowTabWidthPx(paragraph) : DEFAULT_TAB_STOP_PX;
7061
+ const usesCheckboxRowTabFallback = paragraphLooksLikeCheckboxChoiceRow(paragraph);
7062
+ const fallbackTabWidthPx = usesCheckboxRowTabFallback ? checkboxChoiceRowTabWidthPx(paragraph) : DEFAULT_TAB_STOP_PX;
6729
7063
  let approximateLineWidthPx = 0;
6730
7064
  let lastTextStyle = firstRunStyle(paragraph);
6731
7065
  for (let childIndex = 0; childIndex < paragraph.children.length; childIndex += 1) {
@@ -6791,7 +7125,8 @@ function buildParagraphPretextLayoutSource(paragraph, options) {
6791
7125
  const tabWidthPx = resolveTabSpacerWidthPx(
6792
7126
  tabStopPositionsPx,
6793
7127
  approximateLineWidthPx,
6794
- fallbackTabWidthPx
7128
+ fallbackTabWidthPx,
7129
+ usesCheckboxRowTabFallback
6795
7130
  );
6796
7131
  const spacerText = buildParagraphPretextTabSpacerText(
6797
7132
  tabWidthPx,
@@ -7540,10 +7875,14 @@ function updateEstimatedLineWidthPxForText(currentLineWidthPx, text, style) {
7540
7875
  const trailingSegment = segments[segments.length - 1] ?? "";
7541
7876
  return estimateTextAdvanceWidthPx(trailingSegment, style);
7542
7877
  }
7543
- function resolveTabSpacerWidthPx(tabStopPositionsPx, currentLineWidthPx, fallbackWidthPx) {
7878
+ function resolveTabSpacerWidthPx(tabStopPositionsPx, currentLineWidthPx, fallbackWidthPx, fixedFallback = false) {
7544
7879
  const safeFallback = Math.max(12, Math.round(fallbackWidthPx));
7545
7880
  if (tabStopPositionsPx.length === 0) {
7546
- return safeFallback;
7881
+ if (fixedFallback) {
7882
+ return safeFallback;
7883
+ }
7884
+ const nextStop2 = (Math.floor((currentLineWidthPx + 0.5) / safeFallback) + 1) * safeFallback;
7885
+ return Math.max(2, Math.round(nextStop2 - currentLineWidthPx));
7547
7886
  }
7548
7887
  const nextStop = tabStopPositionsPx.find(
7549
7888
  (stop) => stop > currentLineWidthPx + 0.5
@@ -8296,7 +8635,23 @@ function singleLineAutoScaleForFontFamily(fontFamily) {
8296
8635
  }
8297
8636
  return WORD_SINGLE_LINE_AUTO_SCALE;
8298
8637
  }
8638
+ function emptyParagraphLineScaleForFontFamily(fontFamily) {
8639
+ const normalized = normalizeFontFamilyToken(fontFamily) ?? fontFamily?.toLowerCase();
8640
+ if (!normalized) {
8641
+ return WORD_EMPTY_PARAGRAPH_LINE_SCALE;
8642
+ }
8643
+ if (normalized === "times roman" || normalized === "times new roman" || normalized === "cambria" || normalized === "garamond" || normalized === "georgia" || normalized === "book antiqua" || normalized === "palatino linotype") {
8644
+ return WORD_EMPTY_PARAGRAPH_LINE_SCALE_SERIF;
8645
+ }
8646
+ if (normalized === "arial") {
8647
+ return WORD_EMPTY_PARAGRAPH_LINE_SCALE_SANS;
8648
+ }
8649
+ return WORD_EMPTY_PARAGRAPH_LINE_SCALE;
8650
+ }
8299
8651
  function resolveParagraphSingleLineAutoScale(paragraph, fontFamily) {
8652
+ if (paragraphHasOnlyWhitespaceText(paragraph)) {
8653
+ return emptyParagraphLineScaleForFontFamily(fontFamily);
8654
+ }
8300
8655
  const baseScale = singleLineAutoScaleForFontFamily(fontFamily);
8301
8656
  return paragraphHasCheckboxFormField(paragraph) ? Math.max(1.08, baseScale) : baseScale;
8302
8657
  }
@@ -9011,36 +9366,6 @@ function resolveMaxPretextLineRangeEndIndexThatFits(layout, startLineIndex, maxE
9011
9366
  }
9012
9367
  return bestEnd;
9013
9368
  }
9014
- function estimateAbsoluteFloatingImageFootprintPx(paragraph, image) {
9015
- if (!shouldRenderAbsoluteFloatingImage(image)) {
9016
- return 0;
9017
- }
9018
- const floating = image.floating;
9019
- if (!floating) {
9020
- return 0;
9021
- }
9022
- const wrapType = (floating.wrapType ?? "none").trim().toLowerCase();
9023
- if (wrapType !== "none") {
9024
- return 0;
9025
- }
9026
- const behavesAsTextBearingFloatingTextBox = image.syntheticTextBox && !syntheticTextBoxContainsPictureLayer(image) && Boolean(floatingTextBoxVisibleTextFromImage(image));
9027
- if (behavesAsTextBearingFloatingTextBox) {
9028
- if (paragraphHasVisibleText2(paragraph) || paragraphHasFormField2(paragraph) || paragraphContainsSectionBreakProperties(paragraph)) {
9029
- return 0;
9030
- }
9031
- const imageHeightPx = Number.isFinite(image.heightPx) && image.heightPx > 0 ? Math.round(image.heightPx) : Number.isFinite(image.widthPx) && image.widthPx > 0 ? Math.round(image.widthPx) : MIN_PARAGRAPH_LINE_HEIGHT_PX;
9032
- const distTPx = Math.max(0, Math.round(floating.distTPx ?? 0));
9033
- const distBPx = Math.max(0, Math.round(floating.distBPx ?? 0));
9034
- return Math.max(
9035
- MIN_PARAGRAPH_LINE_HEIGHT_PX,
9036
- imageHeightPx + distTPx + distBPx
9037
- );
9038
- }
9039
- if (floating.behindDocument) {
9040
- return 0;
9041
- }
9042
- return 0;
9043
- }
9044
9369
  function resolveAutoLineSpacingMultiple(lineTwips, fallbackMultiple) {
9045
9370
  if (!Number.isFinite(lineTwips)) {
9046
9371
  return Math.max(MIN_AUTO_LINE_MULTIPLE, fallbackMultiple);
@@ -9138,8 +9463,15 @@ function estimateParagraphLineHeightPx(paragraph, docGridLinePitchPx, disableDoc
9138
9463
  docGridMinimumLineHeightPx ?? 0
9139
9464
  );
9140
9465
  }
9141
- const multiple = calibrateAutoLineSpacingMultiple(
9142
- resolveAutoLineSpacingMultiple(lineTwips, defaultLineMultiple),
9466
+ const resolvedAutoMultiple = resolveAutoLineSpacingMultiple(
9467
+ lineTwips,
9468
+ defaultLineMultiple
9469
+ );
9470
+ const multiple = paragraphHasOnlyWhitespaceText(paragraph) ? Math.max(
9471
+ MIN_AUTO_LINE_MULTIPLE,
9472
+ Number((resolvedAutoMultiple * singleLineScale).toFixed(3))
9473
+ ) : calibrateAutoLineSpacingMultiple(
9474
+ resolvedAutoMultiple,
9143
9475
  baseFontFamily,
9144
9476
  singleLineScale
9145
9477
  );
@@ -9216,19 +9548,6 @@ function estimateParagraphHeightPx(paragraph, availableWidthPx, numberingDefinit
9216
9548
  estimateWrappedFloatingImageFootprintPx(paragraph, child)
9217
9549
  );
9218
9550
  }, 0);
9219
- const absoluteFloatingImageHeightPx = paragraph.children.reduce(
9220
- (largest, child) => {
9221
- if (child.type !== "image") {
9222
- return largest;
9223
- }
9224
- return Math.max(
9225
- largest,
9226
- estimateAbsoluteFloatingImageFootprintPx(paragraph, child)
9227
- );
9228
- },
9229
- 0
9230
- );
9231
- const effectiveAbsoluteFloatingImageHeightPx = collapsibleAbsoluteFloatingAnchorOnlyParagraph || decorativeBehindTextAnchorOnlyParagraph ? 0 : absoluteFloatingImageHeightPx;
9232
9551
  const emptyParagraphHeightPx = decorativeBehindTextAnchorOnlyParagraph ? 0 : paragraphIsEffectivelyEmpty(paragraph) ? lineHeightPx + EMPTY_PARAGRAPH_EXTRA_HEIGHT_PX : 0;
9233
9552
  const topBorderInsetPx = paragraphBorderInsetPx(
9234
9553
  paragraph.style?.borders?.top
@@ -9248,7 +9567,6 @@ function estimateParagraphHeightPx(paragraph, availableWidthPx, numberingDefinit
9248
9567
  textFlowHeightPx,
9249
9568
  inlineImageHeightPx,
9250
9569
  wrappedFloatingImageHeightPx,
9251
- effectiveAbsoluteFloatingImageHeightPx,
9252
9570
  emptyParagraphHeightPx
9253
9571
  );
9254
9572
  if (excludeWrappedFloatingImageFootprint && paragraph.children.some((c) => c.type === "image")) {
@@ -10257,7 +10575,7 @@ function splitParagraphSegmentForColumnRender(params) {
10257
10575
  }
10258
10576
  };
10259
10577
  }
10260
- function buildRenderColumnSegmentsForPageSection(model, flowSegments, columnWidthsPx, columnHeightPx, numberingDefinitions, docGridLinePitchPxByNodeIndex, measuredParagraphOuterHeightsPxByNodeIndex, balanceColumns = false) {
10578
+ function buildRenderColumnSegmentsForPageSection(model, flowSegments, columnWidthsPx, columnHeightPx, numberingDefinitions, docGridLinePitchPxByNodeIndex, measuredParagraphOuterHeightsPxByNodeIndex, balanceColumns = false, forceColumnBreakNodeIndexes) {
10261
10579
  const columnCount = Math.max(1, columnWidthsPx.length);
10262
10580
  const columns = Array.from(
10263
10581
  { length: columnCount },
@@ -10312,6 +10630,10 @@ function buildRenderColumnSegmentsForPageSection(model, flowSegments, columnWidt
10312
10630
  consumedHeightPx += Math.max(1, Math.round(heightPx));
10313
10631
  };
10314
10632
  for (const flowSegment of flowSegments) {
10633
+ const isNodeStartSegment = (flowSegment.paragraphLineRange?.startLineIndex ?? 0) === 0 && (flowSegment.tableRowRange?.startRowIndex ?? 0) === 0 && !flowSegment.tableRowSlice;
10634
+ if (isNodeStartSegment && forceColumnBreakNodeIndexes?.has(flowSegment.nodeIndex) && consumedHeightPx > 0) {
10635
+ moveToNextColumn();
10636
+ }
10315
10637
  let pendingSegment = flowSegment;
10316
10638
  let splitGuard = 0;
10317
10639
  while (pendingSegment && splitGuard < 256) {
@@ -10505,6 +10827,7 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
10505
10827
  let previousParagraphAfterPx = 0;
10506
10828
  let currentMetricsIndex = 0;
10507
10829
  let currentSectionPageFlowOriginPx = 0;
10830
+ let committedKeepNextChainEndNodeIndex = -1;
10508
10831
  let currentPageContentHeightPx = resolveMetricsPageContentHeightPx(
10509
10832
  0,
10510
10833
  metricsBySection[0]
@@ -10563,31 +10886,10 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
10563
10886
  previousParagraphAfterPx = 0;
10564
10887
  continue;
10565
10888
  }
10566
- if (node.type === "paragraph" && paragraphActsAsLeadingCoverLayoutPreambleSpacer(
10567
- model,
10568
- nodeIndex,
10569
- node,
10570
- nodeMetrics.pageContentWidthPx,
10571
- nodeMetrics.pageContentHeightPx
10572
- )) {
10573
- previousParagraphAfterPx = 0;
10574
- continue;
10575
- }
10576
10889
  if (node.type === "paragraph" && paragraphActsAsTrailingRenderedPageBreakSpacer(model, nodeIndex, node)) {
10577
10890
  previousParagraphAfterPx = 0;
10578
10891
  continue;
10579
10892
  }
10580
- if (node.type === "paragraph" && paragraphActsAsLeadingCoverLayoutOverlay(
10581
- model,
10582
- nodeIndex,
10583
- node,
10584
- nodeMetrics.pageContentWidthPx,
10585
- nodeMetrics.pageContentHeightPx
10586
- )) {
10587
- currentPageSegments.push({ nodeIndex });
10588
- previousParagraphAfterPx = 0;
10589
- continue;
10590
- }
10591
10893
  if (node.type === "paragraph" && paragraphActsAsDecorativeBehindTextBackgroundOverlay(node)) {
10592
10894
  currentPageSegments.push({ nodeIndex });
10593
10895
  previousParagraphAfterPx = 0;
@@ -10597,22 +10899,6 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
10597
10899
  continue;
10598
10900
  }
10599
10901
  if (node.type === "paragraph") {
10600
- if (paragraphStartsNewPageAfterLeadingCoverLayout(
10601
- model,
10602
- nodeIndex,
10603
- node,
10604
- nodeMetrics.pageContentWidthPx,
10605
- nodeMetrics.pageContentHeightPx
10606
- ) && currentPageSegments.length > 0) {
10607
- startNextPage();
10608
- pageConsumedHeightPx = 0;
10609
- previousParagraphAfterPx = 0;
10610
- currentSectionPageFlowOriginPx = 0;
10611
- currentPageContentHeightPx = resolveMetricsPageContentHeightPx(
10612
- currentPageIndex,
10613
- nodeMetrics
10614
- );
10615
- }
10616
10902
  if (paragraphHasPageBreakBefore2(node) && currentPageSegments.length > 0) {
10617
10903
  startNextPage();
10618
10904
  pageConsumedHeightPx = 0;
@@ -10682,29 +10968,26 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
10682
10968
  const nodeNumberingLabel = paginationNumberingLabels.get(
10683
10969
  `p:${nodeIndex}`
10684
10970
  );
10685
- let rawNodeHeightPx = Math.max(
10686
- 1,
10687
- estimateParagraphHeightPx(
10688
- node,
10689
- nodeMetrics.pageContentWidthPx,
10690
- numberingDefinitions,
10691
- nodeMetrics.docGridLinePitchPx,
10692
- false,
10693
- nodeNumberingLabel
10694
- ) - directBeforeSpacingPx - directAfterSpacingPx + beforeSpacingPx + afterSpacingPx
10695
- );
10696
- const coverFootprintPx = paragraphLikelyFullPageCoverFootprintPx(
10697
- model,
10698
- nodeIndex,
10971
+ const sectionUsesColumnFlow = (nodeMetrics.pageContentHeightMultiplier ?? 1) > 1;
10972
+ const measuredOuterHeightPx = sectionUsesColumnFlow ? options?.measuredParagraphOuterHeightsPxByNodeIndex?.get(nodeIndex) : void 0;
10973
+ const estimatedOrMeasuredHeightPx = Number.isFinite(measuredOuterHeightPx) && measuredOuterHeightPx > 0 ? measuredOuterHeightPx : estimateParagraphHeightPx(
10699
10974
  node,
10700
10975
  nodeMetrics.pageContentWidthPx,
10701
- nodeMetrics.pageContentHeightPx
10976
+ numberingDefinitions,
10977
+ nodeMetrics.docGridLinePitchPx,
10978
+ false,
10979
+ nodeNumberingLabel
10980
+ );
10981
+ let rawNodeHeightPx = Math.max(
10982
+ 1,
10983
+ estimatedOrMeasuredHeightPx - directBeforeSpacingPx - directAfterSpacingPx + beforeSpacingPx + afterSpacingPx
10702
10984
  );
10703
- const paragraphTooTallForSinglePage = Math.max(rawNodeHeightPx, coverFootprintPx) > nodeMetrics.pageContentHeightPx + PAGE_OVERFLOW_TOLERANCE_PX;
10985
+ const paragraphTooTallForSinglePage = rawNodeHeightPx > nodeMetrics.pageContentHeightPx + PAGE_OVERFLOW_TOLERANCE_PX;
10704
10986
  const keepLinesOverflowSplit = node.style?.keepLines === true && paragraphTooTallForSinglePage;
10705
10987
  const keepNextOverflowSplit = node.style?.keepNext === true && paragraphTooTallForSinglePage;
10706
10988
  const forceOverflowSplit = keepLinesOverflowSplit || keepNextOverflowSplit;
10707
- if (forceOverflowSplit && pageConsumedHeightPx > 0 && currentPageSegments.length > 0) {
10989
+ const nodeIsWithinCommittedKeepNextChain = nodeIndex <= committedKeepNextChainEndNodeIndex;
10990
+ if (forceOverflowSplit && !nodeIsWithinCommittedKeepNextChain && pageConsumedHeightPx > 0 && currentPageSegments.length > 0) {
10708
10991
  startNextPage();
10709
10992
  pageConsumedHeightPx = 0;
10710
10993
  previousParagraphAfterPx = 0;
@@ -10717,7 +11000,7 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
10717
11000
  const collapsedMarginPx = pageConsumedHeightPx > 0 ? Math.min(previousParagraphAfterPx, beforeSpacingPx) : 0;
10718
11001
  const collapsedNodeHeightPx = Math.max(
10719
11002
  1,
10720
- Math.max(rawNodeHeightPx, coverFootprintPx) - collapsedMarginPx
11003
+ rawNodeHeightPx - collapsedMarginPx
10721
11004
  );
10722
11005
  const paragraphSupportsPretextSegmentRendering = Boolean(
10723
11006
  paragraphPretextSourceForSegmentRendering
@@ -10743,7 +11026,7 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
10743
11026
  }
10744
11027
  const collapsedNodeHeightPxAdjusted = Math.max(
10745
11028
  1,
10746
- Math.max(rawNodeHeightPx, coverFootprintPx) - collapsedMarginPx
11029
+ rawNodeHeightPx - collapsedMarginPx
10747
11030
  );
10748
11031
  const paragraphPretextLineCount = paragraphContainsExplicitLineBreakText(node) || paragraphContainsTabCharacter(node) ? resolveParagraphPretextLayoutForSegmentRendering()?.lineCount : void 0;
10749
11032
  const supportsImageParagraphLineSplit = paragraphHasImage2(node) && paragraphSupportsPretextSegmentRendering;
@@ -10967,7 +11250,8 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
10967
11250
  continue;
10968
11251
  }
10969
11252
  let requiredHeightPx = collapsedNodeHeightPxAdjusted;
10970
- if (node.style?.keepNext === true && paragraphHasVisibleText2(node)) {
11253
+ let keepNextChainEndNodeIndex = -1;
11254
+ if (node.style?.keepNext === true && !nodeIsWithinCommittedKeepNextChain && paragraphHasVisibleText2(node)) {
10971
11255
  let chainCursor = nodeIndex;
10972
11256
  let chainPreviousParagraphAfterPx = afterSpacingPx;
10973
11257
  while (chainCursor < model.nodes.length - 1) {
@@ -11027,6 +11311,7 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
11027
11311
  );
11028
11312
  chainPreviousParagraphAfterPx = nextAfterSpacingPx;
11029
11313
  }
11314
+ keepNextChainEndNodeIndex = chainCursor;
11030
11315
  }
11031
11316
  const remainingHeightPx = currentPageContentHeightPx - pageConsumedHeightPx;
11032
11317
  const canKeepTrailingSectionTailOnCurrentPage = shouldKeepTrailingSectionTailOnCurrentPage(
@@ -11068,11 +11353,11 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
11068
11353
  nodeMetrics
11069
11354
  );
11070
11355
  }
11356
+ if (pageConsumedHeightPx === 0 && keepNextChainEndNodeIndex > nodeIndex) {
11357
+ committedKeepNextChainEndNodeIndex = keepNextChainEndNodeIndex;
11358
+ }
11071
11359
  currentPageSegments.push({ nodeIndex });
11072
- const effectiveNodeHeightPx = Math.max(
11073
- pageConsumedHeightPx > 0 ? collapsedNodeHeightPx : rawNodeHeightPx,
11074
- coverFootprintPx
11075
- );
11360
+ const effectiveNodeHeightPx = pageConsumedHeightPx > 0 ? collapsedNodeHeightPx : rawNodeHeightPx;
11076
11361
  pageConsumedHeightPx += effectiveNodeHeightPx;
11077
11362
  previousParagraphAfterPx = afterSpacingPx;
11078
11363
  continue;
@@ -11879,11 +12164,15 @@ function paragraphLineHeight(paragraph, docGridLinePitchPx, disableDocGridSnap =
11879
12164
  disableDocGridSnap
11880
12165
  )}px`;
11881
12166
  }
11882
- const lineMultiple = calibrateAutoLineSpacingMultiple(
11883
- resolveAutoLineSpacingMultiple(
11884
- lineTwips,
11885
- DEFAULT_PARAGRAPH_LINE_MULTIPLE
11886
- ),
12167
+ const resolvedAutoMultiple = resolveAutoLineSpacingMultiple(
12168
+ lineTwips,
12169
+ DEFAULT_PARAGRAPH_LINE_MULTIPLE
12170
+ );
12171
+ const lineMultiple = paragraphHasOnlyWhitespaceText(paragraph) ? Math.max(
12172
+ MIN_AUTO_LINE_MULTIPLE,
12173
+ Number((resolvedAutoMultiple * singleLineScale).toFixed(3))
12174
+ ) : calibrateAutoLineSpacingMultiple(
12175
+ resolvedAutoMultiple,
11887
12176
  baseFontFamily,
11888
12177
  singleLineScale
11889
12178
  );
@@ -12227,8 +12516,7 @@ function paragraphBlockStyle(paragraph, numberingDefinitions, headingStyles, doc
12227
12516
  const suppressIndentForFloatingAnchorOnlyParagraph = paragraphIsFloatingImageAnchorOnly(paragraph);
12228
12517
  const suppressStackingContextForBehindTextAnchorOnlyParagraph = paragraphIsBehindTextAbsoluteFloatingImageAnchorOnly(paragraph);
12229
12518
  const suppressFlowFootprintForBehindTextAnchorOnlyParagraph = paragraphActsAsDecorativeBehindTextBackgroundOverlay(paragraph);
12230
- const textBearingAbsoluteFloatingTextBoxFootprintPx = paragraphTextBearingAbsoluteFloatingTextBoxFootprintPx(paragraph);
12231
- const reservedMinHeightPx = suppressFlowFootprintForBehindTextAnchorOnlyParagraph ? void 0 : textBearingAbsoluteFloatingTextBoxFootprintPx > 0 ? textBearingAbsoluteFloatingTextBoxFootprintPx : paragraphIsEffectivelyEmpty(paragraph) ? estimateParagraphLineHeightPx(
12519
+ const reservedMinHeightPx = suppressFlowFootprintForBehindTextAnchorOnlyParagraph ? void 0 : paragraphIsEffectivelyEmpty(paragraph) ? estimateParagraphLineHeightPx(
12232
12520
  paragraph,
12233
12521
  docGridLinePitchPx,
12234
12522
  disableDocGridSnap
@@ -13986,7 +14274,8 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
13986
14274
  const resolveNextTabWidthPx = () => resolveTabSpacerWidthPx(
13987
14275
  tabStopPositionsPx,
13988
14276
  approximateLineWidthPx,
13989
- fallbackTabWidthPx
14277
+ fallbackTabWidthPx,
14278
+ checkboxChoiceRow
13990
14279
  );
13991
14280
  const appendPlainTextWithSoftBreakControl = (target, keySeed, text, style, measureStyle) => {
13992
14281
  const shouldControlSoftBreakStretch = paragraph.style?.align === "justify" && text.includes("\n");
@@ -15741,6 +16030,10 @@ function columnWidthsFromTableDefinition(table, columnCount) {
15741
16030
  const gridWidths = table.style?.columnWidthsTwips;
15742
16031
  const rowDerivedWidths = deriveColumnWidthsFromTableRows(table, columnCount);
15743
16032
  if (gridWidths && gridWidths.length === columnCount) {
16033
+ console.log("[colw]", columnCount, gridWidths.length, !!rowDerivedWidths, gridConflictsWithRowWidths(table, gridWidths));
16034
+ if (rowDerivedWidths && rowDerivedWidths.length > 0 && gridConflictsWithRowWidths(table, gridWidths)) {
16035
+ return rowDerivedWidths;
16036
+ }
15744
16037
  return normalizeColumnWidthsTwips(gridWidths, columnCount);
15745
16038
  }
15746
16039
  if (rowDerivedWidths && rowDerivedWidths.length > 0) {
@@ -15751,6 +16044,35 @@ function columnWidthsFromTableDefinition(table, columnCount) {
15751
16044
  }
15752
16045
  return void 0;
15753
16046
  }
16047
+ function gridConflictsWithRowWidths(table, gridWidths) {
16048
+ let conflictRows = 0;
16049
+ let measuredRows = 0;
16050
+ for (const row of table.rows) {
16051
+ let columnCursor = 0;
16052
+ let measuredCells = 0;
16053
+ let conflictCells = 0;
16054
+ for (const cell of row.cells) {
16055
+ const span = cell.style?.gridSpan && cell.style.gridSpan > 1 ? cell.style.gridSpan : 1;
16056
+ const expected = gridWidths.slice(columnCursor, columnCursor + span).reduce((sum, value) => sum + Math.max(0, value), 0);
16057
+ columnCursor += span;
16058
+ const actual = cell.style?.widthTwips;
16059
+ if (!actual || actual <= 0 || expected <= 0) {
16060
+ continue;
16061
+ }
16062
+ measuredCells += 1;
16063
+ if (Math.abs(actual - expected) / expected > 0.2) {
16064
+ conflictCells += 1;
16065
+ }
16066
+ }
16067
+ if (measuredCells > 0) {
16068
+ measuredRows += 1;
16069
+ if (conflictCells * 2 > measuredCells) {
16070
+ conflictRows += 1;
16071
+ }
16072
+ }
16073
+ }
16074
+ return measuredRows > 0 && conflictRows * 2 > measuredRows;
16075
+ }
15754
16076
  function deriveColumnWidthsFromTableRows(table, columnCount) {
15755
16077
  let bestCandidate;
15756
16078
  let bestPositiveCount = -1;
@@ -18970,10 +19292,10 @@ function useDocxEditor(options = {}) {
18970
19292
  );
18971
19293
  const importDocxFile = React.useCallback(
18972
19294
  async (file) => {
18973
- if (!/\.docx$/i.test(file.name)) {
19295
+ if (!/\.docx?$/i.test(file.name)) {
18974
19296
  replaceDocumentWithImportError(
18975
19297
  file.name,
18976
- new Error("Only .docx files are supported")
19298
+ new Error("Only .docx and .doc files are supported")
18977
19299
  );
18978
19300
  return;
18979
19301
  }
@@ -19034,7 +19356,7 @@ function useDocxEditor(options = {}) {
19034
19356
  const url = URL.createObjectURL(blob);
19035
19357
  const anchor = document.createElement("a");
19036
19358
  anchor.href = url;
19037
- anchor.download = fileName.endsWith(".docx") ? fileName.replace(/\.docx$/i, "") + "-edited.docx" : "edited.docx";
19359
+ anchor.download = /\.docx?$/i.test(fileName) ? fileName.replace(/\.docx?$/i, "") + "-edited.docx" : "edited.docx";
19038
19360
  anchor.style.display = "none";
19039
19361
  document.body.append(anchor);
19040
19362
  anchor.click();
@@ -22031,12 +22353,32 @@ function ensureDocxViewerPageSurfaceRegistry(editor) {
22031
22353
  if (!registry) {
22032
22354
  registry = {
22033
22355
  pageElements: /* @__PURE__ */ new Map(),
22356
+ pageContentKeys: /* @__PURE__ */ new Map(),
22034
22357
  listeners: /* @__PURE__ */ new Set()
22035
22358
  };
22036
22359
  docxViewerPageSurfaceRegistryByEditor.set(owner, registry);
22037
22360
  }
22038
22361
  return registry;
22039
22362
  }
22363
+ function syncDocxViewerPageSurfaceContentKeys(editor, contentKeysByPage) {
22364
+ const registry = ensureDocxViewerPageSurfaceRegistry(editor);
22365
+ let changed = false;
22366
+ contentKeysByPage.forEach((contentKey, pageIndex) => {
22367
+ if (registry.pageContentKeys.get(pageIndex) !== contentKey) {
22368
+ registry.pageContentKeys.set(pageIndex, contentKey);
22369
+ changed = true;
22370
+ }
22371
+ });
22372
+ registry.pageContentKeys.forEach((_, pageIndex) => {
22373
+ if (pageIndex >= contentKeysByPage.length) {
22374
+ registry.pageContentKeys.delete(pageIndex);
22375
+ changed = true;
22376
+ }
22377
+ });
22378
+ if (changed) {
22379
+ notifyDocxViewerPageSurfaceSubscribers(registry);
22380
+ }
22381
+ }
22040
22382
  function subscribeDocxViewerPageSurfaces(editor, listener) {
22041
22383
  const registry = ensureDocxViewerPageSurfaceRegistry(editor);
22042
22384
  registry.listeners.add(listener);
@@ -22100,62 +22442,8 @@ function resolveDocxViewerPageSurfaceSize(element, fallbackWidthPx, fallbackHeig
22100
22442
  heightPx: Math.max(1, Math.round(fallbackHeightPx))
22101
22443
  };
22102
22444
  }
22103
- async function rasterizeDocxViewerPageSurfaceToCanvas(params) {
22104
- if (typeof window === "undefined" || typeof XMLSerializer === "undefined") {
22105
- throw new Error("DOCX thumbnails require a browser environment.");
22106
- }
22107
- const {
22108
- pageElement,
22109
- sourceWidthPx,
22110
- sourceHeightPx,
22111
- canvas,
22112
- widthPx,
22113
- heightPx,
22114
- pixelWidthPx,
22115
- pixelHeightPx
22116
- } = params;
22117
- const safeSourceWidthPx = Math.max(1, Math.round(sourceWidthPx));
22118
- const safeSourceHeightPx = Math.max(1, Math.round(sourceHeightPx));
22119
- const scaleX = widthPx / safeSourceWidthPx;
22120
- const scaleY = heightPx / safeSourceHeightPx;
22121
- const serializedPage = new XMLSerializer().serializeToString(
22122
- pageElement.cloneNode(true)
22123
- );
22124
- const svgMarkup = `
22125
- <svg xmlns="http://www.w3.org/2000/svg" width="${widthPx}" height="${heightPx}" viewBox="0 0 ${widthPx} ${heightPx}">
22126
- <foreignObject x="0" y="0" width="100%" height="100%">
22127
- <div xmlns="http://www.w3.org/1999/xhtml" style="width:${widthPx}px;height:${heightPx}px;overflow:hidden;">
22128
- <div style="width:${safeSourceWidthPx}px;height:${safeSourceHeightPx}px;transform-origin:top left;transform:scale(${scaleX}, ${scaleY});">
22129
- ${serializedPage}
22130
- </div>
22131
- </div>
22132
- </foreignObject>
22133
- </svg>
22134
- `;
22135
- const svgDataUrl = svgDataUri(svgMarkup);
22136
- const image = await new Promise((resolve, reject) => {
22137
- const nextImage = new Image();
22138
- nextImage.decoding = "async";
22139
- nextImage.onload = () => resolve(nextImage);
22140
- nextImage.onerror = () => {
22141
- reject(new Error("Failed to rasterize DOCX page thumbnail."));
22142
- };
22143
- nextImage.src = svgDataUrl;
22144
- });
22145
- canvas.width = Math.max(1, Math.round(pixelWidthPx));
22146
- canvas.height = Math.max(1, Math.round(pixelHeightPx));
22147
- canvas.style.width = `${Math.max(1, Math.round(widthPx))}px`;
22148
- canvas.style.height = `${Math.max(1, Math.round(heightPx))}px`;
22149
- const context = canvas.getContext("2d");
22150
- if (!context) {
22151
- throw new Error("2D canvas context is unavailable for DOCX thumbnails.");
22152
- }
22153
- context.setTransform(1, 0, 0, 1, 0, 0);
22154
- context.clearRect(0, 0, canvas.width, canvas.height);
22155
- context.imageSmoothingEnabled = true;
22156
- context.imageSmoothingQuality = "high";
22157
- context.drawImage(image, 0, 0, canvas.width, canvas.height);
22158
- }
22445
+ var DOCX_THUMBNAIL_SURFACE_CACHE_MAX_ENTRIES = 32;
22446
+ var DOCX_THUMBNAIL_MIN_RASTER_INTERVAL_MS = 200;
22159
22447
  function resolveDocxPageThumbnailResolution(options) {
22160
22448
  const safeSourceWidthPx = Math.max(1, Math.round(options.sourceWidthPx));
22161
22449
  const safeSourceHeightPx = Math.max(1, Math.round(options.sourceHeightPx));
@@ -22229,8 +22517,51 @@ function useDocxPageThumbnails(editor, options = {}) {
22229
22517
  },
22230
22518
  []
22231
22519
  );
22520
+ const thumbnailSurfaceCacheRef = React.useRef(void 0);
22521
+ const thumbnailRasterQueueRef = React.useRef(void 0);
22522
+ const lastPaintedThumbnailKeyByCanvasRef = React.useRef(
22523
+ /* @__PURE__ */ new WeakMap()
22524
+ );
22525
+ const ensureThumbnailSurfaceCache = React.useCallback(() => {
22526
+ if (!thumbnailSurfaceCacheRef.current) {
22527
+ thumbnailSurfaceCacheRef.current = new DocxThumbnailSurfaceCache(
22528
+ DOCX_THUMBNAIL_SURFACE_CACHE_MAX_ENTRIES
22529
+ );
22530
+ }
22531
+ return thumbnailSurfaceCacheRef.current;
22532
+ }, []);
22533
+ const ensureThumbnailRasterQueue = React.useCallback(() => {
22534
+ if (!thumbnailRasterQueueRef.current) {
22535
+ thumbnailRasterQueueRef.current = new SerialIdleTaskQueue({
22536
+ minTaskIntervalMs: DOCX_THUMBNAIL_MIN_RASTER_INTERVAL_MS
22537
+ });
22538
+ }
22539
+ return thumbnailRasterQueueRef.current;
22540
+ }, []);
22541
+ React.useEffect(() => {
22542
+ thumbnailSurfaceCacheRef.current?.clear();
22543
+ thumbnailRasterQueueRef.current?.clear();
22544
+ lastPaintedThumbnailKeyByCanvasRef.current = /* @__PURE__ */ new WeakMap();
22545
+ }, [editor.documentLoadNonce, pageSurfaceRegistryOwner]);
22546
+ const thumbnailResolutionOptionsKey = React.useMemo(() => {
22547
+ const bounds = options.resolution;
22548
+ const boundsKey = typeof bounds === "number" ? `n${bounds}` : bounds ? `b${bounds.maxWidth ?? ""}x${bounds.maxHeight ?? ""}` : "";
22549
+ return `${boundsKey}|${options.maxWidthPx ?? ""}|${options.maxHeightPx ?? ""}|${options.pixelRatio ?? ""}`;
22550
+ }, [
22551
+ options.maxHeightPx,
22552
+ options.maxWidthPx,
22553
+ options.pixelRatio,
22554
+ options.resolution
22555
+ ]);
22556
+ const thumbnailSkipKeyForPage = React.useCallback(
22557
+ (pageIndex) => {
22558
+ const contentKey = pageSurfaceRegistry.pageContentKeys.get(pageIndex);
22559
+ return contentKey === void 0 ? void 0 : `${contentKey}|${editor.documentTheme}|${thumbnailResolutionOptionsKey}`;
22560
+ },
22561
+ [editor.documentTheme, pageSurfaceRegistry, thumbnailResolutionOptionsKey]
22562
+ );
22232
22563
  const renderPageThumbnailToCanvas = React.useCallback(
22233
- async (pageIndex, canvas) => {
22564
+ async (pageIndex, canvas, renderOptions) => {
22234
22565
  if (options.disabled) {
22235
22566
  return;
22236
22567
  }
@@ -22238,63 +22569,101 @@ function useDocxPageThumbnails(editor, options = {}) {
22238
22569
  if (!targetCanvas) {
22239
22570
  return;
22240
22571
  }
22241
- const pageElement = mountedPageElements.get(pageIndex);
22572
+ const pageElement = pageSurfaceRegistry.pageElements.get(pageIndex);
22242
22573
  if (!pageElement || !pageElement.isConnected) {
22243
22574
  updatePageThumbnailState(pageIndex, "unavailable");
22244
22575
  return;
22245
22576
  }
22246
- const sourceSize = resolveDocxViewerPageSurfaceSize(
22247
- pageElement,
22248
- fallbackLayout.pageWidthPx,
22249
- fallbackLayout.pageHeightPx
22250
- );
22251
- const resolution = resolveDocxPageThumbnailResolution({
22252
- sourceWidthPx: sourceSize.widthPx,
22253
- sourceHeightPx: sourceSize.heightPx,
22254
- resolution: options.resolution,
22255
- maxWidthPx: options.maxWidthPx,
22256
- maxHeightPx: options.maxHeightPx,
22257
- pixelRatio: options.pixelRatio
22258
- });
22577
+ const force = renderOptions?.force === true;
22578
+ const lastPaintedKey = lastPaintedThumbnailKeyByCanvasRef.current.get(targetCanvas);
22579
+ if (!force && lastPaintedKey !== void 0 && lastPaintedKey === thumbnailSkipKeyForPage(pageIndex)) {
22580
+ updatePageThumbnailState(pageIndex, "ready");
22581
+ return;
22582
+ }
22259
22583
  updatePageThumbnailState(pageIndex, "rendering");
22260
- try {
22261
- await rasterizeDocxViewerPageSurfaceToCanvas({
22262
- pageElement,
22584
+ await ensureThumbnailRasterQueue().enqueue(targetCanvas, async () => {
22585
+ const livePageElement = pageSurfaceRegistry.pageElements.get(pageIndex);
22586
+ if (!livePageElement || !livePageElement.isConnected) {
22587
+ updatePageThumbnailState(pageIndex, "unavailable");
22588
+ return;
22589
+ }
22590
+ const runSkipKey = thumbnailSkipKeyForPage(pageIndex);
22591
+ const sourceSize = resolveDocxViewerPageSurfaceSize(
22592
+ livePageElement,
22593
+ fallbackLayout.pageWidthPx,
22594
+ fallbackLayout.pageHeightPx
22595
+ );
22596
+ const resolution = resolveDocxPageThumbnailResolution({
22263
22597
  sourceWidthPx: sourceSize.widthPx,
22264
22598
  sourceHeightPx: sourceSize.heightPx,
22265
- canvas: targetCanvas,
22266
- widthPx: resolution.widthPx,
22267
- heightPx: resolution.heightPx,
22268
- pixelWidthPx: resolution.pixelWidthPx,
22269
- pixelHeightPx: resolution.pixelHeightPx
22599
+ resolution: options.resolution,
22600
+ maxWidthPx: options.maxWidthPx,
22601
+ maxHeightPx: options.maxHeightPx,
22602
+ pixelRatio: options.pixelRatio
22270
22603
  });
22271
- updatePageThumbnailState(pageIndex, "ready");
22272
- } catch (error) {
22273
- updatePageThumbnailState(
22274
- pageIndex,
22275
- "error",
22276
- error instanceof Error ? error : new Error("Failed to render DOCX page thumbnail.")
22277
- );
22278
- }
22604
+ const surfaceKey = runSkipKey === void 0 ? void 0 : `${runSkipKey}|${sourceSize.widthPx}x${sourceSize.heightPx}|${resolution.pixelWidthPx}x${resolution.pixelHeightPx}`;
22605
+ const surfaceCache = ensureThumbnailSurfaceCache();
22606
+ try {
22607
+ let surface = !force && surfaceKey !== void 0 ? surfaceCache.get(surfaceKey) : void 0;
22608
+ if (!surface) {
22609
+ surface = await rasterizeDocxThumbnailSurface({
22610
+ pageElement: livePageElement,
22611
+ sourceWidthPx: sourceSize.widthPx,
22612
+ sourceHeightPx: sourceSize.heightPx,
22613
+ widthPx: resolution.widthPx,
22614
+ heightPx: resolution.heightPx,
22615
+ pixelWidthPx: resolution.pixelWidthPx,
22616
+ pixelHeightPx: resolution.pixelHeightPx
22617
+ });
22618
+ if (surfaceKey !== void 0) {
22619
+ surfaceCache.set(surfaceKey, surface);
22620
+ }
22621
+ }
22622
+ blitDocxThumbnailSurface(surface, targetCanvas, resolution);
22623
+ if (runSkipKey !== void 0) {
22624
+ lastPaintedThumbnailKeyByCanvasRef.current.set(
22625
+ targetCanvas,
22626
+ runSkipKey
22627
+ );
22628
+ }
22629
+ updatePageThumbnailState(pageIndex, "ready");
22630
+ } catch (error) {
22631
+ updatePageThumbnailState(
22632
+ pageIndex,
22633
+ "error",
22634
+ error instanceof Error ? error : new Error("Failed to render DOCX page thumbnail.")
22635
+ );
22636
+ }
22637
+ });
22279
22638
  },
22280
22639
  [
22640
+ ensureThumbnailRasterQueue,
22641
+ ensureThumbnailSurfaceCache,
22281
22642
  fallbackLayout.pageHeightPx,
22282
22643
  fallbackLayout.pageWidthPx,
22283
- mountedPageElements,
22284
22644
  options.disabled,
22285
22645
  options.resolution,
22286
22646
  options.maxHeightPx,
22287
22647
  options.maxWidthPx,
22288
22648
  options.pixelRatio,
22649
+ pageSurfaceRegistry,
22650
+ thumbnailSkipKeyForPage,
22289
22651
  updatePageThumbnailState
22290
22652
  ]
22291
22653
  );
22292
- const rerenderAttachedThumbnails = React.useCallback(async () => {
22293
- const tasks = [...attachedCanvasByPageRef.current.keys()].map(
22294
- (pageIndex) => renderPageThumbnailToCanvas(pageIndex)
22295
- );
22296
- await Promise.all(tasks);
22297
- }, [renderPageThumbnailToCanvas]);
22654
+ const requestAttachedThumbnailRenders = React.useCallback(
22655
+ async (renderOptions) => {
22656
+ const tasks = [...attachedCanvasByPageRef.current.keys()].map(
22657
+ (pageIndex) => renderPageThumbnailToCanvas(pageIndex, void 0, renderOptions)
22658
+ );
22659
+ await Promise.all(tasks);
22660
+ },
22661
+ [renderPageThumbnailToCanvas]
22662
+ );
22663
+ const rerenderAttachedThumbnails = React.useCallback(
22664
+ async () => requestAttachedThumbnailRenders({ force: true }),
22665
+ [requestAttachedThumbnailRenders]
22666
+ );
22298
22667
  const renderPageThumbnailToCanvasRef = React.useRef(
22299
22668
  renderPageThumbnailToCanvas
22300
22669
  );
@@ -22306,7 +22675,7 @@ function useDocxPageThumbnails(editor, options = {}) {
22306
22675
  renderToCanvasCallbacksRef.current.clear();
22307
22676
  }, [pageSurfaceRegistryOwner]);
22308
22677
  React.useEffect(() => {
22309
- void rerenderAttachedThumbnails();
22678
+ void requestAttachedThumbnailRenders();
22310
22679
  }, [
22311
22680
  editor.documentLoadNonce,
22312
22681
  editor.documentTheme,
@@ -22317,7 +22686,7 @@ function useDocxPageThumbnails(editor, options = {}) {
22317
22686
  options.maxHeightPx,
22318
22687
  options.maxWidthPx,
22319
22688
  options.pixelRatio,
22320
- rerenderAttachedThumbnails
22689
+ requestAttachedThumbnailRenders
22321
22690
  ]);
22322
22691
  const thumbnails = React.useMemo(() => {
22323
22692
  const totalPages = Math.max(1, editor.totalPages);
@@ -24188,7 +24557,8 @@ function DocxEditorViewer({
24188
24557
  preferLastRenderedParagraphStartBreaks,
24189
24558
  strictLastRenderedParagraphStartBreaks: paginationOptions?.strictLastRenderedParagraphStartBreaks ?? false,
24190
24559
  measuredTableRowHeightsByNodeIndex,
24191
- measuredPageContentHeightsPxByPageIndex: resolvedPageContentHeights
24560
+ measuredPageContentHeightsPxByPageIndex: resolvedPageContentHeights,
24561
+ measuredParagraphOuterHeightsPxByNodeIndex
24192
24562
  }
24193
24563
  );
24194
24564
  const basePages = buildPagesWithHeights(baseMeasuredHeights);
@@ -24228,7 +24598,10 @@ function DocxEditorViewer({
24228
24598
  }
24229
24599
  );
24230
24600
  const minimumPageCount = Math.max(1, hardBreakCount + 1);
24231
- const targetPageCount = Number.isFinite(storedDocumentPageCount2) && storedDocumentPageCount2 > 0 ? Math.max(
24601
+ const hasColumnFlowSections = paginationSectionMetrics.some(
24602
+ (metrics) => (metrics.pageContentHeightMultiplier ?? 1) > 1
24603
+ );
24604
+ const targetPageCount = !hasColumnFlowSections && Number.isFinite(storedDocumentPageCount2) && storedDocumentPageCount2 > 0 ? Math.max(
24232
24605
  minimumPageCount,
24233
24606
  Math.round(storedDocumentPageCount2)
24234
24607
  ) : void 0;
@@ -24461,7 +24834,8 @@ function DocxEditorViewer({
24461
24834
  measuredPageContentHeightsPxByPageIndex: measuredPageContentHeightsOverride === null ? void 0 : scaleMeasuredPageContentHeights(
24462
24835
  measuredPageContentHeightsOverride ?? measuredPageContentHeightByIndex,
24463
24836
  heightScale
24464
- )
24837
+ ),
24838
+ measuredParagraphOuterHeightsPxByNodeIndex
24465
24839
  }
24466
24840
  )
24467
24841
  });
@@ -24534,6 +24908,7 @@ function DocxEditorViewer({
24534
24908
  hasMeasuredBodyFooterOverlap,
24535
24909
  initialPaginationBackgroundRefinementUnlocked,
24536
24910
  measuredPageContentHeightByIndex,
24911
+ measuredParagraphOuterHeightsPxByNodeIndex,
24537
24912
  tableMeasuredRowHeightsForPagination
24538
24913
  ]);
24539
24914
  const pageNodeSegmentsByPage = pageSegmentationPlan.pages;
@@ -24701,6 +25076,42 @@ function DocxEditorViewer({
24701
25076
  pageNodeSegmentsByPage,
24702
25077
  primarySectionPropertiesXml
24703
25078
  ]);
25079
+ const pageThumbnailContentKeysByPage = React.useMemo(() => {
25080
+ const metadataSignature = docModelThumbnailMetadataSignature(
25081
+ editor.model.metadata
25082
+ );
25083
+ return pageNodeSegmentsByPage.map((pageSegments, pageIndex) => {
25084
+ const sectionInfo = pageSectionInfoByIndex[pageIndex];
25085
+ let nodeSignatures = "";
25086
+ for (const segment of pageSegments) {
25087
+ nodeSignatures += docNodeContentSignature(
25088
+ editor.model.nodes[segment.nodeIndex]
25089
+ );
25090
+ nodeSignatures += ",";
25091
+ }
25092
+ return [
25093
+ editor.documentLoadNonce,
25094
+ trackedChangesEnabled ? "tc1" : "tc0",
25095
+ metadataSignature,
25096
+ sectionInfo ? `${sectionInfo.sectionIndex}.${sectionInfo.pageNumber}` : "s?",
25097
+ pageNodeSegmentIdentityKeysByPage[pageIndex] ?? "",
25098
+ nodeSignatures
25099
+ ].join("|");
25100
+ });
25101
+ }, [
25102
+ editor.documentLoadNonce,
25103
+ editor.model,
25104
+ pageNodeSegmentIdentityKeysByPage,
25105
+ pageNodeSegmentsByPage,
25106
+ pageSectionInfoByIndex,
25107
+ trackedChangesEnabled
25108
+ ]);
25109
+ React.useEffect(() => {
25110
+ syncDocxViewerPageSurfaceContentKeys(
25111
+ editor,
25112
+ pageThumbnailContentKeysByPage
25113
+ );
25114
+ }, [pageSurfaceRegistryOwner, pageThumbnailContentKeysByPage]);
24704
25115
  const resolveStyleRefFieldValueForPage = React.useMemo(() => {
24705
25116
  const valueCache = /* @__PURE__ */ new Map();
24706
25117
  const nodes = editor.model.nodes;
@@ -25260,6 +25671,8 @@ function DocxEditorViewer({
25260
25671
  setMeasuredParagraphOuterHeightsPxByNodeIndex((current) => {
25261
25672
  const next = new Map(current);
25262
25673
  let changed = false;
25674
+ const rootElement = viewerRootRef.current;
25675
+ const zoomScale = rootElement ? resolveEffectiveZoomScale(rootElement) : 1;
25263
25676
  paragraphElementsRef.current.forEach((element, nodeIndex) => {
25264
25677
  if (!element.isConnected || element.dataset.docxParagraphPartialLineRange === "true" || element.closest('[data-docx-header-footer-region="footer"]') || element.closest('[data-docx-header-footer-region="header"]')) {
25265
25678
  return;
@@ -25274,10 +25687,11 @@ function DocxEditorViewer({
25274
25687
  const outerHeightPx = Math.max(
25275
25688
  1,
25276
25689
  Math.round(
25277
- rect.height + (Number.isFinite(marginTop) ? marginTop : 0) + (Number.isFinite(marginBottom) ? marginBottom : 0)
25690
+ rect.height / zoomScale + (Number.isFinite(marginTop) ? marginTop : 0) + (Number.isFinite(marginBottom) ? marginBottom : 0)
25278
25691
  )
25279
25692
  );
25280
- if (next.get(nodeIndex) !== outerHeightPx) {
25693
+ const previousHeightPx = next.get(nodeIndex);
25694
+ if (previousHeightPx === void 0 || Math.abs(previousHeightPx - outerHeightPx) > 1) {
25281
25695
  next.set(nodeIndex, outerHeightPx);
25282
25696
  changed = true;
25283
25697
  }
@@ -32414,6 +32828,7 @@ function DocxEditorViewer({
32414
32828
  "span",
32415
32829
  {
32416
32830
  contentEditable: false,
32831
+ ...{ [DOCX_THUMBNAIL_EXCLUDE_ATTRIBUTE]: "true" },
32417
32832
  style: {
32418
32833
  position: "absolute",
32419
32834
  left: rect.left,
@@ -32436,6 +32851,7 @@ function DocxEditorViewer({
32436
32851
  "span",
32437
32852
  {
32438
32853
  contentEditable: false,
32854
+ ...{ [DOCX_THUMBNAIL_EXCLUDE_ATTRIBUTE]: "true" },
32439
32855
  style: {
32440
32856
  position: "absolute",
32441
32857
  left: caretRect.left,
@@ -34830,7 +35246,7 @@ function DocxEditorViewer({
34830
35246
  const extractDroppedDocxFile = React.useCallback(
34831
35247
  (dataTransfer) => {
34832
35248
  return Array.from(dataTransfer?.files ?? []).find(
34833
- (candidate) => /\.docx$/i.test(candidate.name)
35249
+ (candidate) => /\.docx?$/i.test(candidate.name)
34834
35250
  );
34835
35251
  },
34836
35252
  []
@@ -35639,13 +36055,6 @@ function DocxEditorViewer({
35639
36055
  options?.pageFlowForeignExclusions ?? []
35640
36056
  )
35641
36057
  );
35642
- const leadingCoverLayoutSpacer = paragraphActsAsLeadingCoverLayoutSpacer(
35643
- editor.model,
35644
- nodeIndex,
35645
- node,
35646
- paragraphContentWidthPx,
35647
- resolvedPageLayout.pageHeightPx - resolvedPageLayout.marginsPx.top - resolvedPageLayout.marginsPx.bottom
35648
- );
35649
36058
  const beforeSpacingPx = effectiveParagraphBeforeSpacingPx(
35650
36059
  editor.model,
35651
36060
  nodeIndex,
@@ -35704,9 +36113,6 @@ function DocxEditorViewer({
35704
36113
  lineHeight: 0,
35705
36114
  overflow: "visible"
35706
36115
  } : requiresPageAbsoluteContext ? { position: "static" } : requiresLocalAbsoluteContext ? { position: "relative" } : hasDualWrappedFloatingImage ? { position: "relative" } : void 0,
35707
- ...leadingCoverLayoutSpacer ? {
35708
- minHeight: `${estimateParagraphLineHeightPx(node, nodeDocGridLinePitchPx) + EMPTY_PARAGRAPH_EXTRA_HEIGHT_PX + LEADING_COVER_SPACER_EXTRA_HEIGHT_PX}px`
35709
- } : void 0,
35710
36116
  // Carry the paragraph's run font on the editable host so text typed into
35711
36117
  // it (which is a bare, not-yet-committed text node, not a styled run
35712
36118
  // span) renders in the same font the committed run will use — i.e. the
@@ -36741,6 +37147,22 @@ ${currentText.slice(end)}`;
36741
37147
  const rowSpanValue = cell.style?.rowSpan && cell.style.rowSpan > 1 ? cell.style.rowSpan : 1;
36742
37148
  const colSpan = colSpanValue > 1 ? colSpanValue : void 0;
36743
37149
  const rowSpan = rowSpanValue > 1 ? rowSpanValue : void 0;
37150
+ const exactRowSpanRows = node.rows.slice(
37151
+ rowIndex,
37152
+ rowIndex + rowSpanValue
37153
+ );
37154
+ const exactRowSpanClipHeightPx = exactRowSpanRows.length > 0 && exactRowSpanRows.every((spannedRow, rowOffset) => {
37155
+ const spannedHeightPx = rowHeightsPx[rowIndex + rowOffset];
37156
+ return spannedRow.style?.heightRule === "exact" && Number.isFinite(spannedHeightPx) && spannedHeightPx > 0;
37157
+ }) ? exactRowSpanRows.reduce(
37158
+ (sum, _spannedRow, rowOffset) => sum + Math.max(
37159
+ MIN_PARAGRAPH_LINE_HEIGHT_PX,
37160
+ Math.round(
37161
+ rowHeightsPx[rowIndex + rowOffset]
37162
+ )
37163
+ ),
37164
+ 0
37165
+ ) : void 0;
36744
37166
  const startColumnIndex = columnCursor;
36745
37167
  const boundaryColumnIndex = startColumnIndex + colSpanValue - 1;
36746
37168
  columnCursor += colSpanValue;
@@ -37853,7 +38275,23 @@ ${currentText.slice(end)}`;
37853
38275
  }
37854
38276
  )
37855
38277
  }
37856
- ) : /* @__PURE__ */ jsx("div", { style: { display: "grid", gap: 0 }, children: renderStaticCellContent() })
38278
+ ) : /* @__PURE__ */ jsx(
38279
+ "div",
38280
+ {
38281
+ style: {
38282
+ display: "grid",
38283
+ gap: 0,
38284
+ // Without the clip the row balloons to fit
38285
+ // content (height on a <tr>/<td> is only
38286
+ // ever a minimum).
38287
+ ...exactRowSpanClipHeightPx !== void 0 && !isSlicedRow ? {
38288
+ maxHeight: `${exactRowSpanClipHeightPx}px`,
38289
+ overflow: "hidden"
38290
+ } : void 0
38291
+ },
38292
+ children: renderStaticCellContent()
38293
+ }
38294
+ )
37857
38295
  },
37858
38296
  `cell-${nodeIndex}-${rowIndex}-${cellIndex}`
37859
38297
  );
@@ -39634,13 +40072,30 @@ ${currentText.slice(end)}`;
39634
40072
  currentGroup.segments.push(segment);
39635
40073
  return;
39636
40074
  }
40075
+ if (currentGroup && parseSectionStartType(
40076
+ documentSections[currentSectionIndex]?.sectionPropertiesXml
40077
+ ) === "nextcolumn") {
40078
+ const previousColumns = sectionColumnsBySectionIndex[currentGroup.sectionIndex];
40079
+ const nextColumns = sectionColumnsBySectionIndex[currentSectionIndex];
40080
+ const sameGeometry = previousColumns && nextColumns && previousColumns.count === nextColumns.count && Math.abs(
40081
+ previousColumns.gapPx - nextColumns.gapPx
40082
+ ) <= 2 && JSON.stringify(previousColumns.widthsPx ?? null) === JSON.stringify(nextColumns.widthsPx ?? null);
40083
+ if (sameGeometry) {
40084
+ currentGroup.segments.push(segment);
40085
+ return;
40086
+ }
40087
+ }
39637
40088
  sectionGroups.push({
39638
40089
  sectionIndex: currentSectionIndex,
39639
40090
  segments: [segment]
39640
40091
  });
39641
40092
  });
40093
+ if (typeof window !== "undefined" && window.__docxDebugGroups) {
40094
+ console.log("[groups]", pageIndex, JSON.stringify(sectionGroups.map((g) => ({ s: g.sectionIndex, n: g.segments.map((x) => x.nodeIndex) }))));
40095
+ }
39642
40096
  return sectionGroups.map((group, groupIndex) => {
39643
40097
  const sectionColumns = sectionColumnsBySectionIndex[group.sectionIndex];
40098
+ const isLastGroupOnPage = groupIndex === sectionGroups.length - 1;
39644
40099
  const overlaySegments = group.segments.filter(
39645
40100
  (segment) => {
39646
40101
  const segmentNode = editor.model.nodes[segment.nodeIndex];
@@ -39801,7 +40256,19 @@ ${currentText.slice(end)}`;
39801
40256
  editor.model.metadata.numberingDefinitions,
39802
40257
  docGridLinePitchPxByNodeIndex,
39803
40258
  measuredParagraphOuterHeightsPxByNodeIndex,
39804
- isLastPage
40259
+ isLastPage || !isLastGroupOnPage,
40260
+ new Set(
40261
+ (editor.model.metadata.sections ?? []).filter(
40262
+ (candidate) => parseSectionStartType(
40263
+ candidate.sectionPropertiesXml
40264
+ ) === "nextcolumn"
40265
+ ).map(
40266
+ (candidate) => Math.max(
40267
+ 0,
40268
+ Math.round(candidate.startNodeIndex)
40269
+ )
40270
+ )
40271
+ )
39805
40272
  );
39806
40273
  return /* @__PURE__ */ jsxs(Fragment2, { children: [
39807
40274
  overlaySegments.map((overlaySegment) => {
@@ -39837,7 +40304,9 @@ ${currentText.slice(end)}`;
39837
40304
  gridTemplateColumns: columnWidthsPx.map((widthPx) => `${widthPx}px`).join(" "),
39838
40305
  columnGap: sectionColumns.gapPx,
39839
40306
  alignItems: "start",
39840
- minHeight: pageBodyAvailableHeightPx,
40307
+ ...isLastGroupOnPage ? {
40308
+ minHeight: pageBodyAvailableHeightPx
40309
+ } : void 0,
39841
40310
  position: "relative",
39842
40311
  zIndex: 1
39843
40312
  },
@@ -39845,9 +40314,9 @@ ${currentText.slice(end)}`;
39845
40314
  (segmentsForColumn, columnIndex) => /* @__PURE__ */ jsx(
39846
40315
  "div",
39847
40316
  {
39848
- style: {
40317
+ style: isLastGroupOnPage ? {
39849
40318
  minHeight: pageBodyAvailableHeightPx
39850
- },
40319
+ } : void 0,
39851
40320
  children: segmentsForColumn.map(
39852
40321
  (segment) => renderSegmentInColumn(
39853
40322
  segment,
@@ -41702,6 +42171,7 @@ function collectDocxEstimatedOverflowBreakStartNodeIndexes(model, hardBreakStart
41702
42171
  let pageConsumedHeightPx = 0;
41703
42172
  let previousParagraphAfterPx = 0;
41704
42173
  let currentMetricsIndex = 0;
42174
+ let committedKeepNextChainEndNodeIndex = -1;
41705
42175
  const suppressSpacingBeforeAfterPageBreak = options?.suppressSpacingBeforeAfterPageBreak ?? false;
41706
42176
  let currentPageContentHeightPx = metricsBySection[0]?.pageContentHeightPx ?? fallbackMetrics.pageContentHeightPx;
41707
42177
  for (let nodeIndex = 0; nodeIndex < model.nodes.length; nodeIndex += 1) {
@@ -41739,7 +42209,8 @@ function collectDocxEstimatedOverflowBreakStartNodeIndexes(model, hardBreakStart
41739
42209
  const collapsedMarginPx = node.type === "paragraph" && pageConsumedHeightPx > 0 ? Math.min(previousParagraphAfterPx, nodeBeforeSpacingPx) : 0;
41740
42210
  const collapsedNodeHeightPx = Math.max(1, rawNodeHeightPx - collapsedMarginPx);
41741
42211
  let requiredHeightPx = collapsedNodeHeightPx;
41742
- if (node.type === "paragraph" && node.style?.keepNext === true && callbacks.paragraphHasVisibleText(node)) {
42212
+ let keepNextChainEndNodeIndex = -1;
42213
+ if (node.type === "paragraph" && node.style?.keepNext === true && nodeIndex > committedKeepNextChainEndNodeIndex && callbacks.paragraphHasVisibleText(node)) {
41743
42214
  let chainCursor = nodeIndex;
41744
42215
  let chainPreviousParagraphAfterPx = paragraphAfterSpacingPx2(node);
41745
42216
  while (chainCursor < model.nodes.length - 1) {
@@ -41797,6 +42268,7 @@ function collectDocxEstimatedOverflowBreakStartNodeIndexes(model, hardBreakStart
41797
42268
  );
41798
42269
  chainPreviousParagraphAfterPx = paragraphAfterSpacingPx2(nextChainNode);
41799
42270
  }
42271
+ keepNextChainEndNodeIndex = chainCursor;
41800
42272
  }
41801
42273
  const remainingHeightPx = currentPageContentHeightPx - pageConsumedHeightPx;
41802
42274
  if (pageConsumedHeightPx > 0 && requiredHeightPx > remainingHeightPx + pageOverflowTolerancePx) {
@@ -41805,6 +42277,9 @@ function collectDocxEstimatedOverflowBreakStartNodeIndexes(model, hardBreakStart
41805
42277
  previousParagraphAfterPx = 0;
41806
42278
  currentPageContentHeightPx = nodeMetrics.pageContentHeightPx;
41807
42279
  }
42280
+ if (pageConsumedHeightPx === 0 && keepNextChainEndNodeIndex > nodeIndex) {
42281
+ committedKeepNextChainEndNodeIndex = keepNextChainEndNodeIndex;
42282
+ }
41808
42283
  const effectiveNodeHeightPx = pageConsumedHeightPx > 0 ? collapsedNodeHeightPx : rawNodeHeightPx;
41809
42284
  pageConsumedHeightPx += effectiveNodeHeightPx;
41810
42285
  previousParagraphAfterPx = node.type === "paragraph" ? paragraphAfterSpacingPx2(node) : 0;
@@ -41856,6 +42331,7 @@ function buildDocumentPageNodeSegments2(model, pageContentHeightPx, pageContentW
41856
42331
  let pageConsumedHeightPx = 0;
41857
42332
  let previousParagraphAfterPx = 0;
41858
42333
  let currentMetricsIndex = 0;
42334
+ let committedKeepNextChainEndNodeIndex = -1;
41859
42335
  let currentPageContentHeightPx = resolvePageContentHeightPx(
41860
42336
  0,
41861
42337
  metricsBySection[0]?.pageContentHeightPx ?? fallbackMetrics.pageContentHeightPx
@@ -41919,7 +42395,8 @@ function buildDocumentPageNodeSegments2(model, pageContentHeightPx, pageContentW
41919
42395
  const keepLinesOverflowSplit = node.style?.keepLines === true && paragraphTooTallForSinglePage;
41920
42396
  const keepNextOverflowSplit = node.style?.keepNext === true && paragraphTooTallForSinglePage;
41921
42397
  const forceOverflowSplit = keepLinesOverflowSplit || keepNextOverflowSplit;
41922
- if (forceOverflowSplit && pageConsumedHeightPx > 0 && currentPageSegments.length > 0) {
42398
+ const nodeIsWithinCommittedKeepNextChain = nodeIndex <= committedKeepNextChainEndNodeIndex;
42399
+ if (forceOverflowSplit && !nodeIsWithinCommittedKeepNextChain && pageConsumedHeightPx > 0 && currentPageSegments.length > 0) {
41923
42400
  startNextPage();
41924
42401
  pageConsumedHeightPx = 0;
41925
42402
  previousParagraphAfterPx = 0;
@@ -42069,7 +42546,8 @@ function buildDocumentPageNodeSegments2(model, pageContentHeightPx, pageContentW
42069
42546
  continue;
42070
42547
  }
42071
42548
  let requiredHeightPx = collapsedNodeHeightPx;
42072
- if (node.style?.keepNext === true && callbacks.paragraphHasVisibleText(node)) {
42549
+ let keepNextChainEndNodeIndex = -1;
42550
+ if (node.style?.keepNext === true && !nodeIsWithinCommittedKeepNextChain && callbacks.paragraphHasVisibleText(node)) {
42073
42551
  let chainCursor = nodeIndex;
42074
42552
  let chainPreviousParagraphAfterPx = afterSpacingPx;
42075
42553
  while (chainCursor < model.nodes.length - 1) {
@@ -42127,6 +42605,7 @@ function buildDocumentPageNodeSegments2(model, pageContentHeightPx, pageContentW
42127
42605
  );
42128
42606
  chainPreviousParagraphAfterPx = paragraphAfterSpacingPx2(nextChainNode);
42129
42607
  }
42608
+ keepNextChainEndNodeIndex = chainCursor;
42130
42609
  }
42131
42610
  const remainingHeightPx = currentPageContentHeightPx - pageConsumedHeightPx;
42132
42611
  if (pageConsumedHeightPx > 0 && requiredHeightPx > remainingHeightPx + pageOverflowTolerancePx) {
@@ -42138,6 +42617,9 @@ function buildDocumentPageNodeSegments2(model, pageContentHeightPx, pageContentW
42138
42617
  nodeMetrics.pageContentHeightPx
42139
42618
  );
42140
42619
  }
42620
+ if (pageConsumedHeightPx === 0 && keepNextChainEndNodeIndex > nodeIndex) {
42621
+ committedKeepNextChainEndNodeIndex = keepNextChainEndNodeIndex;
42622
+ }
42141
42623
  currentPageSegments.push({ nodeIndex });
42142
42624
  const effectiveNodeHeightPx = pageConsumedHeightPx > 0 ? collapsedNodeHeightPx : rawNodeHeightPx;
42143
42625
  pageConsumedHeightPx += effectiveNodeHeightPx;
@@ -42868,6 +43350,7 @@ export {
42868
43350
  defaultStarterModel,
42869
43351
  duplicateParagraph,
42870
43352
  getPart,
43353
+ initWasm,
42871
43354
  insertParagraph,
42872
43355
  layoutDocument,
42873
43356
  modelToDocumentXml,
@@ -42909,6 +43392,7 @@ export {
42909
43392
  setParagraphHeading,
42910
43393
  setRunColor,
42911
43394
  setRunHighlight,
43395
+ setWasmSource,
42912
43396
  splitParagraphChildrenAtTextOffsets,
42913
43397
  toggleRunStyleFlag,
42914
43398
  updateParagraphText,