@extend-ai/react-docx 0.8.3 → 0.9.0

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
@@ -27,6 +27,7 @@ import {
27
27
 
28
28
  // src/index.tsx
29
29
  import * as React2 from "react";
30
+ import { flushSync as flushSync2 } from "react-dom";
30
31
 
31
32
  // ../layout-engine/src/index.ts
32
33
  var DEFAULT_OPTIONS = {
@@ -445,529 +446,6 @@ async function importDocxBuffer(buffer, options = {}) {
445
446
  return result;
446
447
  }
447
448
 
448
- // src/section-layout.ts
449
- var DEFAULT_DOC_PAGE_WIDTH = 900;
450
- var DEFAULT_DOC_PAGE_HEIGHT = 1200;
451
- var DEFAULT_DOC_PAGE_MARGIN = 56;
452
- var TWIPS_PER_PIXEL = 15;
453
- var DEFAULT_DOCUMENT_LAYOUT = {
454
- pageWidthPx: DEFAULT_DOC_PAGE_WIDTH,
455
- pageHeightPx: DEFAULT_DOC_PAGE_HEIGHT,
456
- marginsPx: {
457
- top: DEFAULT_DOC_PAGE_MARGIN,
458
- right: DEFAULT_DOC_PAGE_MARGIN,
459
- bottom: DEFAULT_DOC_PAGE_MARGIN,
460
- left: DEFAULT_DOC_PAGE_MARGIN
461
- },
462
- headerDistancePx: DEFAULT_DOC_PAGE_MARGIN,
463
- footerDistancePx: DEFAULT_DOC_PAGE_MARGIN,
464
- docGridLinePitchPx: void 0
465
- };
466
- function pageMarginPaddingStyle(marginsPx) {
467
- return {
468
- paddingTop: marginsPx.top,
469
- paddingRight: marginsPx.right,
470
- paddingBottom: marginsPx.bottom,
471
- paddingLeft: marginsPx.left
472
- };
473
- }
474
- function twipsToPixels(twips) {
475
- if (!Number.isFinite(twips)) {
476
- return void 0;
477
- }
478
- return Math.max(0, Math.round(twips / TWIPS_PER_PIXEL));
479
- }
480
- function readTwipsAttribute(tagXml, attribute) {
481
- if (!tagXml) {
482
- return void 0;
483
- }
484
- const match = tagXml.match(new RegExp(`${attribute}="(\\d+)"`, "i"));
485
- if (!match?.[1]) {
486
- return void 0;
487
- }
488
- const parsed = Number(match[1]);
489
- return Number.isFinite(parsed) ? parsed : void 0;
490
- }
491
- function readStringAttribute(tagXml, attribute) {
492
- if (!tagXml) {
493
- return void 0;
494
- }
495
- return tagXml.match(new RegExp(`${attribute}="([^"]+)"`, "i"))?.[1];
496
- }
497
- function resolvePageSizeForOrientation(params) {
498
- const normalizedOrientation = params.orientation?.trim().toLowerCase();
499
- const widthPx = Math.max(1, params.widthPx);
500
- const heightPx = Math.max(1, params.heightPx);
501
- if (normalizedOrientation === "landscape" && widthPx < heightPx) {
502
- return { widthPx: heightPx, heightPx: widthPx };
503
- }
504
- if (normalizedOrientation === "portrait" && widthPx > heightPx) {
505
- return { widthPx: heightPx, heightPx: widthPx };
506
- }
507
- return { widthPx, heightPx };
508
- }
509
- function normalizeHexColor(value) {
510
- if (!value) {
511
- return void 0;
512
- }
513
- const normalized = value.trim();
514
- if (!normalized || normalized.toLowerCase() === "auto") {
515
- return void 0;
516
- }
517
- if (/^[0-9a-fA-F]{6}$/.test(normalized)) {
518
- return `#${normalized}`;
519
- }
520
- if (/^#[0-9a-fA-F]{6}$/.test(normalized)) {
521
- return normalized;
522
- }
523
- return void 0;
524
- }
525
- function borderTypeToCssStyle(rawType) {
526
- const value = rawType?.trim().toLowerCase();
527
- if (!value || value === "none" || value === "nil") {
528
- return void 0;
529
- }
530
- if (value === "double") {
531
- return "double";
532
- }
533
- if (value === "dashed" || value === "dashsmallgap" || value === "dotdash" || value === "dotdotdash") {
534
- return "dashed";
535
- }
536
- if (value === "dotted") {
537
- return "dotted";
538
- }
539
- return "solid";
540
- }
541
- function pointsToPixels(points) {
542
- if (!Number.isFinite(points)) {
543
- return void 0;
544
- }
545
- return Math.max(0, Math.round(points * (96 / 72)));
546
- }
547
- function parseSectionPageBorderEdge(sectionPropertiesXml, edge) {
548
- if (!sectionPropertiesXml) {
549
- return void 0;
550
- }
551
- const bordersXml = sectionPropertiesXml.match(
552
- /<w:pgBorders\b[\s\S]*?<\/w:pgBorders>/i
553
- )?.[0];
554
- if (!bordersXml) {
555
- return void 0;
556
- }
557
- const edgeTag = bordersXml.match(new RegExp(`<w:${edge}\\b[^>]*/?>`, "i"))?.[0];
558
- if (!edgeTag) {
559
- return void 0;
560
- }
561
- const cssStyle = borderTypeToCssStyle(readStringAttribute(edgeTag, "w:val"));
562
- if (!cssStyle) {
563
- return void 0;
564
- }
565
- const sizeEighthPt = Number(readStringAttribute(edgeTag, "w:sz"));
566
- const widthPx = Number.isFinite(sizeEighthPt) && sizeEighthPt > 0 ? Math.max(0.5, Number((sizeEighthPt / 6).toFixed(2))) : 1;
567
- const color = normalizeHexColor(readStringAttribute(edgeTag, "w:color")) ?? "#000000";
568
- const offsetPoints = Number(readStringAttribute(edgeTag, "w:space"));
569
- return {
570
- cssBorder: `${widthPx}px ${cssStyle} ${color}`,
571
- offsetPx: pointsToPixels(offsetPoints) ?? 0,
572
- shadow: readStringAttribute(edgeTag, "w:shadow")?.trim().toLowerCase() === "1" || readStringAttribute(edgeTag, "w:shadow")?.trim().toLowerCase() === "true"
573
- };
574
- }
575
- function parseSectionPageBorders(sectionPropertiesXml) {
576
- if (!sectionPropertiesXml || !/<w:pgBorders\b/i.test(sectionPropertiesXml)) {
577
- return void 0;
578
- }
579
- const borders = {
580
- top: parseSectionPageBorderEdge(sectionPropertiesXml, "top"),
581
- right: parseSectionPageBorderEdge(sectionPropertiesXml, "right"),
582
- bottom: parseSectionPageBorderEdge(sectionPropertiesXml, "bottom"),
583
- left: parseSectionPageBorderEdge(sectionPropertiesXml, "left")
584
- };
585
- return borders.top || borders.right || borders.bottom || borders.left ? borders : void 0;
586
- }
587
- function parseSectionLayout(sectionPropertiesXml) {
588
- if (!sectionPropertiesXml) {
589
- return DEFAULT_DOCUMENT_LAYOUT;
590
- }
591
- const pageSizeTag = sectionPropertiesXml.match(/<w:pgSz\b[^>]*>/i)?.[0];
592
- const pageMarginTag = sectionPropertiesXml.match(/<w:pgMar\b[^>]*>/i)?.[0];
593
- const docGridTag = sectionPropertiesXml.match(/<w:docGrid\b[^>]*\/?>/i)?.[0];
594
- const rawPageWidthPx = twipsToPixels(readTwipsAttribute(pageSizeTag, "w:w")) ?? DEFAULT_DOCUMENT_LAYOUT.pageWidthPx;
595
- const rawPageHeightPx = twipsToPixels(readTwipsAttribute(pageSizeTag, "w:h")) ?? DEFAULT_DOCUMENT_LAYOUT.pageHeightPx;
596
- const pageSize = resolvePageSizeForOrientation({
597
- widthPx: rawPageWidthPx,
598
- heightPx: rawPageHeightPx,
599
- orientation: readStringAttribute(pageSizeTag, "w:orient")
600
- });
601
- const topMarginPx = twipsToPixels(readTwipsAttribute(pageMarginTag, "w:top")) ?? DEFAULT_DOCUMENT_LAYOUT.marginsPx.top;
602
- const rightMarginPx = twipsToPixels(readTwipsAttribute(pageMarginTag, "w:right")) ?? DEFAULT_DOCUMENT_LAYOUT.marginsPx.right;
603
- const bottomMarginPx = twipsToPixels(readTwipsAttribute(pageMarginTag, "w:bottom")) ?? DEFAULT_DOCUMENT_LAYOUT.marginsPx.bottom;
604
- const leftMarginPx = twipsToPixels(readTwipsAttribute(pageMarginTag, "w:left")) ?? DEFAULT_DOCUMENT_LAYOUT.marginsPx.left;
605
- const headerDistancePx = twipsToPixels(readTwipsAttribute(pageMarginTag, "w:header")) ?? DEFAULT_DOCUMENT_LAYOUT.headerDistancePx;
606
- const footerDistancePx = twipsToPixels(readTwipsAttribute(pageMarginTag, "w:footer")) ?? DEFAULT_DOCUMENT_LAYOUT.footerDistancePx;
607
- const docGridType = readStringAttribute(docGridTag, "w:type")?.toLowerCase();
608
- const docGridLinePitchPx = docGridType === "lines" || docGridType === "linesandchars" || docGridType === "snaptochars" ? twipsToPixels(readTwipsAttribute(docGridTag, "w:linePitch")) ?? DEFAULT_DOCUMENT_LAYOUT.docGridLinePitchPx : DEFAULT_DOCUMENT_LAYOUT.docGridLinePitchPx;
609
- return {
610
- pageWidthPx: pageSize.widthPx,
611
- pageHeightPx: pageSize.heightPx,
612
- marginsPx: {
613
- top: topMarginPx,
614
- right: rightMarginPx,
615
- bottom: bottomMarginPx,
616
- left: leftMarginPx
617
- },
618
- headerDistancePx,
619
- footerDistancePx,
620
- docGridLinePitchPx
621
- };
622
- }
623
- function resolveDocumentSectionPropertiesXml(model) {
624
- return model.metadata.sections?.[0]?.sectionPropertiesXml ?? model.metadata.sectionPropertiesXml;
625
- }
626
- function resolveDocumentLayout(model) {
627
- return parseSectionLayout(resolveDocumentSectionPropertiesXml(model));
628
- }
629
-
630
- // src/image-render.ts
631
- import { encode as encodePng } from "fast-png";
632
- import UTIFModule from "utif";
633
- var TIFF_CONTENT_TYPES = /* @__PURE__ */ new Set(["image/tiff", "image/tif"]);
634
- var PLACEHOLDER_FALLBACK_CONTENT_TYPES = /* @__PURE__ */ new Set([
635
- "image/x-emf",
636
- "image/emf",
637
- "image/x-wmf",
638
- "image/wmf"
639
- ]);
640
- var TIFF_DATA_URI_PREFIXES = ["data:image/tiff", "data:image/tif"];
641
- var EMF_DATA_URI_PREFIXES = ["data:image/emf", "data:image/x-emf"];
642
- var WMF_DATA_URI_PREFIXES = ["data:image/wmf", "data:image/x-wmf"];
643
- var UTIF = UTIFModule;
644
- var convertedTiffSrcCache = /* @__PURE__ */ new Map();
645
- function normalizeImageContentType(image) {
646
- return image.contentType?.trim().toLowerCase();
647
- }
648
- function normalizeImageSrc(image) {
649
- return image.src?.trim();
650
- }
651
- function imageHasTiffContent(image) {
652
- const contentType = normalizeImageContentType(image);
653
- if (contentType && TIFF_CONTENT_TYPES.has(contentType)) {
654
- return true;
655
- }
656
- const src = normalizeImageSrc(image)?.toLowerCase();
657
- return Boolean(src && TIFF_DATA_URI_PREFIXES.some((prefix) => src.startsWith(prefix)));
658
- }
659
- function imageHasMetafileContent(image) {
660
- const contentType = normalizeImageContentType(image);
661
- if (contentType && PLACEHOLDER_FALLBACK_CONTENT_TYPES.has(contentType)) {
662
- return true;
663
- }
664
- const src = normalizeImageSrc(image)?.toLowerCase();
665
- return Boolean(
666
- src && (EMF_DATA_URI_PREFIXES.some((prefix) => src.startsWith(prefix)) || WMF_DATA_URI_PREFIXES.some((prefix) => src.startsWith(prefix)))
667
- );
668
- }
669
- function imageUsesEmfContent(image) {
670
- const contentType = normalizeImageContentType(image);
671
- if (contentType === "image/x-emf" || contentType === "image/emf") {
672
- return true;
673
- }
674
- const src = normalizeImageSrc(image)?.toLowerCase();
675
- return Boolean(src && EMF_DATA_URI_PREFIXES.some((prefix) => src.startsWith(prefix)));
676
- }
677
- function imageUsesWmfContent(image) {
678
- const contentType = normalizeImageContentType(image);
679
- if (contentType === "image/x-wmf" || contentType === "image/wmf") {
680
- return true;
681
- }
682
- const src = normalizeImageSrc(image)?.toLowerCase();
683
- return Boolean(src && WMF_DATA_URI_PREFIXES.some((prefix) => src.startsWith(prefix)));
684
- }
685
- function imageUsesPlaceholderFallback(image) {
686
- return imageHasMetafileContent(image);
687
- }
688
- function unsupportedImageFallbackLabel(image, widthPx, heightPx) {
689
- const isSmallIcon = (widthPx ?? 0) <= 56 && (heightPx ?? 0) <= 56;
690
- if (imageUsesEmfContent(image)) {
691
- return isSmallIcon ? "e" : "EMF";
692
- }
693
- if (imageUsesWmfContent(image)) {
694
- return isSmallIcon ? "w" : "WMF";
695
- }
696
- return isSmallIcon ? "e" : "TIFF";
697
- }
698
- function dataUriToBytes(dataUri) {
699
- const match = dataUri.match(/^data:([^;]+);base64,(.+)$/i);
700
- if (!match) {
701
- return void 0;
702
- }
703
- const [, , base64] = match;
704
- if (typeof Buffer !== "undefined") {
705
- return new Uint8Array(Buffer.from(base64, "base64"));
706
- }
707
- if (typeof atob !== "function") {
708
- return void 0;
709
- }
710
- const binary = atob(base64);
711
- const bytes = new Uint8Array(binary.length);
712
- for (let index = 0; index < binary.length; index += 1) {
713
- bytes[index] = binary.charCodeAt(index);
714
- }
715
- return bytes;
716
- }
717
- function bytesToBase64(bytes) {
718
- if (typeof Buffer !== "undefined") {
719
- return Buffer.from(bytes).toString("base64");
720
- }
721
- if (typeof btoa !== "function") {
722
- return void 0;
723
- }
724
- let binary = "";
725
- const chunkSize = 32768;
726
- for (let index = 0; index < bytes.byteLength; index += chunkSize) {
727
- const chunk = bytes.subarray(index, index + chunkSize);
728
- binary += String.fromCharCode(...chunk);
729
- }
730
- return btoa(binary);
731
- }
732
- function toArrayBuffer(bytes) {
733
- const { buffer, byteOffset, byteLength } = bytes;
734
- if (byteOffset === 0 && byteLength === buffer.byteLength) {
735
- return buffer;
736
- }
737
- return buffer.slice(byteOffset, byteOffset + byteLength);
738
- }
739
- function resolveTiffBytes(image) {
740
- if (image.data?.byteLength) {
741
- return image.data;
742
- }
743
- const src = normalizeImageSrc(image);
744
- return src ? dataUriToBytes(src) : void 0;
745
- }
746
- function selectPrimaryTiffPage(ifds) {
747
- if (ifds.length === 0) {
748
- return void 0;
749
- }
750
- let candidates = ifds;
751
- if (Array.isArray(ifds[0]?.subIFD) && ifds[0].subIFD.length > 0) {
752
- candidates = candidates.concat(ifds[0].subIFD);
753
- }
754
- let best;
755
- let maxArea = -1;
756
- for (const candidate of candidates) {
757
- const width = candidate.width ?? candidate.t256 ?? 0;
758
- const height = candidate.height ?? candidate.t257 ?? 0;
759
- if (!width || !height) {
760
- continue;
761
- }
762
- const area = width * height;
763
- if (best === void 0 || area > maxArea) {
764
- best = candidate;
765
- maxArea = area;
766
- }
767
- }
768
- return best ?? ifds[0];
769
- }
770
- function convertTiffToPngDataUri(bytes) {
771
- const tiffBuffer = toArrayBuffer(bytes);
772
- const ifds = UTIF.decode(tiffBuffer);
773
- const page = selectPrimaryTiffPage(ifds);
774
- if (!page) {
775
- return void 0;
776
- }
777
- UTIF.decodeImage(tiffBuffer, page, ifds);
778
- const width = page.width ?? page.t256 ?? 0;
779
- const height = page.height ?? page.t257 ?? 0;
780
- const rgba = UTIF.toRGBA8(page);
781
- if (!width || !height || rgba.byteLength !== width * height * 4) {
782
- return void 0;
783
- }
784
- const pngBytes = encodePng({
785
- width,
786
- height,
787
- data: rgba,
788
- channels: 4,
789
- depth: 8
790
- });
791
- const base64 = bytesToBase64(pngBytes);
792
- return base64 ? `data:image/png;base64,${base64}` : void 0;
793
- }
794
- function resolveRenderableImageSource(image) {
795
- const src = normalizeImageSrc(image);
796
- if (!src) {
797
- return void 0;
798
- }
799
- if (imageHasMetafileContent(image)) {
800
- return void 0;
801
- }
802
- if (!imageHasTiffContent(image)) {
803
- return src;
804
- }
805
- const cached = convertedTiffSrcCache.get(src);
806
- if (cached !== void 0 || convertedTiffSrcCache.has(src)) {
807
- return cached;
808
- }
809
- let converted;
810
- try {
811
- const bytes = resolveTiffBytes(image);
812
- if (bytes) {
813
- converted = convertTiffToPngDataUri(bytes);
814
- }
815
- } catch {
816
- converted = void 0;
817
- }
818
- convertedTiffSrcCache.set(src, converted);
819
- return converted;
820
- }
821
- function subscribeRenderableImageSourceUpdates(listener) {
822
- void listener;
823
- return () => {
824
- };
825
- }
826
-
827
- // src/script-fonts.ts
828
- var EAST_ASIA_SCRIPT_RE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Bopomofo}\p{Script=Yi}]/u;
829
- var COMPLEX_SCRIPT_RE = /[\p{Script=Arabic}\p{Script=Hebrew}\p{Script=Syriac}\p{Script=Thaana}\p{Script=Nko}\p{Script=Samaritan}\p{Script=Mandaic}\p{Script=Devanagari}\p{Script=Bengali}\p{Script=Gurmukhi}\p{Script=Gujarati}\p{Script=Oriya}\p{Script=Tamil}\p{Script=Telugu}\p{Script=Kannada}\p{Script=Malayalam}\p{Script=Sinhala}\p{Script=Thai}\p{Script=Lao}\p{Script=Tibetan}\p{Script=Myanmar}\p{Script=Khmer}]/u;
830
- var NEUTRAL_OR_CONTINUATION_RE = /[\p{Mark}\p{Separator}\p{Punctuation}\u200c\u200d\ufe00-\ufe0f]/u;
831
- var EAST_ASIA_LANGUAGE_RE = /^(?:ja|ko|zh)(?:-|$)/i;
832
- var COMPLEX_SCRIPT_LANGUAGE_RE = /^(?:ar|ckb|dv|fa|he|hi|ks|ku|pa-arab|ps|sd|syr|ug|ur|yi)(?:-|$)/i;
833
- function normalizeFamily(value) {
834
- const normalized = value?.trim();
835
- return normalized || void 0;
836
- }
837
- function normalizedFamilyKey(value) {
838
- return normalizeFamily(value)?.replace(/^['"]+|['"]+$/g, "").toLowerCase();
839
- }
840
- function hasLegacyFontFamilyOverride(style) {
841
- const current = normalizedFamilyKey(style?.fontFamily);
842
- const imported = normalizedFamilyKey(style?.sourceFontFamily);
843
- if (!current) {
844
- return false;
845
- }
846
- if (imported) {
847
- return current !== imported;
848
- }
849
- return !(normalizeFamily(style?.fontFamilyAscii) || normalizeFamily(style?.fontFamilyHAnsi) || normalizeFamily(style?.fontFamilyEastAsia) || normalizeFamily(style?.fontFamilyCs) || normalizeFamily(style?.fontThemeAscii) || normalizeFamily(style?.fontThemeHAnsi) || normalizeFamily(style?.fontThemeEastAsia) || normalizeFamily(style?.fontThemeCs));
850
- }
851
- function normalizedFontHint(style) {
852
- return style?.fontHint?.trim().replace(/[-_\s]/g, "").toLowerCase() ?? "";
853
- }
854
- function neutralScript(style) {
855
- const hint = normalizedFontHint(style);
856
- if (hint === "eastasia") {
857
- return "eastAsia";
858
- }
859
- if (hint === "cs" || hint === "complexscript" || hint === "bidi") {
860
- return "complexScript";
861
- }
862
- if (style?.rightToLeft || COMPLEX_SCRIPT_LANGUAGE_RE.test(style?.languageBidi?.trim() ?? "")) {
863
- return "complexScript";
864
- }
865
- if (EAST_ASIA_LANGUAGE_RE.test(style?.languageEastAsia?.trim() ?? "")) {
866
- return "eastAsia";
867
- }
868
- return "highAnsi";
869
- }
870
- function classifyDocxFontScript(value, style) {
871
- const character = Array.from(value)[0];
872
- if (!character) {
873
- return void 0;
874
- }
875
- if (style?.complexScript === true || style?.rightToLeft === true && style?.complexScript !== false) {
876
- return "complexScript";
877
- }
878
- const codePoint = character.codePointAt(0) ?? 0;
879
- if (codePoint <= 127) {
880
- return "ascii";
881
- }
882
- if (EAST_ASIA_SCRIPT_RE.test(character) || codePoint >= 12288 && codePoint <= 12351 || codePoint >= 65280 && codePoint <= 65519) {
883
- return "eastAsia";
884
- }
885
- if (COMPLEX_SCRIPT_RE.test(character)) {
886
- return "complexScript";
887
- }
888
- if (NEUTRAL_OR_CONTINUATION_RE.test(character)) {
889
- return void 0;
890
- }
891
- return "highAnsi";
892
- }
893
- function resolveDocxScriptFontFamily(style, script) {
894
- const legacyFamily = normalizeFamily(style?.fontFamily);
895
- if (hasLegacyFontFamilyOverride(style)) {
896
- return legacyFamily;
897
- }
898
- switch (script) {
899
- case "ascii":
900
- return normalizeFamily(style?.resolvedFontFamilyAscii) ?? normalizeFamily(style?.fontFamilyAscii) ?? normalizeFamily(style?.resolvedFontFamilyHAnsi) ?? normalizeFamily(style?.fontFamilyHAnsi) ?? legacyFamily;
901
- case "highAnsi":
902
- return normalizeFamily(style?.resolvedFontFamilyHAnsi) ?? normalizeFamily(style?.fontFamilyHAnsi) ?? normalizeFamily(style?.resolvedFontFamilyAscii) ?? normalizeFamily(style?.fontFamilyAscii) ?? legacyFamily;
903
- case "eastAsia":
904
- return normalizeFamily(style?.resolvedFontFamilyEastAsia) ?? normalizeFamily(style?.fontFamilyEastAsia) ?? normalizeFamily(style?.resolvedFontFamilyHAnsi) ?? normalizeFamily(style?.fontFamilyHAnsi) ?? normalizeFamily(style?.resolvedFontFamilyAscii) ?? normalizeFamily(style?.fontFamilyAscii) ?? legacyFamily;
905
- case "complexScript":
906
- return normalizeFamily(style?.resolvedFontFamilyCs) ?? normalizeFamily(style?.fontFamilyCs) ?? normalizeFamily(style?.resolvedFontFamilyHAnsi) ?? normalizeFamily(style?.fontFamilyHAnsi) ?? normalizeFamily(style?.resolvedFontFamilyAscii) ?? normalizeFamily(style?.fontFamilyAscii) ?? legacyFamily;
907
- }
908
- }
909
- function segmentTextByDocxScriptFont(text, style) {
910
- if (!text) {
911
- return [];
912
- }
913
- const tokens = [];
914
- let offset = 0;
915
- for (const character of text) {
916
- const startOffset = offset;
917
- offset += character.length;
918
- tokens.push({
919
- text: character,
920
- startOffset,
921
- endOffset: offset,
922
- script: classifyDocxFontScript(character, style)
923
- });
924
- }
925
- const fallbackScript = neutralScript(style);
926
- const nextStrongScript = new Array(
927
- tokens.length
928
- );
929
- let nextScript;
930
- for (let index = tokens.length - 1; index >= 0; index -= 1) {
931
- nextStrongScript[index] = nextScript;
932
- if (tokens[index]?.script) {
933
- nextScript = tokens[index]?.script;
934
- }
935
- }
936
- let previousScript;
937
- for (let index = 0; index < tokens.length; index += 1) {
938
- const token = tokens[index];
939
- if (!token) {
940
- continue;
941
- }
942
- token.script = token.script ?? previousScript ?? nextStrongScript[index] ?? fallbackScript;
943
- previousScript = token.script;
944
- }
945
- const segments = [];
946
- for (const token of tokens) {
947
- const script = token.script ?? fallbackScript;
948
- const fontFamily = resolveDocxScriptFontFamily(style, script);
949
- const previous = segments[segments.length - 1];
950
- if (previous && normalizedFamilyKey(previous.fontFamily) === normalizedFamilyKey(fontFamily)) {
951
- previous.text += token.text;
952
- previous.endOffset = token.endOffset;
953
- continue;
954
- }
955
- segments.push({
956
- text: token.text,
957
- startOffset: token.startOffset,
958
- endOffset: token.endOffset,
959
- script,
960
- fontFamily
961
- });
962
- }
963
- return segments;
964
- }
965
- function resolveDocxTextFontFamily(text, style) {
966
- return segmentTextByDocxScriptFont(text, style).find(
967
- (segment) => segment.fontFamily
968
- )?.fontFamily ?? normalizeFamily(style?.fontFamily);
969
- }
970
-
971
449
  // src/editor.tsx
972
450
  import * as React from "react";
973
451
  import { flushSync } from "react-dom";
@@ -3515,450 +2993,973 @@ function applyTextEditingIntent(text, selectionStart, selectionEnd, intent) {
3515
2993
  return void 0;
3516
2994
  }
3517
2995
  }
3518
- function shouldCoalesceTextCommit(previous, next, windowMs = 1e3) {
3519
- return Boolean(
3520
- previous && previous.key === next.key && next.at >= previous.at && next.at - previous.at <= windowMs && previous.caretAfter === next.caretBefore
3521
- );
2996
+ function shouldCoalesceTextCommit(previous, next, windowMs = 1e3) {
2997
+ return Boolean(
2998
+ previous && previous.key === next.key && next.at >= previous.at && next.at - previous.at <= windowMs && previous.caretAfter === next.caretBefore
2999
+ );
3000
+ }
3001
+ function mergeCellParagraphTexts(paragraphTexts, boundaryParagraphIndex) {
3002
+ if (boundaryParagraphIndex <= 0 || boundaryParagraphIndex >= paragraphTexts.length) {
3003
+ return void 0;
3004
+ }
3005
+ const mergedTexts = [
3006
+ ...paragraphTexts.slice(0, boundaryParagraphIndex - 1),
3007
+ `${paragraphTexts[boundaryParagraphIndex - 1]}${paragraphTexts[boundaryParagraphIndex]}`,
3008
+ ...paragraphTexts.slice(boundaryParagraphIndex + 1)
3009
+ ];
3010
+ const cellText = mergedTexts.join("\n");
3011
+ let caretGlobalOffset = 0;
3012
+ for (let index = 0; index < boundaryParagraphIndex - 1; index += 1) {
3013
+ caretGlobalOffset += paragraphTexts[index].length + 1;
3014
+ }
3015
+ caretGlobalOffset += paragraphTexts[boundaryParagraphIndex - 1].length;
3016
+ const parts = cellText.split("\n");
3017
+ let remaining = caretGlobalOffset;
3018
+ let caretParagraphIndex = 0;
3019
+ while (caretParagraphIndex < parts.length - 1 && remaining > parts[caretParagraphIndex].length) {
3020
+ remaining -= parts[caretParagraphIndex].length + 1;
3021
+ caretParagraphIndex += 1;
3022
+ }
3023
+ return {
3024
+ cellText,
3025
+ caretParagraphIndex,
3026
+ caretOffset: Math.max(0, Math.min(remaining, parts[caretParagraphIndex].length))
3027
+ };
3028
+ }
3029
+
3030
+ // src/pagination-breaks.ts
3031
+ var PAGE_BREAK_XML_PATTERN = /<w:br\b[^>]*w:type="page"[^>]*\/?>/i;
3032
+ var PAGE_BREAK_BEFORE_XML_PATTERN = /<w:pageBreakBefore\b[^>]*\/?>/i;
3033
+ var tableExplicitPageBreakInfoBySourceXml = /* @__PURE__ */ new Map();
3034
+ function isOnOffTagEnabled(tagXml) {
3035
+ if (!tagXml) {
3036
+ return false;
3037
+ }
3038
+ const valueMatch = tagXml.match(/\bw:val="([^"]+)"/i)?.[1]?.trim().toLowerCase();
3039
+ if (!valueMatch) {
3040
+ return true;
3041
+ }
3042
+ return !["0", "false", "off", "no"].includes(valueMatch);
3043
+ }
3044
+ function paragraphHasExplicitPageBreak(paragraph) {
3045
+ const sourceXml = paragraph.sourceXml ?? "";
3046
+ return sourceXml.length > 0 && PAGE_BREAK_XML_PATTERN.test(sourceXml);
3047
+ }
3048
+ function paragraphHasPageBreakBefore(paragraph) {
3049
+ if (paragraph.style?.pageBreakBefore === true) {
3050
+ return true;
3051
+ }
3052
+ const sourceXml = paragraph.sourceXml ?? "";
3053
+ if (!sourceXml) {
3054
+ return false;
3055
+ }
3056
+ return isOnOffTagEnabled(sourceXml.match(PAGE_BREAK_BEFORE_XML_PATTERN)?.[0]);
3057
+ }
3058
+ function paragraphHasVisibleText(paragraph) {
3059
+ return paragraph.children.some(
3060
+ (child) => child.type === "text" && child.text.trim().length > 0 || child.type === "form-field" && (child.value ?? "").trim().length > 0
3061
+ );
3062
+ }
3063
+ function paragraphHasImage(paragraph) {
3064
+ return paragraph.children.some((child) => child.type === "image");
3065
+ }
3066
+ function paragraphHasFormField(paragraph) {
3067
+ return paragraph.children.some((child) => child.type === "form-field");
3068
+ }
3069
+ function paragraphIsOnlyExplicitPageBreak(paragraph) {
3070
+ return paragraphHasExplicitPageBreak(paragraph) && !paragraphHasVisibleText(paragraph) && !paragraphHasImage(paragraph) && !paragraphHasFormField(paragraph);
3071
+ }
3072
+ function tableCellDirectParagraphs(nodeContent) {
3073
+ return nodeContent.filter((entry) => entry.type === "paragraph");
3074
+ }
3075
+ function tableRowHasSubstantiveContentOutsideBreakOnlyParagraphs(row) {
3076
+ return row.cells.some(
3077
+ (cell) => tableCellDirectParagraphs(cell.nodes).some((paragraph) => {
3078
+ if (paragraphIsOnlyExplicitPageBreak(paragraph)) {
3079
+ return false;
3080
+ }
3081
+ return paragraphHasVisibleText(paragraph) || paragraphHasImage(paragraph) || paragraphHasFormField(paragraph) || paragraphHasExplicitPageBreak(paragraph);
3082
+ })
3083
+ );
3084
+ }
3085
+ function tableRowSubstantiveCellIndexes(row) {
3086
+ const indexes = [];
3087
+ row.cells.forEach((cell, cellIndex) => {
3088
+ const substantive = tableCellDirectParagraphs(cell.nodes).some((paragraph) => {
3089
+ if (paragraphIsOnlyExplicitPageBreak(paragraph)) {
3090
+ return false;
3091
+ }
3092
+ return paragraphHasVisibleText(paragraph) || paragraphHasImage(paragraph) || paragraphHasFormField(paragraph) || paragraphHasExplicitPageBreak(paragraph);
3093
+ });
3094
+ if (substantive) {
3095
+ indexes.push(cellIndex);
3096
+ }
3097
+ });
3098
+ return indexes;
3099
+ }
3100
+ function tableRowUsesTrailingSignatureCellBreakPattern(table, row) {
3101
+ if (table.rows.length !== 1 || row.cells.length < 4) {
3102
+ return false;
3103
+ }
3104
+ const substantiveIndexes = tableRowSubstantiveCellIndexes(row);
3105
+ return substantiveIndexes.length === 1 && substantiveIndexes[0] >= row.cells.length - 1;
3106
+ }
3107
+ function collectTableExplicitPageBreakInfo(table) {
3108
+ const sourceXml = table.sourceXml ?? "";
3109
+ if (sourceXml) {
3110
+ const cached = tableExplicitPageBreakInfoBySourceXml.get(sourceXml);
3111
+ if (cached) {
3112
+ return cached;
3113
+ }
3114
+ }
3115
+ const startRowIndexes = /* @__PURE__ */ new Set();
3116
+ let breakAfterTable = false;
3117
+ table.rows.forEach((row, rowIndex) => {
3118
+ let rowBreakTarget;
3119
+ row.cells.forEach((cell) => {
3120
+ tableCellDirectParagraphs(cell.nodes).forEach((paragraph) => {
3121
+ if (!paragraphHasExplicitPageBreak(paragraph)) {
3122
+ return;
3123
+ }
3124
+ if (paragraphIsOnlyExplicitPageBreak(paragraph) && rowIndex === 0 && tableRowHasSubstantiveContentOutsideBreakOnlyParagraphs(row) && !tableRowUsesTrailingSignatureCellBreakPattern(table, row)) {
3125
+ return;
3126
+ }
3127
+ const breakTarget = paragraphIsOnlyExplicitPageBreak(paragraph) ? rowIndex : rowIndex + 1;
3128
+ if (rowBreakTarget === void 0 || breakTarget < rowBreakTarget) {
3129
+ rowBreakTarget = breakTarget;
3130
+ }
3131
+ });
3132
+ });
3133
+ if (rowBreakTarget === void 0) {
3134
+ return;
3135
+ }
3136
+ if (rowBreakTarget >= table.rows.length) {
3137
+ breakAfterTable = true;
3138
+ return;
3139
+ }
3140
+ startRowIndexes.add(Math.max(0, rowBreakTarget));
3141
+ });
3142
+ const info = {
3143
+ startRowIndexes: [...startRowIndexes].sort((left, right) => left - right),
3144
+ breakAfterTable
3145
+ };
3146
+ if (sourceXml) {
3147
+ tableExplicitPageBreakInfoBySourceXml.set(sourceXml, info);
3148
+ }
3149
+ return info;
3150
+ }
3151
+ function collectTopLevelExplicitPageBreakStartNodeIndexes(nodes) {
3152
+ const breaks = /* @__PURE__ */ new Set();
3153
+ for (let nodeIndex = 0; nodeIndex < nodes.length; nodeIndex += 1) {
3154
+ const node = nodes[nodeIndex];
3155
+ const hasNextNode = nodeIndex + 1 < nodes.length;
3156
+ if (node.type === "paragraph") {
3157
+ if (paragraphHasPageBreakBefore(node)) {
3158
+ breaks.add(nodeIndex);
3159
+ }
3160
+ if (hasNextNode && paragraphHasExplicitPageBreak(node)) {
3161
+ breaks.add(nodeIndex + 1);
3162
+ }
3163
+ continue;
3164
+ }
3165
+ const tableBreakInfo = collectTableExplicitPageBreakInfo(node);
3166
+ if (tableBreakInfo.startRowIndexes.includes(0) && nodeIndex > 0) {
3167
+ breaks.add(nodeIndex);
3168
+ }
3169
+ if (hasNextNode && tableBreakInfo.breakAfterTable) {
3170
+ breaks.add(nodeIndex + 1);
3171
+ }
3172
+ }
3173
+ for (const breakIndex of [...breaks]) {
3174
+ if (breakIndex <= 0 || breakIndex >= nodes.length) {
3175
+ breaks.delete(breakIndex);
3176
+ }
3177
+ }
3178
+ return breaks;
3179
+ }
3180
+
3181
+ // src/page-count-reconciliation.ts
3182
+ var PAGE_COUNT_RECONCILIATION_REFINEMENT_STEPS = 10;
3183
+ function shouldAllowStoredPageCountReduction(options) {
3184
+ const estimatedPageCount = Math.max(
3185
+ 1,
3186
+ Math.round(options.estimatedPageCount)
3187
+ );
3188
+ const targetPageCount = Math.max(1, Math.round(options.targetPageCount));
3189
+ if (targetPageCount >= estimatedPageCount) {
3190
+ return true;
3191
+ }
3192
+ const renderedBreakHintPageCount = Number.isFinite(
3193
+ options.renderedBreakHintPageCount
3194
+ ) ? Math.max(1, Math.round(options.renderedBreakHintPageCount)) : void 0;
3195
+ const renderedBreakHintsSupportTarget = options.hasLastRenderedPageBreakHints === true && renderedBreakHintPageCount !== void 0 && targetPageCount >= renderedBreakHintPageCount;
3196
+ if (options.hasMeasuredBodyFooterOverlap === true) {
3197
+ return renderedBreakHintsSupportTarget;
3198
+ }
3199
+ if (options.hasLastRenderedPageBreakHints !== true) {
3200
+ return true;
3201
+ }
3202
+ return renderedBreakHintsSupportTarget;
3203
+ }
3204
+ function shouldLatchMeasuredBodyFooterOverlap(options) {
3205
+ if (options.measuredBodyFooterOverlap !== true) {
3206
+ return false;
3207
+ }
3208
+ const targetPageCount = Number.isFinite(options.targetPageCount) ? Math.max(1, Math.round(options.targetPageCount)) : void 0;
3209
+ if (targetPageCount === void 0) {
3210
+ return false;
3211
+ }
3212
+ const pageCount = Math.max(1, Math.round(options.pageCount));
3213
+ return pageCount <= targetPageCount;
3214
+ }
3215
+ function resolveMeasuredBodyFooterOverlapLatchState(options) {
3216
+ const shouldConsiderMeasuredBodyFooterOverlap = shouldLatchMeasuredBodyFooterOverlap({
3217
+ pageCount: options.pageCount,
3218
+ targetPageCount: options.targetPageCount,
3219
+ measuredBodyFooterOverlap: options.overlappingPageIndexes.length > 0
3220
+ });
3221
+ if (!shouldConsiderMeasuredBodyFooterOverlap) {
3222
+ return {
3223
+ signature: void 0,
3224
+ consecutivePasses: 0,
3225
+ shouldLatch: false
3226
+ };
3227
+ }
3228
+ const signature = options.overlappingPageIndexes.map((pageIndex) => `${pageIndex}`).join("|");
3229
+ const consecutivePasses = signature === options.previousSignature ? Math.max(0, Math.round(options.previousConsecutivePasses ?? 0)) + 1 : 1;
3230
+ const stabilityThreshold = Math.max(
3231
+ 1,
3232
+ Math.round(options.stabilityThreshold ?? 1)
3233
+ );
3234
+ return {
3235
+ signature,
3236
+ consecutivePasses,
3237
+ shouldLatch: consecutivePasses >= stabilityThreshold
3238
+ };
3239
+ }
3240
+ function createEstimatedPagesBuildMemo(build) {
3241
+ const entries = [];
3242
+ return (measuredTableRowHeightsByNodeIndex, measuredPageContentHeightsPxByPageIndex, strictLastRenderedParagraphStartBreaks) => {
3243
+ const cached = entries.find(
3244
+ (entry) => entry.measuredTableRowHeightsByNodeIndex === measuredTableRowHeightsByNodeIndex && entry.measuredPageContentHeightsPxByPageIndex === measuredPageContentHeightsPxByPageIndex && entry.strictLastRenderedParagraphStartBreaks === strictLastRenderedParagraphStartBreaks
3245
+ );
3246
+ if (cached) {
3247
+ return cached.pages;
3248
+ }
3249
+ const pages = build(
3250
+ measuredTableRowHeightsByNodeIndex,
3251
+ measuredPageContentHeightsPxByPageIndex,
3252
+ strictLastRenderedParagraphStartBreaks
3253
+ );
3254
+ entries.push({
3255
+ measuredTableRowHeightsByNodeIndex,
3256
+ measuredPageContentHeightsPxByPageIndex,
3257
+ strictLastRenderedParagraphStartBreaks,
3258
+ pages
3259
+ });
3260
+ return pages;
3261
+ };
3262
+ }
3263
+ function resolveMeasuredSplitParagraphPageComparison(options) {
3264
+ if (options.canUndo || options.canRedo || !options.hasMeasuredPageContentHeights) {
3265
+ return {
3266
+ pages: options.measuredEstimatedPages,
3267
+ usedPureEstimatedPages: false
3268
+ };
3269
+ }
3270
+ const measuredPagesAreOnlySplitParagraphs = options.measuredEstimatedPages.length > 0 && options.measuredEstimatedPages.every(
3271
+ options.pageContainsOnlySplitParagraphSegments
3272
+ );
3273
+ if (!measuredPagesAreOnlySplitParagraphs) {
3274
+ return {
3275
+ pages: options.measuredEstimatedPages,
3276
+ usedPureEstimatedPages: false
3277
+ };
3278
+ }
3279
+ const pureEstimatedPages = options.buildPureEstimatedPages();
3280
+ const purePagesAreOnlySplitParagraphs = pureEstimatedPages.length > 0 && pureEstimatedPages.every(options.pageContainsOnlySplitParagraphSegments);
3281
+ if (purePagesAreOnlySplitParagraphs && pureEstimatedPages.length < options.measuredEstimatedPages.length) {
3282
+ return { pages: pureEstimatedPages, usedPureEstimatedPages: true };
3283
+ }
3284
+ return {
3285
+ pages: options.measuredEstimatedPages,
3286
+ usedPureEstimatedPages: false
3287
+ };
3288
+ }
3289
+ function isBetterCandidate(candidate, incumbent, targetPageCount) {
3290
+ const candidateDifference = Math.abs(candidate.pageCount - targetPageCount);
3291
+ const incumbentDifference = Math.abs(incumbent.pageCount - targetPageCount);
3292
+ if (candidateDifference !== incumbentDifference) {
3293
+ return candidateDifference < incumbentDifference;
3294
+ }
3295
+ const candidateScaleDelta = Math.abs(candidate.scale - 1);
3296
+ const incumbentScaleDelta = Math.abs(incumbent.scale - 1);
3297
+ if (candidateScaleDelta !== incumbentScaleDelta) {
3298
+ return candidateScaleDelta < incumbentScaleDelta;
3299
+ }
3300
+ return candidate.pageCount === targetPageCount && incumbent.pageCount !== targetPageCount;
3301
+ }
3302
+ function reconcilePageCountCandidateToTargetCountByScalingHeight(options) {
3303
+ const {
3304
+ initialPages,
3305
+ targetPageCount,
3306
+ buildPagesAtScale,
3307
+ maxDifference = 3,
3308
+ scales: customScales
3309
+ } = options;
3310
+ const safeTargetPageCount = Math.max(1, Math.round(targetPageCount));
3311
+ const initialPageCount = initialPages.length;
3312
+ const initialDifference = Math.abs(initialPageCount - safeTargetPageCount);
3313
+ if (initialDifference === 0 || initialDifference > maxDifference) {
3314
+ return {
3315
+ pageCount: initialPageCount,
3316
+ pages: initialPages,
3317
+ scale: 1
3318
+ };
3319
+ }
3320
+ const bestCandidate = {
3321
+ pageCount: initialPageCount,
3322
+ pages: initialPages,
3323
+ scale: 1
3324
+ };
3325
+ let selectedCandidate = bestCandidate;
3326
+ const needMorePages = initialPageCount < safeTargetPageCount;
3327
+ const scales = customScales && customScales.length > 0 ? customScales : needMorePages ? [
3328
+ 0.98,
3329
+ 0.96,
3330
+ 0.94,
3331
+ 0.92,
3332
+ 0.9,
3333
+ 0.88,
3334
+ 0.86,
3335
+ 0.84,
3336
+ 0.82,
3337
+ 0.8,
3338
+ 0.78,
3339
+ 0.76,
3340
+ 0.74,
3341
+ 0.72,
3342
+ 0.7,
3343
+ 0.68,
3344
+ 0.66,
3345
+ 0.64,
3346
+ 0.62,
3347
+ 0.6,
3348
+ 0.58,
3349
+ 0.56,
3350
+ 0.54,
3351
+ 0.52,
3352
+ 0.5,
3353
+ 0.48,
3354
+ 0.46,
3355
+ 0.44,
3356
+ 0.42,
3357
+ 0.4,
3358
+ 0.38,
3359
+ 0.36,
3360
+ 0.34,
3361
+ 0.32,
3362
+ 0.3,
3363
+ 0.28,
3364
+ 0.26,
3365
+ 0.24,
3366
+ 0.22,
3367
+ 0.2
3368
+ ] : [
3369
+ 1.02,
3370
+ 1.04,
3371
+ 1.06,
3372
+ 1.08,
3373
+ 1.1,
3374
+ 1.12,
3375
+ 1.14,
3376
+ 1.16,
3377
+ 1.18,
3378
+ 1.2,
3379
+ 1.22,
3380
+ 1.24,
3381
+ 1.26,
3382
+ 1.28,
3383
+ 1.3
3384
+ ];
3385
+ const buildCandidateAtScale = (scale) => {
3386
+ const pages = buildPagesAtScale(scale);
3387
+ return {
3388
+ pageCount: pages.length,
3389
+ pages,
3390
+ scale
3391
+ };
3392
+ };
3393
+ const refineExactTargetCandidate = (lowerScale, upperScale) => {
3394
+ let bestExactCandidate;
3395
+ const interval = upperScale - lowerScale;
3396
+ if (!Number.isFinite(interval) || Math.abs(interval) < 1e-4) {
3397
+ return selectedCandidate;
3398
+ }
3399
+ for (let step = 1; step <= PAGE_COUNT_RECONCILIATION_REFINEMENT_STEPS; step += 1) {
3400
+ const scale = lowerScale + interval * step / PAGE_COUNT_RECONCILIATION_REFINEMENT_STEPS;
3401
+ const candidate = buildCandidateAtScale(scale);
3402
+ if (isBetterCandidate(candidate, selectedCandidate, safeTargetPageCount)) {
3403
+ selectedCandidate = candidate;
3404
+ }
3405
+ if (candidate.pageCount === safeTargetPageCount && (bestExactCandidate === void 0 || isBetterCandidate(
3406
+ candidate,
3407
+ bestExactCandidate,
3408
+ safeTargetPageCount
3409
+ ))) {
3410
+ bestExactCandidate = candidate;
3411
+ }
3412
+ }
3413
+ return bestExactCandidate ?? selectedCandidate;
3414
+ };
3415
+ let previousScale = 1;
3416
+ let previousPageCount = initialPageCount;
3417
+ for (const scale of scales) {
3418
+ const candidate = buildCandidateAtScale(scale);
3419
+ if (isBetterCandidate(candidate, selectedCandidate, safeTargetPageCount)) {
3420
+ selectedCandidate = candidate;
3421
+ }
3422
+ if (candidate.pageCount === safeTargetPageCount) {
3423
+ const crossedIntoExactTarget = needMorePages ? previousPageCount < safeTargetPageCount : previousPageCount > safeTargetPageCount;
3424
+ if (crossedIntoExactTarget) {
3425
+ return refineExactTargetCandidate(previousScale, scale);
3426
+ }
3427
+ return candidate;
3428
+ }
3429
+ previousScale = scale;
3430
+ previousPageCount = candidate.pageCount;
3431
+ }
3432
+ if (!needMorePages && selectedCandidate.pageCount !== safeTargetPageCount) {
3433
+ return {
3434
+ pageCount: initialPageCount,
3435
+ pages: initialPages,
3436
+ scale: 1
3437
+ };
3438
+ }
3439
+ return selectedCandidate;
3440
+ }
3441
+
3442
+ // src/section-layout.ts
3443
+ var DEFAULT_DOC_PAGE_WIDTH = 900;
3444
+ var DEFAULT_DOC_PAGE_HEIGHT = 1200;
3445
+ var DEFAULT_DOC_PAGE_MARGIN = 56;
3446
+ var TWIPS_PER_PIXEL = 15;
3447
+ var DEFAULT_DOCUMENT_LAYOUT = {
3448
+ pageWidthPx: DEFAULT_DOC_PAGE_WIDTH,
3449
+ pageHeightPx: DEFAULT_DOC_PAGE_HEIGHT,
3450
+ marginsPx: {
3451
+ top: DEFAULT_DOC_PAGE_MARGIN,
3452
+ right: DEFAULT_DOC_PAGE_MARGIN,
3453
+ bottom: DEFAULT_DOC_PAGE_MARGIN,
3454
+ left: DEFAULT_DOC_PAGE_MARGIN
3455
+ },
3456
+ headerDistancePx: DEFAULT_DOC_PAGE_MARGIN,
3457
+ footerDistancePx: DEFAULT_DOC_PAGE_MARGIN,
3458
+ docGridLinePitchPx: void 0
3459
+ };
3460
+ function pageMarginPaddingStyle(marginsPx) {
3461
+ return {
3462
+ paddingTop: marginsPx.top,
3463
+ paddingRight: marginsPx.right,
3464
+ paddingBottom: marginsPx.bottom,
3465
+ paddingLeft: marginsPx.left
3466
+ };
3467
+ }
3468
+ function twipsToPixels(twips) {
3469
+ if (!Number.isFinite(twips)) {
3470
+ return void 0;
3471
+ }
3472
+ return Math.max(0, Math.round(twips / TWIPS_PER_PIXEL));
3473
+ }
3474
+ function readTwipsAttribute(tagXml, attribute) {
3475
+ if (!tagXml) {
3476
+ return void 0;
3477
+ }
3478
+ const match = tagXml.match(new RegExp(`${attribute}="(\\d+)"`, "i"));
3479
+ if (!match?.[1]) {
3480
+ return void 0;
3481
+ }
3482
+ const parsed = Number(match[1]);
3483
+ return Number.isFinite(parsed) ? parsed : void 0;
3484
+ }
3485
+ function readStringAttribute(tagXml, attribute) {
3486
+ if (!tagXml) {
3487
+ return void 0;
3488
+ }
3489
+ return tagXml.match(new RegExp(`${attribute}="([^"]+)"`, "i"))?.[1];
3490
+ }
3491
+ function resolvePageSizeForOrientation(params) {
3492
+ const normalizedOrientation = params.orientation?.trim().toLowerCase();
3493
+ const widthPx = Math.max(1, params.widthPx);
3494
+ const heightPx = Math.max(1, params.heightPx);
3495
+ if (normalizedOrientation === "landscape" && widthPx < heightPx) {
3496
+ return { widthPx: heightPx, heightPx: widthPx };
3497
+ }
3498
+ if (normalizedOrientation === "portrait" && widthPx > heightPx) {
3499
+ return { widthPx: heightPx, heightPx: widthPx };
3500
+ }
3501
+ return { widthPx, heightPx };
3502
+ }
3503
+ function normalizeHexColor(value) {
3504
+ if (!value) {
3505
+ return void 0;
3506
+ }
3507
+ const normalized = value.trim();
3508
+ if (!normalized || normalized.toLowerCase() === "auto") {
3509
+ return void 0;
3510
+ }
3511
+ if (/^[0-9a-fA-F]{6}$/.test(normalized)) {
3512
+ return `#${normalized}`;
3513
+ }
3514
+ if (/^#[0-9a-fA-F]{6}$/.test(normalized)) {
3515
+ return normalized;
3516
+ }
3517
+ return void 0;
3522
3518
  }
3523
- function mergeCellParagraphTexts(paragraphTexts, boundaryParagraphIndex) {
3524
- if (boundaryParagraphIndex <= 0 || boundaryParagraphIndex >= paragraphTexts.length) {
3519
+ function borderTypeToCssStyle(rawType) {
3520
+ const value = rawType?.trim().toLowerCase();
3521
+ if (!value || value === "none" || value === "nil") {
3525
3522
  return void 0;
3526
3523
  }
3527
- const mergedTexts = [
3528
- ...paragraphTexts.slice(0, boundaryParagraphIndex - 1),
3529
- `${paragraphTexts[boundaryParagraphIndex - 1]}${paragraphTexts[boundaryParagraphIndex]}`,
3530
- ...paragraphTexts.slice(boundaryParagraphIndex + 1)
3531
- ];
3532
- const cellText = mergedTexts.join("\n");
3533
- let caretGlobalOffset = 0;
3534
- for (let index = 0; index < boundaryParagraphIndex - 1; index += 1) {
3535
- caretGlobalOffset += paragraphTexts[index].length + 1;
3524
+ if (value === "double") {
3525
+ return "double";
3536
3526
  }
3537
- caretGlobalOffset += paragraphTexts[boundaryParagraphIndex - 1].length;
3538
- const parts = cellText.split("\n");
3539
- let remaining = caretGlobalOffset;
3540
- let caretParagraphIndex = 0;
3541
- while (caretParagraphIndex < parts.length - 1 && remaining > parts[caretParagraphIndex].length) {
3542
- remaining -= parts[caretParagraphIndex].length + 1;
3543
- caretParagraphIndex += 1;
3527
+ if (value === "dashed" || value === "dashsmallgap" || value === "dotdash" || value === "dotdotdash") {
3528
+ return "dashed";
3529
+ }
3530
+ if (value === "dotted") {
3531
+ return "dotted";
3532
+ }
3533
+ return "solid";
3534
+ }
3535
+ function pointsToPixels(points) {
3536
+ if (!Number.isFinite(points)) {
3537
+ return void 0;
3538
+ }
3539
+ return Math.max(0, Math.round(points * (96 / 72)));
3540
+ }
3541
+ function parseSectionPageBorderEdge(sectionPropertiesXml, edge) {
3542
+ if (!sectionPropertiesXml) {
3543
+ return void 0;
3544
+ }
3545
+ const bordersXml = sectionPropertiesXml.match(
3546
+ /<w:pgBorders\b[\s\S]*?<\/w:pgBorders>/i
3547
+ )?.[0];
3548
+ if (!bordersXml) {
3549
+ return void 0;
3550
+ }
3551
+ const edgeTag = bordersXml.match(new RegExp(`<w:${edge}\\b[^>]*/?>`, "i"))?.[0];
3552
+ if (!edgeTag) {
3553
+ return void 0;
3554
+ }
3555
+ const cssStyle = borderTypeToCssStyle(readStringAttribute(edgeTag, "w:val"));
3556
+ if (!cssStyle) {
3557
+ return void 0;
3544
3558
  }
3559
+ const sizeEighthPt = Number(readStringAttribute(edgeTag, "w:sz"));
3560
+ const widthPx = Number.isFinite(sizeEighthPt) && sizeEighthPt > 0 ? Math.max(0.5, Number((sizeEighthPt / 6).toFixed(2))) : 1;
3561
+ const color = normalizeHexColor(readStringAttribute(edgeTag, "w:color")) ?? "#000000";
3562
+ const offsetPoints = Number(readStringAttribute(edgeTag, "w:space"));
3545
3563
  return {
3546
- cellText,
3547
- caretParagraphIndex,
3548
- caretOffset: Math.max(0, Math.min(remaining, parts[caretParagraphIndex].length))
3564
+ cssBorder: `${widthPx}px ${cssStyle} ${color}`,
3565
+ offsetPx: pointsToPixels(offsetPoints) ?? 0,
3566
+ shadow: readStringAttribute(edgeTag, "w:shadow")?.trim().toLowerCase() === "1" || readStringAttribute(edgeTag, "w:shadow")?.trim().toLowerCase() === "true"
3549
3567
  };
3550
3568
  }
3551
-
3552
- // src/pagination-breaks.ts
3553
- var PAGE_BREAK_XML_PATTERN = /<w:br\b[^>]*w:type="page"[^>]*\/?>/i;
3554
- var PAGE_BREAK_BEFORE_XML_PATTERN = /<w:pageBreakBefore\b[^>]*\/?>/i;
3555
- var tableExplicitPageBreakInfoBySourceXml = /* @__PURE__ */ new Map();
3556
- function isOnOffTagEnabled(tagXml) {
3557
- if (!tagXml) {
3558
- return false;
3569
+ function parseSectionPageBorders(sectionPropertiesXml) {
3570
+ if (!sectionPropertiesXml || !/<w:pgBorders\b/i.test(sectionPropertiesXml)) {
3571
+ return void 0;
3559
3572
  }
3560
- const valueMatch = tagXml.match(/\bw:val="([^"]+)"/i)?.[1]?.trim().toLowerCase();
3561
- if (!valueMatch) {
3562
- return true;
3573
+ const borders = {
3574
+ top: parseSectionPageBorderEdge(sectionPropertiesXml, "top"),
3575
+ right: parseSectionPageBorderEdge(sectionPropertiesXml, "right"),
3576
+ bottom: parseSectionPageBorderEdge(sectionPropertiesXml, "bottom"),
3577
+ left: parseSectionPageBorderEdge(sectionPropertiesXml, "left")
3578
+ };
3579
+ return borders.top || borders.right || borders.bottom || borders.left ? borders : void 0;
3580
+ }
3581
+ function parseSectionLayout(sectionPropertiesXml) {
3582
+ if (!sectionPropertiesXml) {
3583
+ return DEFAULT_DOCUMENT_LAYOUT;
3563
3584
  }
3564
- return !["0", "false", "off", "no"].includes(valueMatch);
3585
+ const pageSizeTag = sectionPropertiesXml.match(/<w:pgSz\b[^>]*>/i)?.[0];
3586
+ const pageMarginTag = sectionPropertiesXml.match(/<w:pgMar\b[^>]*>/i)?.[0];
3587
+ const docGridTag = sectionPropertiesXml.match(/<w:docGrid\b[^>]*\/?>/i)?.[0];
3588
+ const rawPageWidthPx = twipsToPixels(readTwipsAttribute(pageSizeTag, "w:w")) ?? DEFAULT_DOCUMENT_LAYOUT.pageWidthPx;
3589
+ const rawPageHeightPx = twipsToPixels(readTwipsAttribute(pageSizeTag, "w:h")) ?? DEFAULT_DOCUMENT_LAYOUT.pageHeightPx;
3590
+ const pageSize = resolvePageSizeForOrientation({
3591
+ widthPx: rawPageWidthPx,
3592
+ heightPx: rawPageHeightPx,
3593
+ orientation: readStringAttribute(pageSizeTag, "w:orient")
3594
+ });
3595
+ const topMarginPx = twipsToPixels(readTwipsAttribute(pageMarginTag, "w:top")) ?? DEFAULT_DOCUMENT_LAYOUT.marginsPx.top;
3596
+ const rightMarginPx = twipsToPixels(readTwipsAttribute(pageMarginTag, "w:right")) ?? DEFAULT_DOCUMENT_LAYOUT.marginsPx.right;
3597
+ const bottomMarginPx = twipsToPixels(readTwipsAttribute(pageMarginTag, "w:bottom")) ?? DEFAULT_DOCUMENT_LAYOUT.marginsPx.bottom;
3598
+ const leftMarginPx = twipsToPixels(readTwipsAttribute(pageMarginTag, "w:left")) ?? DEFAULT_DOCUMENT_LAYOUT.marginsPx.left;
3599
+ const headerDistancePx = twipsToPixels(readTwipsAttribute(pageMarginTag, "w:header")) ?? DEFAULT_DOCUMENT_LAYOUT.headerDistancePx;
3600
+ const footerDistancePx = twipsToPixels(readTwipsAttribute(pageMarginTag, "w:footer")) ?? DEFAULT_DOCUMENT_LAYOUT.footerDistancePx;
3601
+ const docGridType = readStringAttribute(docGridTag, "w:type")?.toLowerCase();
3602
+ const docGridLinePitchPx = docGridType === "lines" || docGridType === "linesandchars" || docGridType === "snaptochars" ? twipsToPixels(readTwipsAttribute(docGridTag, "w:linePitch")) ?? DEFAULT_DOCUMENT_LAYOUT.docGridLinePitchPx : DEFAULT_DOCUMENT_LAYOUT.docGridLinePitchPx;
3603
+ return {
3604
+ pageWidthPx: pageSize.widthPx,
3605
+ pageHeightPx: pageSize.heightPx,
3606
+ marginsPx: {
3607
+ top: topMarginPx,
3608
+ right: rightMarginPx,
3609
+ bottom: bottomMarginPx,
3610
+ left: leftMarginPx
3611
+ },
3612
+ headerDistancePx,
3613
+ footerDistancePx,
3614
+ docGridLinePitchPx
3615
+ };
3565
3616
  }
3566
- function paragraphHasExplicitPageBreak(paragraph) {
3567
- const sourceXml = paragraph.sourceXml ?? "";
3568
- return sourceXml.length > 0 && PAGE_BREAK_XML_PATTERN.test(sourceXml);
3617
+ function resolveDocumentSectionPropertiesXml(model) {
3618
+ return model.metadata.sections?.[0]?.sectionPropertiesXml ?? model.metadata.sectionPropertiesXml;
3569
3619
  }
3570
- function paragraphHasPageBreakBefore(paragraph) {
3571
- if (paragraph.style?.pageBreakBefore === true) {
3620
+ function resolveDocumentLayout(model) {
3621
+ return parseSectionLayout(resolveDocumentSectionPropertiesXml(model));
3622
+ }
3623
+
3624
+ // src/image-render.ts
3625
+ import { encode as encodePng } from "fast-png";
3626
+ import UTIFModule from "utif";
3627
+ var TIFF_CONTENT_TYPES = /* @__PURE__ */ new Set(["image/tiff", "image/tif"]);
3628
+ var PLACEHOLDER_FALLBACK_CONTENT_TYPES = /* @__PURE__ */ new Set([
3629
+ "image/x-emf",
3630
+ "image/emf",
3631
+ "image/x-wmf",
3632
+ "image/wmf"
3633
+ ]);
3634
+ var TIFF_DATA_URI_PREFIXES = ["data:image/tiff", "data:image/tif"];
3635
+ var EMF_DATA_URI_PREFIXES = ["data:image/emf", "data:image/x-emf"];
3636
+ var WMF_DATA_URI_PREFIXES = ["data:image/wmf", "data:image/x-wmf"];
3637
+ var UTIF = UTIFModule;
3638
+ var convertedTiffSrcCache = /* @__PURE__ */ new Map();
3639
+ function normalizeImageContentType(image) {
3640
+ return image.contentType?.trim().toLowerCase();
3641
+ }
3642
+ function normalizeImageSrc(image) {
3643
+ return image.src?.trim();
3644
+ }
3645
+ function imageHasTiffContent(image) {
3646
+ const contentType = normalizeImageContentType(image);
3647
+ if (contentType && TIFF_CONTENT_TYPES.has(contentType)) {
3572
3648
  return true;
3573
3649
  }
3574
- const sourceXml = paragraph.sourceXml ?? "";
3575
- if (!sourceXml) {
3576
- return false;
3577
- }
3578
- return isOnOffTagEnabled(sourceXml.match(PAGE_BREAK_BEFORE_XML_PATTERN)?.[0]);
3650
+ const src = normalizeImageSrc(image)?.toLowerCase();
3651
+ return Boolean(src && TIFF_DATA_URI_PREFIXES.some((prefix) => src.startsWith(prefix)));
3579
3652
  }
3580
- function paragraphHasVisibleText(paragraph) {
3581
- return paragraph.children.some(
3582
- (child) => child.type === "text" && child.text.trim().length > 0 || child.type === "form-field" && (child.value ?? "").trim().length > 0
3653
+ function imageHasMetafileContent(image) {
3654
+ const contentType = normalizeImageContentType(image);
3655
+ if (contentType && PLACEHOLDER_FALLBACK_CONTENT_TYPES.has(contentType)) {
3656
+ return true;
3657
+ }
3658
+ const src = normalizeImageSrc(image)?.toLowerCase();
3659
+ return Boolean(
3660
+ src && (EMF_DATA_URI_PREFIXES.some((prefix) => src.startsWith(prefix)) || WMF_DATA_URI_PREFIXES.some((prefix) => src.startsWith(prefix)))
3583
3661
  );
3584
3662
  }
3585
- function paragraphHasImage(paragraph) {
3586
- return paragraph.children.some((child) => child.type === "image");
3587
- }
3588
- function paragraphHasFormField(paragraph) {
3589
- return paragraph.children.some((child) => child.type === "form-field");
3663
+ function imageUsesEmfContent(image) {
3664
+ const contentType = normalizeImageContentType(image);
3665
+ if (contentType === "image/x-emf" || contentType === "image/emf") {
3666
+ return true;
3667
+ }
3668
+ const src = normalizeImageSrc(image)?.toLowerCase();
3669
+ return Boolean(src && EMF_DATA_URI_PREFIXES.some((prefix) => src.startsWith(prefix)));
3590
3670
  }
3591
- function paragraphIsOnlyExplicitPageBreak(paragraph) {
3592
- return paragraphHasExplicitPageBreak(paragraph) && !paragraphHasVisibleText(paragraph) && !paragraphHasImage(paragraph) && !paragraphHasFormField(paragraph);
3671
+ function imageUsesWmfContent(image) {
3672
+ const contentType = normalizeImageContentType(image);
3673
+ if (contentType === "image/x-wmf" || contentType === "image/wmf") {
3674
+ return true;
3675
+ }
3676
+ const src = normalizeImageSrc(image)?.toLowerCase();
3677
+ return Boolean(src && WMF_DATA_URI_PREFIXES.some((prefix) => src.startsWith(prefix)));
3593
3678
  }
3594
- function tableCellDirectParagraphs(nodeContent) {
3595
- return nodeContent.filter((entry) => entry.type === "paragraph");
3679
+ function imageUsesPlaceholderFallback(image) {
3680
+ return imageHasMetafileContent(image);
3596
3681
  }
3597
- function tableRowHasSubstantiveContentOutsideBreakOnlyParagraphs(row) {
3598
- return row.cells.some(
3599
- (cell) => tableCellDirectParagraphs(cell.nodes).some((paragraph) => {
3600
- if (paragraphIsOnlyExplicitPageBreak(paragraph)) {
3601
- return false;
3602
- }
3603
- return paragraphHasVisibleText(paragraph) || paragraphHasImage(paragraph) || paragraphHasFormField(paragraph) || paragraphHasExplicitPageBreak(paragraph);
3604
- })
3605
- );
3682
+ function unsupportedImageFallbackLabel(image, widthPx, heightPx) {
3683
+ const isSmallIcon = (widthPx ?? 0) <= 56 && (heightPx ?? 0) <= 56;
3684
+ if (imageUsesEmfContent(image)) {
3685
+ return isSmallIcon ? "e" : "EMF";
3686
+ }
3687
+ if (imageUsesWmfContent(image)) {
3688
+ return isSmallIcon ? "w" : "WMF";
3689
+ }
3690
+ return isSmallIcon ? "e" : "TIFF";
3606
3691
  }
3607
- function tableRowSubstantiveCellIndexes(row) {
3608
- const indexes = [];
3609
- row.cells.forEach((cell, cellIndex) => {
3610
- const substantive = tableCellDirectParagraphs(cell.nodes).some((paragraph) => {
3611
- if (paragraphIsOnlyExplicitPageBreak(paragraph)) {
3612
- return false;
3613
- }
3614
- return paragraphHasVisibleText(paragraph) || paragraphHasImage(paragraph) || paragraphHasFormField(paragraph) || paragraphHasExplicitPageBreak(paragraph);
3615
- });
3616
- if (substantive) {
3617
- indexes.push(cellIndex);
3618
- }
3619
- });
3620
- return indexes;
3692
+ function dataUriToBytes(dataUri) {
3693
+ const match = dataUri.match(/^data:([^;]+);base64,(.+)$/i);
3694
+ if (!match) {
3695
+ return void 0;
3696
+ }
3697
+ const [, , base64] = match;
3698
+ if (typeof Buffer !== "undefined") {
3699
+ return new Uint8Array(Buffer.from(base64, "base64"));
3700
+ }
3701
+ if (typeof atob !== "function") {
3702
+ return void 0;
3703
+ }
3704
+ const binary = atob(base64);
3705
+ const bytes = new Uint8Array(binary.length);
3706
+ for (let index = 0; index < binary.length; index += 1) {
3707
+ bytes[index] = binary.charCodeAt(index);
3708
+ }
3709
+ return bytes;
3621
3710
  }
3622
- function tableRowUsesTrailingSignatureCellBreakPattern(table, row) {
3623
- if (table.rows.length !== 1 || row.cells.length < 4) {
3624
- return false;
3711
+ function bytesToBase64(bytes) {
3712
+ if (typeof Buffer !== "undefined") {
3713
+ return Buffer.from(bytes).toString("base64");
3625
3714
  }
3626
- const substantiveIndexes = tableRowSubstantiveCellIndexes(row);
3627
- return substantiveIndexes.length === 1 && substantiveIndexes[0] >= row.cells.length - 1;
3715
+ if (typeof btoa !== "function") {
3716
+ return void 0;
3717
+ }
3718
+ let binary = "";
3719
+ const chunkSize = 32768;
3720
+ for (let index = 0; index < bytes.byteLength; index += chunkSize) {
3721
+ const chunk = bytes.subarray(index, index + chunkSize);
3722
+ binary += String.fromCharCode(...chunk);
3723
+ }
3724
+ return btoa(binary);
3628
3725
  }
3629
- function collectTableExplicitPageBreakInfo(table) {
3630
- const sourceXml = table.sourceXml ?? "";
3631
- if (sourceXml) {
3632
- const cached = tableExplicitPageBreakInfoBySourceXml.get(sourceXml);
3633
- if (cached) {
3634
- return cached;
3635
- }
3726
+ function toArrayBuffer(bytes) {
3727
+ const { buffer, byteOffset, byteLength } = bytes;
3728
+ if (byteOffset === 0 && byteLength === buffer.byteLength) {
3729
+ return buffer;
3636
3730
  }
3637
- const startRowIndexes = /* @__PURE__ */ new Set();
3638
- let breakAfterTable = false;
3639
- table.rows.forEach((row, rowIndex) => {
3640
- let rowBreakTarget;
3641
- row.cells.forEach((cell) => {
3642
- tableCellDirectParagraphs(cell.nodes).forEach((paragraph) => {
3643
- if (!paragraphHasExplicitPageBreak(paragraph)) {
3644
- return;
3645
- }
3646
- if (paragraphIsOnlyExplicitPageBreak(paragraph) && rowIndex === 0 && tableRowHasSubstantiveContentOutsideBreakOnlyParagraphs(row) && !tableRowUsesTrailingSignatureCellBreakPattern(table, row)) {
3647
- return;
3648
- }
3649
- const breakTarget = paragraphIsOnlyExplicitPageBreak(paragraph) ? rowIndex : rowIndex + 1;
3650
- if (rowBreakTarget === void 0 || breakTarget < rowBreakTarget) {
3651
- rowBreakTarget = breakTarget;
3652
- }
3653
- });
3654
- });
3655
- if (rowBreakTarget === void 0) {
3656
- return;
3657
- }
3658
- if (rowBreakTarget >= table.rows.length) {
3659
- breakAfterTable = true;
3660
- return;
3661
- }
3662
- startRowIndexes.add(Math.max(0, rowBreakTarget));
3663
- });
3664
- const info = {
3665
- startRowIndexes: [...startRowIndexes].sort((left, right) => left - right),
3666
- breakAfterTable
3667
- };
3668
- if (sourceXml) {
3669
- tableExplicitPageBreakInfoBySourceXml.set(sourceXml, info);
3731
+ return buffer.slice(byteOffset, byteOffset + byteLength);
3732
+ }
3733
+ function resolveTiffBytes(image) {
3734
+ if (image.data?.byteLength) {
3735
+ return image.data;
3670
3736
  }
3671
- return info;
3737
+ const src = normalizeImageSrc(image);
3738
+ return src ? dataUriToBytes(src) : void 0;
3672
3739
  }
3673
- function collectTopLevelExplicitPageBreakStartNodeIndexes(nodes) {
3674
- const breaks = /* @__PURE__ */ new Set();
3675
- for (let nodeIndex = 0; nodeIndex < nodes.length; nodeIndex += 1) {
3676
- const node = nodes[nodeIndex];
3677
- const hasNextNode = nodeIndex + 1 < nodes.length;
3678
- if (node.type === "paragraph") {
3679
- if (paragraphHasPageBreakBefore(node)) {
3680
- breaks.add(nodeIndex);
3681
- }
3682
- if (hasNextNode && paragraphHasExplicitPageBreak(node)) {
3683
- breaks.add(nodeIndex + 1);
3684
- }
3740
+ function selectPrimaryTiffPage(ifds) {
3741
+ if (ifds.length === 0) {
3742
+ return void 0;
3743
+ }
3744
+ let candidates = ifds;
3745
+ if (Array.isArray(ifds[0]?.subIFD) && ifds[0].subIFD.length > 0) {
3746
+ candidates = candidates.concat(ifds[0].subIFD);
3747
+ }
3748
+ let best;
3749
+ let maxArea = -1;
3750
+ for (const candidate of candidates) {
3751
+ const width = candidate.width ?? candidate.t256 ?? 0;
3752
+ const height = candidate.height ?? candidate.t257 ?? 0;
3753
+ if (!width || !height) {
3685
3754
  continue;
3686
3755
  }
3687
- const tableBreakInfo = collectTableExplicitPageBreakInfo(node);
3688
- if (tableBreakInfo.startRowIndexes.includes(0) && nodeIndex > 0) {
3689
- breaks.add(nodeIndex);
3690
- }
3691
- if (hasNextNode && tableBreakInfo.breakAfterTable) {
3692
- breaks.add(nodeIndex + 1);
3756
+ const area = width * height;
3757
+ if (best === void 0 || area > maxArea) {
3758
+ best = candidate;
3759
+ maxArea = area;
3693
3760
  }
3694
3761
  }
3695
- for (const breakIndex of [...breaks]) {
3696
- if (breakIndex <= 0 || breakIndex >= nodes.length) {
3697
- breaks.delete(breakIndex);
3698
- }
3762
+ return best ?? ifds[0];
3763
+ }
3764
+ function convertTiffToPngDataUri(bytes) {
3765
+ const tiffBuffer = toArrayBuffer(bytes);
3766
+ const ifds = UTIF.decode(tiffBuffer);
3767
+ const page = selectPrimaryTiffPage(ifds);
3768
+ if (!page) {
3769
+ return void 0;
3699
3770
  }
3700
- return breaks;
3771
+ UTIF.decodeImage(tiffBuffer, page, ifds);
3772
+ const width = page.width ?? page.t256 ?? 0;
3773
+ const height = page.height ?? page.t257 ?? 0;
3774
+ const rgba = UTIF.toRGBA8(page);
3775
+ if (!width || !height || rgba.byteLength !== width * height * 4) {
3776
+ return void 0;
3777
+ }
3778
+ const pngBytes = encodePng({
3779
+ width,
3780
+ height,
3781
+ data: rgba,
3782
+ channels: 4,
3783
+ depth: 8
3784
+ });
3785
+ const base64 = bytesToBase64(pngBytes);
3786
+ return base64 ? `data:image/png;base64,${base64}` : void 0;
3701
3787
  }
3702
-
3703
- // src/page-count-reconciliation.ts
3704
- var PAGE_COUNT_RECONCILIATION_REFINEMENT_STEPS = 10;
3705
- function shouldAllowStoredPageCountReduction(options) {
3706
- const estimatedPageCount = Math.max(
3707
- 1,
3708
- Math.round(options.estimatedPageCount)
3709
- );
3710
- const targetPageCount = Math.max(1, Math.round(options.targetPageCount));
3711
- if (targetPageCount >= estimatedPageCount) {
3712
- return true;
3788
+ function resolveRenderableImageSource(image) {
3789
+ const src = normalizeImageSrc(image);
3790
+ if (!src) {
3791
+ return void 0;
3713
3792
  }
3714
- const renderedBreakHintPageCount = Number.isFinite(
3715
- options.renderedBreakHintPageCount
3716
- ) ? Math.max(1, Math.round(options.renderedBreakHintPageCount)) : void 0;
3717
- const renderedBreakHintsSupportTarget = options.hasLastRenderedPageBreakHints === true && renderedBreakHintPageCount !== void 0 && targetPageCount >= renderedBreakHintPageCount;
3718
- if (options.hasMeasuredBodyFooterOverlap === true) {
3719
- return renderedBreakHintsSupportTarget;
3793
+ if (imageHasMetafileContent(image)) {
3794
+ return void 0;
3720
3795
  }
3721
- if (options.hasLastRenderedPageBreakHints !== true) {
3722
- return true;
3796
+ if (!imageHasTiffContent(image)) {
3797
+ return src;
3723
3798
  }
3724
- return renderedBreakHintsSupportTarget;
3725
- }
3726
- function shouldLatchMeasuredBodyFooterOverlap(options) {
3727
- if (options.measuredBodyFooterOverlap !== true) {
3728
- return false;
3799
+ const cached = convertedTiffSrcCache.get(src);
3800
+ if (cached !== void 0 || convertedTiffSrcCache.has(src)) {
3801
+ return cached;
3729
3802
  }
3730
- const targetPageCount = Number.isFinite(options.targetPageCount) ? Math.max(1, Math.round(options.targetPageCount)) : void 0;
3731
- if (targetPageCount === void 0) {
3732
- return false;
3803
+ let converted;
3804
+ try {
3805
+ const bytes = resolveTiffBytes(image);
3806
+ if (bytes) {
3807
+ converted = convertTiffToPngDataUri(bytes);
3808
+ }
3809
+ } catch {
3810
+ converted = void 0;
3733
3811
  }
3734
- const pageCount = Math.max(1, Math.round(options.pageCount));
3735
- return pageCount <= targetPageCount;
3812
+ convertedTiffSrcCache.set(src, converted);
3813
+ return converted;
3736
3814
  }
3737
- function resolveMeasuredBodyFooterOverlapLatchState(options) {
3738
- const shouldConsiderMeasuredBodyFooterOverlap = shouldLatchMeasuredBodyFooterOverlap({
3739
- pageCount: options.pageCount,
3740
- targetPageCount: options.targetPageCount,
3741
- measuredBodyFooterOverlap: options.overlappingPageIndexes.length > 0
3742
- });
3743
- if (!shouldConsiderMeasuredBodyFooterOverlap) {
3744
- return {
3745
- signature: void 0,
3746
- consecutivePasses: 0,
3747
- shouldLatch: false
3748
- };
3749
- }
3750
- const signature = options.overlappingPageIndexes.map((pageIndex) => `${pageIndex}`).join("|");
3751
- const consecutivePasses = signature === options.previousSignature ? Math.max(0, Math.round(options.previousConsecutivePasses ?? 0)) + 1 : 1;
3752
- const stabilityThreshold = Math.max(
3753
- 1,
3754
- Math.round(options.stabilityThreshold ?? 1)
3755
- );
3756
- return {
3757
- signature,
3758
- consecutivePasses,
3759
- shouldLatch: consecutivePasses >= stabilityThreshold
3815
+ function subscribeRenderableImageSourceUpdates(listener) {
3816
+ void listener;
3817
+ return () => {
3760
3818
  };
3761
3819
  }
3762
- function createEstimatedPagesBuildMemo(build) {
3763
- const entries = [];
3764
- return (measuredTableRowHeightsByNodeIndex, measuredPageContentHeightsPxByPageIndex, strictLastRenderedParagraphStartBreaks) => {
3765
- const cached = entries.find(
3766
- (entry) => entry.measuredTableRowHeightsByNodeIndex === measuredTableRowHeightsByNodeIndex && entry.measuredPageContentHeightsPxByPageIndex === measuredPageContentHeightsPxByPageIndex && entry.strictLastRenderedParagraphStartBreaks === strictLastRenderedParagraphStartBreaks
3767
- );
3768
- if (cached) {
3769
- return cached.pages;
3770
- }
3771
- const pages = build(
3772
- measuredTableRowHeightsByNodeIndex,
3773
- measuredPageContentHeightsPxByPageIndex,
3774
- strictLastRenderedParagraphStartBreaks
3775
- );
3776
- entries.push({
3777
- measuredTableRowHeightsByNodeIndex,
3778
- measuredPageContentHeightsPxByPageIndex,
3779
- strictLastRenderedParagraphStartBreaks,
3780
- pages
3781
- });
3782
- return pages;
3783
- };
3820
+
3821
+ // src/script-fonts.ts
3822
+ var EAST_ASIA_SCRIPT_RE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Bopomofo}\p{Script=Yi}]/u;
3823
+ var COMPLEX_SCRIPT_RE = /[\p{Script=Arabic}\p{Script=Hebrew}\p{Script=Syriac}\p{Script=Thaana}\p{Script=Nko}\p{Script=Samaritan}\p{Script=Mandaic}\p{Script=Devanagari}\p{Script=Bengali}\p{Script=Gurmukhi}\p{Script=Gujarati}\p{Script=Oriya}\p{Script=Tamil}\p{Script=Telugu}\p{Script=Kannada}\p{Script=Malayalam}\p{Script=Sinhala}\p{Script=Thai}\p{Script=Lao}\p{Script=Tibetan}\p{Script=Myanmar}\p{Script=Khmer}]/u;
3824
+ var NEUTRAL_OR_CONTINUATION_RE = /[\p{Mark}\p{Separator}\p{Punctuation}\u200c\u200d\ufe00-\ufe0f]/u;
3825
+ var EAST_ASIA_LANGUAGE_RE = /^(?:ja|ko|zh)(?:-|$)/i;
3826
+ var COMPLEX_SCRIPT_LANGUAGE_RE = /^(?:ar|ckb|dv|fa|he|hi|ks|ku|pa-arab|ps|sd|syr|ug|ur|yi)(?:-|$)/i;
3827
+ function normalizeFamily(value) {
3828
+ const normalized = value?.trim();
3829
+ return normalized || void 0;
3830
+ }
3831
+ function normalizedFamilyKey(value) {
3832
+ return normalizeFamily(value)?.replace(/^['"]+|['"]+$/g, "").toLowerCase();
3833
+ }
3834
+ function hasLegacyFontFamilyOverride(style) {
3835
+ const current = normalizedFamilyKey(style?.fontFamily);
3836
+ const imported = normalizedFamilyKey(style?.sourceFontFamily);
3837
+ if (!current) {
3838
+ return false;
3839
+ }
3840
+ if (imported) {
3841
+ return current !== imported;
3842
+ }
3843
+ return !(normalizeFamily(style?.fontFamilyAscii) || normalizeFamily(style?.fontFamilyHAnsi) || normalizeFamily(style?.fontFamilyEastAsia) || normalizeFamily(style?.fontFamilyCs) || normalizeFamily(style?.fontThemeAscii) || normalizeFamily(style?.fontThemeHAnsi) || normalizeFamily(style?.fontThemeEastAsia) || normalizeFamily(style?.fontThemeCs));
3844
+ }
3845
+ function normalizedFontHint(style) {
3846
+ return style?.fontHint?.trim().replace(/[-_\s]/g, "").toLowerCase() ?? "";
3784
3847
  }
3785
- function resolveMeasuredSplitParagraphPageComparison(options) {
3786
- if (options.canUndo || options.canRedo || !options.hasMeasuredPageContentHeights) {
3787
- return {
3788
- pages: options.measuredEstimatedPages,
3789
- usedPureEstimatedPages: false
3790
- };
3848
+ function neutralScript(style) {
3849
+ const hint = normalizedFontHint(style);
3850
+ if (hint === "eastasia") {
3851
+ return "eastAsia";
3791
3852
  }
3792
- const measuredPagesAreOnlySplitParagraphs = options.measuredEstimatedPages.length > 0 && options.measuredEstimatedPages.every(
3793
- options.pageContainsOnlySplitParagraphSegments
3794
- );
3795
- if (!measuredPagesAreOnlySplitParagraphs) {
3796
- return {
3797
- pages: options.measuredEstimatedPages,
3798
- usedPureEstimatedPages: false
3799
- };
3853
+ if (hint === "cs" || hint === "complexscript" || hint === "bidi") {
3854
+ return "complexScript";
3800
3855
  }
3801
- const pureEstimatedPages = options.buildPureEstimatedPages();
3802
- const purePagesAreOnlySplitParagraphs = pureEstimatedPages.length > 0 && pureEstimatedPages.every(options.pageContainsOnlySplitParagraphSegments);
3803
- if (purePagesAreOnlySplitParagraphs && pureEstimatedPages.length < options.measuredEstimatedPages.length) {
3804
- return { pages: pureEstimatedPages, usedPureEstimatedPages: true };
3856
+ if (style?.rightToLeft || COMPLEX_SCRIPT_LANGUAGE_RE.test(style?.languageBidi?.trim() ?? "")) {
3857
+ return "complexScript";
3805
3858
  }
3806
- return {
3807
- pages: options.measuredEstimatedPages,
3808
- usedPureEstimatedPages: false
3809
- };
3859
+ if (EAST_ASIA_LANGUAGE_RE.test(style?.languageEastAsia?.trim() ?? "")) {
3860
+ return "eastAsia";
3861
+ }
3862
+ return "highAnsi";
3810
3863
  }
3811
- function isBetterCandidate(candidate, incumbent, targetPageCount) {
3812
- const candidateDifference = Math.abs(candidate.pageCount - targetPageCount);
3813
- const incumbentDifference = Math.abs(incumbent.pageCount - targetPageCount);
3814
- if (candidateDifference !== incumbentDifference) {
3815
- return candidateDifference < incumbentDifference;
3864
+ function classifyDocxFontScript(value, style) {
3865
+ const character = Array.from(value)[0];
3866
+ if (!character) {
3867
+ return void 0;
3816
3868
  }
3817
- const candidateScaleDelta = Math.abs(candidate.scale - 1);
3818
- const incumbentScaleDelta = Math.abs(incumbent.scale - 1);
3819
- if (candidateScaleDelta !== incumbentScaleDelta) {
3820
- return candidateScaleDelta < incumbentScaleDelta;
3869
+ if (style?.complexScript === true || style?.rightToLeft === true && style?.complexScript !== false) {
3870
+ return "complexScript";
3821
3871
  }
3822
- return candidate.pageCount === targetPageCount && incumbent.pageCount !== targetPageCount;
3872
+ const codePoint = character.codePointAt(0) ?? 0;
3873
+ if (codePoint <= 127) {
3874
+ return "ascii";
3875
+ }
3876
+ if (EAST_ASIA_SCRIPT_RE.test(character) || codePoint >= 12288 && codePoint <= 12351 || codePoint >= 65280 && codePoint <= 65519) {
3877
+ return "eastAsia";
3878
+ }
3879
+ if (COMPLEX_SCRIPT_RE.test(character)) {
3880
+ return "complexScript";
3881
+ }
3882
+ if (NEUTRAL_OR_CONTINUATION_RE.test(character)) {
3883
+ return void 0;
3884
+ }
3885
+ return "highAnsi";
3823
3886
  }
3824
- function reconcilePageCountCandidateToTargetCountByScalingHeight(options) {
3825
- const {
3826
- initialPages,
3827
- targetPageCount,
3828
- buildPagesAtScale,
3829
- maxDifference = 3,
3830
- scales: customScales
3831
- } = options;
3832
- const safeTargetPageCount = Math.max(1, Math.round(targetPageCount));
3833
- const initialPageCount = initialPages.length;
3834
- const initialDifference = Math.abs(initialPageCount - safeTargetPageCount);
3835
- if (initialDifference === 0 || initialDifference > maxDifference) {
3836
- return {
3837
- pageCount: initialPageCount,
3838
- pages: initialPages,
3839
- scale: 1
3840
- };
3887
+ function resolveDocxScriptFontFamily(style, script) {
3888
+ const legacyFamily = normalizeFamily(style?.fontFamily);
3889
+ if (hasLegacyFontFamilyOverride(style)) {
3890
+ return legacyFamily;
3841
3891
  }
3842
- const bestCandidate = {
3843
- pageCount: initialPageCount,
3844
- pages: initialPages,
3845
- scale: 1
3846
- };
3847
- let selectedCandidate = bestCandidate;
3848
- const needMorePages = initialPageCount < safeTargetPageCount;
3849
- const scales = customScales && customScales.length > 0 ? customScales : needMorePages ? [
3850
- 0.98,
3851
- 0.96,
3852
- 0.94,
3853
- 0.92,
3854
- 0.9,
3855
- 0.88,
3856
- 0.86,
3857
- 0.84,
3858
- 0.82,
3859
- 0.8,
3860
- 0.78,
3861
- 0.76,
3862
- 0.74,
3863
- 0.72,
3864
- 0.7,
3865
- 0.68,
3866
- 0.66,
3867
- 0.64,
3868
- 0.62,
3869
- 0.6,
3870
- 0.58,
3871
- 0.56,
3872
- 0.54,
3873
- 0.52,
3874
- 0.5,
3875
- 0.48,
3876
- 0.46,
3877
- 0.44,
3878
- 0.42,
3879
- 0.4,
3880
- 0.38,
3881
- 0.36,
3882
- 0.34,
3883
- 0.32,
3884
- 0.3,
3885
- 0.28,
3886
- 0.26,
3887
- 0.24,
3888
- 0.22,
3889
- 0.2
3890
- ] : [
3891
- 1.02,
3892
- 1.04,
3893
- 1.06,
3894
- 1.08,
3895
- 1.1,
3896
- 1.12,
3897
- 1.14,
3898
- 1.16,
3899
- 1.18,
3900
- 1.2,
3901
- 1.22,
3902
- 1.24,
3903
- 1.26,
3904
- 1.28,
3905
- 1.3
3906
- ];
3907
- const buildCandidateAtScale = (scale) => {
3908
- const pages = buildPagesAtScale(scale);
3909
- return {
3910
- pageCount: pages.length,
3911
- pages,
3912
- scale
3913
- };
3914
- };
3915
- const refineExactTargetCandidate = (lowerScale, upperScale) => {
3916
- let bestExactCandidate;
3917
- const interval = upperScale - lowerScale;
3918
- if (!Number.isFinite(interval) || Math.abs(interval) < 1e-4) {
3919
- return selectedCandidate;
3920
- }
3921
- for (let step = 1; step <= PAGE_COUNT_RECONCILIATION_REFINEMENT_STEPS; step += 1) {
3922
- const scale = lowerScale + interval * step / PAGE_COUNT_RECONCILIATION_REFINEMENT_STEPS;
3923
- const candidate = buildCandidateAtScale(scale);
3924
- if (isBetterCandidate(candidate, selectedCandidate, safeTargetPageCount)) {
3925
- selectedCandidate = candidate;
3926
- }
3927
- if (candidate.pageCount === safeTargetPageCount && (bestExactCandidate === void 0 || isBetterCandidate(
3928
- candidate,
3929
- bestExactCandidate,
3930
- safeTargetPageCount
3931
- ))) {
3932
- bestExactCandidate = candidate;
3933
- }
3934
- }
3935
- return bestExactCandidate ?? selectedCandidate;
3936
- };
3937
- let previousScale = 1;
3938
- let previousPageCount = initialPageCount;
3939
- for (const scale of scales) {
3940
- const candidate = buildCandidateAtScale(scale);
3941
- if (isBetterCandidate(candidate, selectedCandidate, safeTargetPageCount)) {
3942
- selectedCandidate = candidate;
3892
+ switch (script) {
3893
+ case "ascii":
3894
+ return normalizeFamily(style?.resolvedFontFamilyAscii) ?? normalizeFamily(style?.fontFamilyAscii) ?? normalizeFamily(style?.resolvedFontFamilyHAnsi) ?? normalizeFamily(style?.fontFamilyHAnsi) ?? legacyFamily;
3895
+ case "highAnsi":
3896
+ return normalizeFamily(style?.resolvedFontFamilyHAnsi) ?? normalizeFamily(style?.fontFamilyHAnsi) ?? normalizeFamily(style?.resolvedFontFamilyAscii) ?? normalizeFamily(style?.fontFamilyAscii) ?? legacyFamily;
3897
+ case "eastAsia":
3898
+ return normalizeFamily(style?.resolvedFontFamilyEastAsia) ?? normalizeFamily(style?.fontFamilyEastAsia) ?? normalizeFamily(style?.resolvedFontFamilyHAnsi) ?? normalizeFamily(style?.fontFamilyHAnsi) ?? normalizeFamily(style?.resolvedFontFamilyAscii) ?? normalizeFamily(style?.fontFamilyAscii) ?? legacyFamily;
3899
+ case "complexScript":
3900
+ return normalizeFamily(style?.resolvedFontFamilyCs) ?? normalizeFamily(style?.fontFamilyCs) ?? normalizeFamily(style?.resolvedFontFamilyHAnsi) ?? normalizeFamily(style?.fontFamilyHAnsi) ?? normalizeFamily(style?.resolvedFontFamilyAscii) ?? normalizeFamily(style?.fontFamilyAscii) ?? legacyFamily;
3901
+ }
3902
+ }
3903
+ function segmentTextByDocxScriptFont(text, style) {
3904
+ if (!text) {
3905
+ return [];
3906
+ }
3907
+ const tokens = [];
3908
+ let offset = 0;
3909
+ for (const character of text) {
3910
+ const startOffset = offset;
3911
+ offset += character.length;
3912
+ tokens.push({
3913
+ text: character,
3914
+ startOffset,
3915
+ endOffset: offset,
3916
+ script: classifyDocxFontScript(character, style)
3917
+ });
3918
+ }
3919
+ const fallbackScript = neutralScript(style);
3920
+ const nextStrongScript = new Array(
3921
+ tokens.length
3922
+ );
3923
+ let nextScript;
3924
+ for (let index = tokens.length - 1; index >= 0; index -= 1) {
3925
+ nextStrongScript[index] = nextScript;
3926
+ if (tokens[index]?.script) {
3927
+ nextScript = tokens[index]?.script;
3943
3928
  }
3944
- if (candidate.pageCount === safeTargetPageCount) {
3945
- const crossedIntoExactTarget = needMorePages ? previousPageCount < safeTargetPageCount : previousPageCount > safeTargetPageCount;
3946
- if (crossedIntoExactTarget) {
3947
- return refineExactTargetCandidate(previousScale, scale);
3948
- }
3949
- return candidate;
3929
+ }
3930
+ let previousScript;
3931
+ for (let index = 0; index < tokens.length; index += 1) {
3932
+ const token = tokens[index];
3933
+ if (!token) {
3934
+ continue;
3950
3935
  }
3951
- previousScale = scale;
3952
- previousPageCount = candidate.pageCount;
3936
+ token.script = token.script ?? previousScript ?? nextStrongScript[index] ?? fallbackScript;
3937
+ previousScript = token.script;
3953
3938
  }
3954
- if (!needMorePages && selectedCandidate.pageCount !== safeTargetPageCount) {
3955
- return {
3956
- pageCount: initialPageCount,
3957
- pages: initialPages,
3958
- scale: 1
3959
- };
3939
+ const segments = [];
3940
+ for (const token of tokens) {
3941
+ const script = token.script ?? fallbackScript;
3942
+ const fontFamily = resolveDocxScriptFontFamily(style, script);
3943
+ const previous = segments[segments.length - 1];
3944
+ if (previous && normalizedFamilyKey(previous.fontFamily) === normalizedFamilyKey(fontFamily)) {
3945
+ previous.text += token.text;
3946
+ previous.endOffset = token.endOffset;
3947
+ continue;
3948
+ }
3949
+ segments.push({
3950
+ text: token.text,
3951
+ startOffset: token.startOffset,
3952
+ endOffset: token.endOffset,
3953
+ script,
3954
+ fontFamily
3955
+ });
3960
3956
  }
3961
- return selectedCandidate;
3957
+ return segments;
3958
+ }
3959
+ function resolveDocxTextFontFamily(text, style) {
3960
+ return segmentTextByDocxScriptFont(text, style).find(
3961
+ (segment) => segment.fontFamily
3962
+ )?.fontFamily ?? normalizeFamily(style?.fontFamily);
3962
3963
  }
3963
3964
 
3964
3965
  // src/editor-transaction-state.ts
@@ -5826,7 +5827,7 @@ var SerialIdleTaskQueue = class {
5826
5827
  if (this.pending.length === 0) {
5827
5828
  return void 0;
5828
5829
  }
5829
- const now = this.now();
5830
+ const now2 = this.now();
5830
5831
  let earliestWaitMs;
5831
5832
  let bestIndex = -1;
5832
5833
  let bestEntry;
@@ -5836,7 +5837,7 @@ var SerialIdleTaskQueue = class {
5836
5837
  continue;
5837
5838
  }
5838
5839
  const lastRunAt = this.lastRunAtByKey.get(candidate.key);
5839
- const waitMs = lastRunAt === void 0 ? 0 : lastRunAt + this.minTaskIntervalMs - now;
5840
+ const waitMs = lastRunAt === void 0 ? 0 : lastRunAt + this.minTaskIntervalMs - now2;
5840
5841
  if (waitMs <= 0) {
5841
5842
  if (!bestEntry || candidate.priority < bestEntry.priority || candidate.priority === bestEntry.priority && candidate.sequence < bestEntry.sequence) {
5842
5843
  bestEntry = candidate;
@@ -22277,11 +22278,16 @@ function useDocxEditor(options = {}) {
22277
22278
  const starterTemplateRef = React.useRef(
22278
22279
  cloneDocModelWithBlockIds(options.starterModel ?? defaultStarterModel)
22279
22280
  );
22281
+ const initialModelRef = React.useRef(
22282
+ cloneDocModelWithBlockIds(
22283
+ options.document?.model ?? starterTemplateRef.current
22284
+ )
22285
+ );
22280
22286
  const [editorState, dispatchCanonicalEditorState] = React.useReducer(
22281
22287
  reduceDocxEditorState,
22282
- starterTemplateRef.current,
22283
- (starterModel) => ({
22284
- model: cloneDocModelWithBlockIds(starterModel),
22288
+ initialModelRef.current,
22289
+ (initialModel) => ({
22290
+ model: cloneDocModelWithBlockIds(initialModel),
22285
22291
  selection: {
22286
22292
  kind: "paragraph",
22287
22293
  nodeIndex: 0
@@ -22367,7 +22373,7 @@ function useDocxEditor(options = {}) {
22367
22373
  },
22368
22374
  [applyCanonicalEditorStateAction]
22369
22375
  );
22370
- const [basePackage, setBasePackage] = React.useState();
22376
+ const [basePackage, setBasePackage] = React.useState(options.document?.package);
22371
22377
  const [documentLoadNonce, setDocumentLoadNonce] = React.useState(0);
22372
22378
  const documentLoadNonceRef = React.useRef(0);
22373
22379
  const advanceDocumentLoadNonce = React.useCallback(() => {
@@ -22375,11 +22381,11 @@ function useDocxEditor(options = {}) {
22375
22381
  setDocumentLoadNonce(documentLoadNonceRef.current);
22376
22382
  }, []);
22377
22383
  const [fileName, setFileName] = React.useState(
22378
- options.initialFileName ?? "(new document)"
22384
+ options.initialFileName ?? options.document?.fileName ?? "(new document)"
22379
22385
  );
22380
22386
  const [selectedFormFieldLocation, setSelectedFormFieldLocation] = React.useState();
22381
22387
  const [status, setStatus] = React.useState(
22382
- options.initialStatus ?? "Ready"
22388
+ options.initialStatus ?? (options.document?.fileName ? `Loaded ${options.document.fileName}` : "Ready")
22383
22389
  );
22384
22390
  const [importError, setImportError] = React.useState();
22385
22391
  const [isImporting, setIsImporting] = React.useState(false);
@@ -22796,6 +22802,16 @@ function useDocxEditor(options = {}) {
22796
22802
  unloadEmbeddedFonts();
22797
22803
  };
22798
22804
  }, [unloadEmbeddedFonts]);
22805
+ React.useEffect(() => {
22806
+ if (!options.document || options.loadEmbeddedFonts === false || options.document.embeddedFontsLoaded) {
22807
+ return;
22808
+ }
22809
+ void loadEmbeddedFontsFromPackage(options.document.package);
22810
+ }, [
22811
+ loadEmbeddedFontsFromPackage,
22812
+ options.document,
22813
+ options.loadEmbeddedFonts
22814
+ ]);
22799
22815
  const dispatchEditorTransaction = React.useCallback(
22800
22816
  (resolver) => {
22801
22817
  const currentState = canonicalEditorStateRef.current;
@@ -26182,7 +26198,7 @@ function useDocxEditor(options = {}) {
26182
26198
  );
26183
26199
  const commitParagraphTextAtRange = React.useCallback(
26184
26200
  (location, text, caret, options2) => {
26185
- const now = Date.now();
26201
+ const now2 = Date.now();
26186
26202
  return dispatchEditorTransaction((current) => {
26187
26203
  const paragraph = getParagraphAtLocation(
26188
26204
  current.model,
@@ -26199,9 +26215,9 @@ function useDocxEditor(options = {}) {
26199
26215
  const coalesce = options2?.coalesceKey !== void 0 && shouldCoalesceTextCommit(textCommitBurstRef.current, {
26200
26216
  key: options2.coalesceKey,
26201
26217
  caretBefore: insertionOffset,
26202
- at: now
26218
+ at: now2
26203
26219
  });
26204
- textCommitBurstRef.current = options2?.coalesceKey !== void 0 ? { key: options2.coalesceKey, caretAfter: caret.end, at: now } : void 0;
26220
+ textCommitBurstRef.current = options2?.coalesceKey !== void 0 ? { key: options2.coalesceKey, caretAfter: caret.end, at: now2 } : void 0;
26205
26221
  return {
26206
26222
  model: nextModel,
26207
26223
  selection: selectionFromTextRangeLocation(location),
@@ -26982,7 +26998,7 @@ function DocxDetachedThumbnailPageSurface({
26982
26998
  {
26983
26999
  editor,
26984
27000
  mode: "read-only",
26985
- visiblePageRange: { startPageIndex: pageIndex, endPageIndex: pageIndex },
27001
+ pageIndexes: [pageIndex],
26986
27002
  pageVirtualization: { enabled: false },
26987
27003
  showTrackedChanges: editor.showTrackedChanges,
26988
27004
  showComments: editor.showComments,
@@ -26999,6 +27015,7 @@ var DocxDetachedThumbnailSurfaceRenderer = class {
26999
27015
  root;
27000
27016
  activePageIndex;
27001
27017
  activeRenderKey;
27018
+ rootGeneration = 0;
27002
27019
  async renderPageSurface(params) {
27003
27020
  if (typeof document === "undefined" || typeof window === "undefined") {
27004
27021
  return void 0;
@@ -27024,16 +27041,24 @@ var DocxDetachedThumbnailSurfaceRenderer = class {
27024
27041
  return this.waitForPageSurface(registry, pageIndex);
27025
27042
  }
27026
27043
  clear() {
27027
- if (this.root) {
27028
- this.root.unmount();
27029
- this.root = void 0;
27030
- }
27031
- if (this.host?.parentNode) {
27032
- this.host.parentNode.removeChild(this.host);
27033
- }
27044
+ const root = this.root;
27045
+ const host = this.host;
27046
+ this.rootGeneration += 1;
27047
+ this.root = void 0;
27034
27048
  this.host = void 0;
27035
27049
  this.activePageIndex = void 0;
27036
27050
  this.activeRenderKey = void 0;
27051
+ const dispose = () => {
27052
+ root?.unmount();
27053
+ if (host?.parentNode) {
27054
+ host.parentNode.removeChild(host);
27055
+ }
27056
+ };
27057
+ if (root && typeof window !== "undefined") {
27058
+ window.setTimeout(dispose, 0);
27059
+ } else {
27060
+ dispose();
27061
+ }
27037
27062
  }
27038
27063
  async ensureRoot() {
27039
27064
  if (this.root) {
@@ -27042,6 +27067,7 @@ var DocxDetachedThumbnailSurfaceRenderer = class {
27042
27067
  if (typeof document === "undefined") {
27043
27068
  return;
27044
27069
  }
27070
+ const generation = this.rootGeneration;
27045
27071
  const host = document.createElement("div");
27046
27072
  host.setAttribute("data-docx-thumbnail-detached-renderer", "true");
27047
27073
  Object.assign(host.style, {
@@ -27058,6 +27084,12 @@ var DocxDetachedThumbnailSurfaceRenderer = class {
27058
27084
  document.body.appendChild(host);
27059
27085
  this.host = host;
27060
27086
  const { createRoot } = await import("react-dom/client");
27087
+ if (generation !== this.rootGeneration) {
27088
+ if (host.parentNode) {
27089
+ host.parentNode.removeChild(host);
27090
+ }
27091
+ return;
27092
+ }
27061
27093
  this.root = createRoot(host);
27062
27094
  }
27063
27095
  async waitForPageSurface(registry, pageIndex) {
@@ -27195,6 +27227,7 @@ function useDocxPageThumbnails(editor, options = {}) {
27195
27227
  const nextThumbnailCanvasQueueIdRef = React.useRef(0);
27196
27228
  const queuedPrefetchThumbnailKeysRef = React.useRef(/* @__PURE__ */ new Set());
27197
27229
  const thumbnailMinRasterIntervalMs = normalizeDocxThumbnailMinRasterIntervalMs(options.minRasterIntervalMs);
27230
+ const defaultThumbnailScheduling = options.scheduling ?? "idle";
27198
27231
  const ensureThumbnailSurfaceCache = React.useCallback(() => {
27199
27232
  if (!thumbnailSurfaceCacheRef.current) {
27200
27233
  thumbnailSurfaceCacheRef.current = new DocxThumbnailSurfaceCache(
@@ -27275,20 +27308,43 @@ function useDocxPageThumbnails(editor, options = {}) {
27275
27308
  [editor.documentTheme, pageSurfaceRegistry, thumbnailResolutionOptionsKey]
27276
27309
  );
27277
27310
  const totalThumbnailPages = Math.max(1, editor.totalPages);
27278
- const visibleThumbnailPageIndexes = React.useMemo(
27279
- () => normalizeDocxThumbnailPageIndexes(
27280
- options.renderWindow?.visiblePageIndexes,
27311
+ const hardThumbnailPageIndexes = React.useMemo(
27312
+ () => options.pageIndexes === void 0 ? void 0 : normalizeDocxThumbnailPageIndexes(
27313
+ options.pageIndexes,
27281
27314
  totalThumbnailPages
27282
27315
  ),
27283
- [options.renderWindow?.visiblePageIndexes, totalThumbnailPages]
27316
+ [options.pageIndexes, totalThumbnailPages]
27284
27317
  );
27285
- const prefetchThumbnailPageIndexes = React.useMemo(
27286
- () => normalizeDocxThumbnailPageIndexes(
27318
+ const hardThumbnailPageIndexSet = React.useMemo(
27319
+ () => hardThumbnailPageIndexes ? new Set(hardThumbnailPageIndexes) : void 0,
27320
+ [hardThumbnailPageIndexes]
27321
+ );
27322
+ const visibleThumbnailPageIndexes = React.useMemo(() => {
27323
+ const normalized = normalizeDocxThumbnailPageIndexes(
27324
+ options.renderWindow?.visiblePageIndexes,
27325
+ totalThumbnailPages
27326
+ );
27327
+ return hardThumbnailPageIndexSet ? normalized.filter(
27328
+ (pageIndex) => hardThumbnailPageIndexSet.has(pageIndex)
27329
+ ) : normalized;
27330
+ }, [
27331
+ hardThumbnailPageIndexSet,
27332
+ options.renderWindow?.visiblePageIndexes,
27333
+ totalThumbnailPages
27334
+ ]);
27335
+ const prefetchThumbnailPageIndexes = React.useMemo(() => {
27336
+ const normalized = normalizeDocxThumbnailPageIndexes(
27287
27337
  options.renderWindow?.prefetchPageIndexes,
27288
27338
  totalThumbnailPages
27289
- ),
27290
- [options.renderWindow?.prefetchPageIndexes, totalThumbnailPages]
27291
- );
27339
+ );
27340
+ return hardThumbnailPageIndexSet ? normalized.filter(
27341
+ (pageIndex) => hardThumbnailPageIndexSet.has(pageIndex)
27342
+ ) : normalized;
27343
+ }, [
27344
+ hardThumbnailPageIndexSet,
27345
+ options.renderWindow?.prefetchPageIndexes,
27346
+ totalThumbnailPages
27347
+ ]);
27292
27348
  const visibleThumbnailPageIndexesKey = docxThumbnailPageIndexesKey(
27293
27349
  visibleThumbnailPageIndexes
27294
27350
  );
@@ -27445,6 +27501,10 @@ function useDocxPageThumbnails(editor, options = {}) {
27445
27501
  await paintIntoTarget();
27446
27502
  return;
27447
27503
  }
27504
+ if ((renderOptions?.scheduling ?? defaultThumbnailScheduling) === "immediate") {
27505
+ await paintIntoTarget();
27506
+ return;
27507
+ }
27448
27508
  await ensureThumbnailRasterQueue().enqueue(
27449
27509
  thumbnailQueueKeyForCanvas(targetCanvas),
27450
27510
  paintIntoTarget,
@@ -27458,6 +27518,7 @@ function useDocxPageThumbnails(editor, options = {}) {
27458
27518
  },
27459
27519
  [
27460
27520
  ensureThumbnailRasterQueue,
27521
+ defaultThumbnailScheduling,
27461
27522
  options.disabled,
27462
27523
  pageSurfaceRegistry,
27463
27524
  renderPageThumbnailSurface,
@@ -27579,7 +27640,8 @@ function useDocxPageThumbnails(editor, options = {}) {
27579
27640
  ]);
27580
27641
  const thumbnailGeometryItems = React.useMemo(() => {
27581
27642
  const totalPages = Math.max(1, editor.totalPages);
27582
- return Array.from({ length: totalPages }, (_, pageIndex) => {
27643
+ const geometryPageIndexes = hardThumbnailPageIndexes ?? Array.from({ length: totalPages }, (_, pageIndex) => pageIndex);
27644
+ return geometryPageIndexes.map((pageIndex) => {
27583
27645
  const pageElement = mountedPageElements.get(pageIndex);
27584
27646
  const sourceSize = pageElement && pageElement.isConnected ? resolveDocxViewerPageSurfaceSize(
27585
27647
  pageElement,
@@ -27633,8 +27695,11 @@ function useDocxPageThumbnails(editor, options = {}) {
27633
27695
  return nextCanvasRef;
27634
27696
  })(),
27635
27697
  renderToCanvas: renderToCanvasCallbacksRef.current.get(pageIndex) ?? (() => {
27636
- const nextRenderToCanvas = async (canvas) => {
27637
- await renderPageThumbnailToCanvasRef.current(pageIndex, canvas);
27698
+ const nextRenderToCanvas = async (canvas, renderOptions) => {
27699
+ await renderPageThumbnailToCanvasRef.current(pageIndex, canvas, {
27700
+ force: renderOptions?.force,
27701
+ scheduling: renderOptions?.scheduling
27702
+ });
27638
27703
  };
27639
27704
  renderToCanvasCallbacksRef.current.set(
27640
27705
  pageIndex,
@@ -27660,6 +27725,7 @@ function useDocxPageThumbnails(editor, options = {}) {
27660
27725
  options.maxHeightPx,
27661
27726
  options.maxWidthPx,
27662
27727
  options.pixelRatio,
27728
+ hardThumbnailPageIndexes,
27663
27729
  pageSurfaceRegistry,
27664
27730
  thumbnailQueueKeyForCanvas
27665
27731
  ]);
@@ -27680,7 +27746,9 @@ function useDocxPageThumbnails(editor, options = {}) {
27680
27746
  if (!canvas || options.disabled) {
27681
27747
  return false;
27682
27748
  }
27683
- const thumbnail = thumbnails[pageIndex];
27749
+ const thumbnail = thumbnails.find(
27750
+ (candidate) => candidate.pageIndex === pageIndex
27751
+ );
27684
27752
  if (!thumbnail) {
27685
27753
  return false;
27686
27754
  }
@@ -28818,6 +28886,7 @@ function DocxEditorViewer({
28818
28886
  loadingState,
28819
28887
  pageVirtualization,
28820
28888
  visiblePageRange,
28889
+ pageIndexes,
28821
28890
  onPageCountChange,
28822
28891
  onRequestPageReveal,
28823
28892
  headingStyles,
@@ -28969,16 +29038,16 @@ function DocxEditorViewer({
28969
29038
  const [embeddedTableColumnWidths, setEmbeddedTableColumnWidths] = React.useState({});
28970
29039
  const handleRepeatedFormFieldClick = React.useCallback(
28971
29040
  (location) => {
28972
- const now = typeof performance !== "undefined" ? performance.now() : Date.now();
29041
+ const now2 = typeof performance !== "undefined" ? performance.now() : Date.now();
28973
29042
  const key = formFieldLocationKey(location);
28974
29043
  const previousClick = lastFormFieldClickRef.current;
28975
- if (previousClick && previousClick.key === key && now - previousClick.time <= 350) {
29044
+ if (previousClick && previousClick.key === key && now2 - previousClick.time <= 350) {
28976
29045
  lastFormFieldClickRef.current = void 0;
28977
29046
  editor.selectFormField(location);
28978
29047
  onFormFieldDoubleClick?.(location);
28979
29048
  return true;
28980
29049
  }
28981
- lastFormFieldClickRef.current = { key, time: now };
29050
+ lastFormFieldClickRef.current = { key, time: now2 };
28982
29051
  return false;
28983
29052
  },
28984
29053
  [editor.selectFormField, formFieldLocationKey, onFormFieldDoubleClick]
@@ -30250,6 +30319,13 @@ function DocxEditorViewer({
30250
30319
  };
30251
30320
  }, [editor.model.nodes, pageNodeSegmentsByPage]);
30252
30321
  const pageCount = pageNodeSegmentsByPage.length;
30322
+ const requestedPageIndexes = React.useMemo(
30323
+ () => normalizeDocxThumbnailPageIndexes(pageIndexes, pageCount).sort(
30324
+ (left, right) => left - right
30325
+ ),
30326
+ [pageCount, pageIndexes]
30327
+ );
30328
+ const hasRequestedPageIndexes = pageIndexes !== void 0;
30253
30329
  const hasLargeTableLayoutSurface = React.useMemo(() => {
30254
30330
  let tableParagraphCount = 0;
30255
30331
  let tableCellCount = 0;
@@ -30344,7 +30420,7 @@ function DocxEditorViewer({
30344
30420
  )
30345
30421
  )
30346
30422
  ) : 0;
30347
- const hasExternalVisiblePageRange = Number.isFinite(Number(visiblePageRange?.startPageIndex)) || Number.isFinite(Number(visiblePageRange?.endPageIndex));
30423
+ const hasExternalVisiblePageRange = hasRequestedPageIndexes || Number.isFinite(Number(visiblePageRange?.startPageIndex)) || Number.isFinite(Number(visiblePageRange?.endPageIndex));
30348
30424
  const [deferInternalPageVirtualization, setDeferInternalPageVirtualization] = React.useState(false);
30349
30425
  const [internalVirtualScrollElement, setInternalVirtualScrollElement] = React.useState(null);
30350
30426
  const [virtualizerMeasurementScale, setVirtualizerMeasurementScale] = React.useState(1);
@@ -30927,9 +31003,12 @@ function DocxEditorViewer({
30927
31003
  const initialPremeasurePageLimit = hasLargeTableLayoutSurface ? Math.min(2, INITIAL_PAGINATION_PREMEASURE_PAGE_LIMIT) : INITIAL_PAGINATION_PREMEASURE_PAGE_LIMIT;
30928
31004
  const initialPremeasureEndPageIndex = hasExternalVisiblePageRange ? normalizedVisiblePageRange.endPageIndex : Math.min(pageCount - 1, initialPremeasurePageLimit - 1);
30929
31005
  const measureAllPagesBeforeInitialReveal = hideDocumentUntilPaginationSettled && pageCount > 0 && initialPremeasureStartPageIndex === 0 && initialPremeasureEndPageIndex >= pageCount - 1;
30930
- const visiblePageStartIndex = measureAllPagesBeforeInitialReveal ? 0 : hideDocumentUntilPaginationSettled ? initialPremeasureStartPageIndex : normalizedVisiblePageRange.startPageIndex;
30931
- const visiblePageEndIndex = measureAllPagesBeforeInitialReveal ? pageCount - 1 : hideDocumentUntilPaginationSettled ? initialPremeasureEndPageIndex : normalizedVisiblePageRange.endPageIndex;
31006
+ const visiblePageStartIndex = hasRequestedPageIndexes ? requestedPageIndexes[0] ?? 0 : measureAllPagesBeforeInitialReveal ? 0 : hideDocumentUntilPaginationSettled ? initialPremeasureStartPageIndex : normalizedVisiblePageRange.startPageIndex;
31007
+ const visiblePageEndIndex = hasRequestedPageIndexes ? requestedPageIndexes[requestedPageIndexes.length - 1] ?? -1 : measureAllPagesBeforeInitialReveal ? pageCount - 1 : hideDocumentUntilPaginationSettled ? initialPremeasureEndPageIndex : normalizedVisiblePageRange.endPageIndex;
30932
31008
  const visiblePageIndexes = React.useMemo(() => {
31009
+ if (hasRequestedPageIndexes) {
31010
+ return requestedPageIndexes;
31011
+ }
30933
31012
  if (pageCount <= 0 || visiblePageEndIndex < visiblePageStartIndex) {
30934
31013
  return [];
30935
31014
  }
@@ -30938,7 +31017,13 @@ function DocxEditorViewer({
30938
31017
  indexes.push(pageIndex);
30939
31018
  }
30940
31019
  return indexes;
30941
- }, [pageCount, visiblePageEndIndex, visiblePageStartIndex]);
31020
+ }, [
31021
+ hasRequestedPageIndexes,
31022
+ pageCount,
31023
+ requestedPageIndexes,
31024
+ visiblePageEndIndex,
31025
+ visiblePageStartIndex
31026
+ ]);
30942
31027
  const pageStackVirtualSpacers = React.useMemo(() => {
30943
31028
  const pageWrapperWidthPxForIndex = (pageIndex) => {
30944
31029
  const pageLayout = pageSectionInfoByIndex[pageIndex]?.layout ?? documentLayout;
@@ -31368,9 +31453,14 @@ function DocxEditorViewer({
31368
31453
  ]);
31369
31454
  const isPageVisible = React.useCallback(
31370
31455
  (pageIndex) => {
31371
- return pageIndex >= visiblePageStartIndex && pageIndex <= visiblePageEndIndex;
31456
+ return hasRequestedPageIndexes ? requestedPageIndexes.includes(pageIndex) : pageIndex >= visiblePageStartIndex && pageIndex <= visiblePageEndIndex;
31372
31457
  },
31373
- [visiblePageEndIndex, visiblePageStartIndex]
31458
+ [
31459
+ hasRequestedPageIndexes,
31460
+ requestedPageIndexes,
31461
+ visiblePageEndIndex,
31462
+ visiblePageStartIndex
31463
+ ]
31374
31464
  );
31375
31465
  const requestPageReveal = React.useCallback(
31376
31466
  (targetPageIndex) => {
@@ -47921,6 +48011,223 @@ ${currentText.slice(end)}`;
47921
48011
  );
47922
48012
  }
47923
48013
 
48014
+ // src/parsed-docx.ts
48015
+ function now() {
48016
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
48017
+ }
48018
+ function xmlAttribute3(xml, name) {
48019
+ const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
48020
+ return xml.match(new RegExp(`${escapedName}="([^"]*)"`, "i"))?.[1];
48021
+ }
48022
+ function relationshipPartName(partName) {
48023
+ const segments = partName.split("/");
48024
+ const name = segments.pop() ?? partName;
48025
+ const directory = segments.join("/");
48026
+ return directory ? `${directory}/_rels/${name}.rels` : `_rels/${name}.rels`;
48027
+ }
48028
+ function resolveRelativePartName(basePartName, target) {
48029
+ if (/^[a-z]+:/i.test(target)) {
48030
+ return target;
48031
+ }
48032
+ if (target.startsWith("/")) {
48033
+ return target.replace(/^\/+/, "");
48034
+ }
48035
+ const segments = basePartName.replace(/^\/+/, "").split("/");
48036
+ segments.pop();
48037
+ target.split("/").forEach((segment) => {
48038
+ if (!segment || segment === ".") {
48039
+ return;
48040
+ }
48041
+ if (segment === "..") {
48042
+ segments.pop();
48043
+ return;
48044
+ }
48045
+ segments.push(segment);
48046
+ });
48047
+ return segments.join("/");
48048
+ }
48049
+ function parseRelationships(pkg, partName) {
48050
+ const relationships = /* @__PURE__ */ new Map();
48051
+ const xml = pkg.parts.get(relationshipPartName(partName))?.content;
48052
+ if (!xml) {
48053
+ return relationships;
48054
+ }
48055
+ const pattern = /<Relationship\b[^>]*Id="([^"]+)"[^>]*Target="([^"]+)"[^>]*\/?>/gi;
48056
+ let match;
48057
+ while (match = pattern.exec(xml)) {
48058
+ const id = match[1]?.trim();
48059
+ const target = match[2]?.trim();
48060
+ if (id && target) {
48061
+ relationships.set(id, resolveRelativePartName(partName, target));
48062
+ }
48063
+ }
48064
+ return relationships;
48065
+ }
48066
+ function deobfuscateFontData(data, key) {
48067
+ const output = Uint8Array.from(data);
48068
+ const normalizedKey = (key ?? "").replace(/[{}-]/g, "");
48069
+ if (/^[0-9a-f]{32}$/i.test(normalizedKey)) {
48070
+ const keyBytes = Uint8Array.from(
48071
+ normalizedKey.match(/../g)?.map((pair) => Number.parseInt(pair, 16)) ?? []
48072
+ ).reverse();
48073
+ const length = Math.min(32, output.length);
48074
+ for (let index = 0; index < length; index += 1) {
48075
+ output[index] ^= keyBytes[index % keyBytes.length] ?? 0;
48076
+ }
48077
+ }
48078
+ return output.buffer.slice(
48079
+ output.byteOffset,
48080
+ output.byteOffset + output.byteLength
48081
+ );
48082
+ }
48083
+ function embeddedFontDescriptors(pkg) {
48084
+ const partName = "word/fontTable.xml";
48085
+ const xml = pkg.parts.get(partName)?.content;
48086
+ if (!xml) {
48087
+ return [];
48088
+ }
48089
+ const relationships = parseRelationships(pkg, partName);
48090
+ const descriptors = [];
48091
+ const fontPattern = /<w:font\b[^>]*w:name="([^"]+)"[^>]*>([\s\S]*?)<\/w:font>/gi;
48092
+ const variants = [
48093
+ { tag: "embedRegular", style: "normal", weight: "400" },
48094
+ { tag: "embedBold", style: "normal", weight: "700" },
48095
+ { tag: "embedItalic", style: "italic", weight: "400" },
48096
+ { tag: "embedBoldItalic", style: "italic", weight: "700" }
48097
+ ];
48098
+ let fontMatch;
48099
+ while (fontMatch = fontPattern.exec(xml)) {
48100
+ const family = fontMatch[1]?.trim();
48101
+ const fontXml = fontMatch[2] ?? "";
48102
+ if (!family) {
48103
+ continue;
48104
+ }
48105
+ variants.forEach((variant) => {
48106
+ const tagXml = fontXml.match(new RegExp(`<w:${variant.tag}\\b[^>]*\\/?>`, "i"))?.[0] ?? "";
48107
+ const relationshipId = xmlAttribute3(tagXml, "r:id");
48108
+ const resolvedPartName = relationshipId ? relationships.get(relationshipId) : void 0;
48109
+ const data = resolvedPartName ? pkg.binaryAssets.get(resolvedPartName) : void 0;
48110
+ if (!data) {
48111
+ return;
48112
+ }
48113
+ descriptors.push({
48114
+ family,
48115
+ style: variant.style,
48116
+ weight: variant.weight,
48117
+ source: deobfuscateFontData(data, xmlAttribute3(tagXml, "w:fontKey"))
48118
+ });
48119
+ });
48120
+ }
48121
+ return descriptors;
48122
+ }
48123
+ async function sourceToArrayBuffer(source) {
48124
+ if (source instanceof ArrayBuffer) {
48125
+ return source;
48126
+ }
48127
+ if (ArrayBuffer.isView(source)) {
48128
+ return source.buffer.slice(
48129
+ source.byteOffset,
48130
+ source.byteOffset + source.byteLength
48131
+ );
48132
+ }
48133
+ return source.arrayBuffer();
48134
+ }
48135
+ async function parseDocxForViewer(source, options = {}) {
48136
+ const startedAt = now();
48137
+ const readStartedAt = now();
48138
+ const buffer = await sourceToArrayBuffer(source);
48139
+ const readFinishedAt = now();
48140
+ const importResult = await importDocxBuffer(buffer, {
48141
+ signal: options.signal,
48142
+ transferBuffer: options.transferBuffer,
48143
+ useWorker: options.useWorker
48144
+ });
48145
+ const importFinishedAt = now();
48146
+ let faces = [];
48147
+ let fontLoadingMs = 0;
48148
+ let fontLoadingComplete = false;
48149
+ let fontLoadPromise;
48150
+ const dispose = () => {
48151
+ if (typeof document !== "undefined" && "fonts" in document) {
48152
+ faces.forEach((face) => {
48153
+ try {
48154
+ document.fonts.delete(face);
48155
+ } catch {
48156
+ }
48157
+ });
48158
+ }
48159
+ faces = [];
48160
+ };
48161
+ const loadEmbeddedFonts = () => {
48162
+ if (fontLoadPromise) {
48163
+ return fontLoadPromise;
48164
+ }
48165
+ fontLoadPromise = (async () => {
48166
+ if (typeof document === "undefined" || !("fonts" in document) || typeof FontFace === "undefined") {
48167
+ fontLoadingComplete = true;
48168
+ return;
48169
+ }
48170
+ const fontStartedAt = now();
48171
+ const loaded = (await Promise.all(
48172
+ embeddedFontDescriptors(importResult.package).map(
48173
+ async (descriptor) => {
48174
+ try {
48175
+ const face = new FontFace(
48176
+ descriptor.family,
48177
+ descriptor.source,
48178
+ {
48179
+ style: descriptor.style,
48180
+ weight: descriptor.weight
48181
+ }
48182
+ );
48183
+ await face.load();
48184
+ return face;
48185
+ } catch {
48186
+ return void 0;
48187
+ }
48188
+ }
48189
+ )
48190
+ )).filter((face) => Boolean(face));
48191
+ loaded.forEach((face) => document.fonts.add(face));
48192
+ faces = loaded;
48193
+ try {
48194
+ await document.fonts.ready;
48195
+ } catch {
48196
+ }
48197
+ fontLoadingMs += now() - fontStartedAt;
48198
+ fontLoadingComplete = true;
48199
+ })();
48200
+ return fontLoadPromise;
48201
+ };
48202
+ if (options.loadEmbeddedFonts !== false && options.loadEmbeddedFonts !== "defer") {
48203
+ await loadEmbeddedFonts();
48204
+ }
48205
+ const nonFontTotalMs = importFinishedAt - startedAt;
48206
+ const timings = {
48207
+ fileReadMs: readFinishedAt - readStartedAt,
48208
+ parseMs: importResult.timings?.parseMs ?? importFinishedAt - readFinishedAt,
48209
+ modelConstructionMs: importResult.timings?.buildModelMs ?? 0,
48210
+ get fontLoadingMs() {
48211
+ return fontLoadingMs;
48212
+ },
48213
+ get totalMs() {
48214
+ return nonFontTotalMs + fontLoadingMs;
48215
+ }
48216
+ };
48217
+ return {
48218
+ model: importResult.model,
48219
+ package: importResult.package,
48220
+ source: importResult.source,
48221
+ fileName: options.fileName ?? (typeof File !== "undefined" && source instanceof File ? source.name : void 0),
48222
+ timings,
48223
+ get embeddedFontsLoaded() {
48224
+ return fontLoadingComplete;
48225
+ },
48226
+ loadEmbeddedFonts,
48227
+ dispose
48228
+ };
48229
+ }
48230
+
47924
48231
  // ../layout-core/src/pagination.ts
47925
48232
  var TWIPS_PER_PIXEL2 = 15;
47926
48233
  var PAGE_BREAK_XML_PATTERN3 = /<w:br\b[^>]*w:type="page"[^>]*\/?>/i;
@@ -49701,6 +50008,9 @@ function renderBlock(block) {
49701
50008
  function ReactDocxViewer({
49702
50009
  file,
49703
50010
  model,
50011
+ document: document2,
50012
+ loadEmbeddedFonts = true,
50013
+ pageIndexes,
49704
50014
  className,
49705
50015
  layoutOptions,
49706
50016
  emptyState
@@ -49709,8 +50019,13 @@ function ReactDocxViewer({
49709
50019
  model: parsedModel,
49710
50020
  isLoading,
49711
50021
  error
49712
- } = useDocxModel(model ? void 0 : file);
49713
- const resolvedModel = model ?? parsedModel;
50022
+ } = useDocxModel(model || document2 ? void 0 : file);
50023
+ const resolvedModel = model ?? document2?.model ?? parsedModel;
50024
+ React2.useEffect(() => {
50025
+ if (document2 && loadEmbeddedFonts && !document2.embeddedFontsLoaded) {
50026
+ void document2.loadEmbeddedFonts();
50027
+ }
50028
+ }, [document2, loadEmbeddedFonts]);
49714
50029
  const modelWithSections = React2.useMemo(() => {
49715
50030
  if (!resolvedModel) {
49716
50031
  return void 0;
@@ -49742,6 +50057,12 @@ function ReactDocxViewer({
49742
50057
  }
49743
50058
  return layoutDocument(modelWithSections, resolvedLayoutOptions);
49744
50059
  }, [modelWithSections, resolvedLayoutOptions]);
50060
+ const requestedPageIndexSet = React2.useMemo(
50061
+ () => pageIndexes === void 0 ? void 0 : new Set(
50062
+ pageIndexes.filter((pageIndex) => Number.isFinite(pageIndex)).map((pageIndex) => Math.trunc(pageIndex))
50063
+ ),
50064
+ [pageIndexes]
50065
+ );
49745
50066
  if (isLoading) {
49746
50067
  return /* @__PURE__ */ jsx2("div", { className, children: "Loading DOCX..." });
49747
50068
  }
@@ -49763,29 +50084,288 @@ function ReactDocxViewer({
49763
50084
  className,
49764
50085
  "data-testid": "react-docx-viewer",
49765
50086
  style: containerStyle,
49766
- children: pages.map((page) => /* @__PURE__ */ jsx2(
49767
- "section",
49768
- {
49769
- "data-page": page.number,
49770
- style: {
49771
- width: pageWidth,
49772
- minHeight: pageHeight,
49773
- boxSizing: "border-box",
49774
- padding: pagePadding,
49775
- background: "#fff",
49776
- border: "1px solid #d4d4d4",
49777
- boxShadow: "0 8px 24px rgba(0, 0, 0, 0.08)",
49778
- display: "grid",
49779
- gap: 8,
49780
- alignContent: "start"
50087
+ children: pages.map(
50088
+ (page, pageIndex) => requestedPageIndexSet && !requestedPageIndexSet.has(pageIndex) ? null : /* @__PURE__ */ jsx2(
50089
+ "section",
50090
+ {
50091
+ "data-page": page.number,
50092
+ "data-page-index": pageIndex,
50093
+ style: {
50094
+ width: pageWidth,
50095
+ minHeight: pageHeight,
50096
+ boxSizing: "border-box",
50097
+ padding: pagePadding,
50098
+ background: "#fff",
50099
+ border: "1px solid #d4d4d4",
50100
+ boxShadow: "0 8px 24px rgba(0, 0, 0, 0.08)",
50101
+ display: "grid",
50102
+ gap: 8,
50103
+ alignContent: "start"
50104
+ },
50105
+ children: page.blocks.map(renderBlock)
49781
50106
  },
49782
- children: page.blocks.map(renderBlock)
49783
- },
49784
- page.number
49785
- ))
50107
+ page.number
50108
+ )
50109
+ )
49786
50110
  }
49787
50111
  );
49788
50112
  }
50113
+ function thumbnailNow() {
50114
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
50115
+ }
50116
+ function canvasToBlob(canvas, mimeType, quality) {
50117
+ return new Promise((resolve, reject) => {
50118
+ canvas.toBlob(
50119
+ (blob) => {
50120
+ if (blob) {
50121
+ resolve(blob);
50122
+ } else {
50123
+ reject(new Error("Failed to encode DOCX thumbnail."));
50124
+ }
50125
+ },
50126
+ mimeType,
50127
+ quality
50128
+ );
50129
+ });
50130
+ }
50131
+ function createDocxThumbnailRenderer(documentModel, options = {}) {
50132
+ const model = "model" in documentModel ? documentModel.model : documentModel;
50133
+ const layout = resolveDocumentLayout(model);
50134
+ const layoutOptions = {
50135
+ pageWidth: layout.pageWidthPx,
50136
+ pageHeight: layout.pageHeightPx
50137
+ };
50138
+ const headerNodes = model.metadata.headerSections[0]?.nodes ?? [];
50139
+ const footerNodes = model.metadata.footerSections[0]?.nodes ?? [];
50140
+ const paginationStartedAt = thumbnailNow();
50141
+ const builtPageSegments = buildDocumentPageNodeSegments(
50142
+ model,
50143
+ Math.max(
50144
+ 120,
50145
+ layout.pageHeightPx - layout.marginsPx.top - layout.marginsPx.bottom
50146
+ ),
50147
+ Math.max(
50148
+ 120,
50149
+ layout.pageWidthPx - layout.marginsPx.left - layout.marginsPx.right
50150
+ ),
50151
+ model.metadata.numberingDefinitions
50152
+ );
50153
+ const pageSegments = builtPageSegments.length > 0 ? builtPageSegments : [[]];
50154
+ const paginationMs = thumbnailNow() - paginationStartedAt;
50155
+ const pageLayoutCache = /* @__PURE__ */ new Map();
50156
+ const layoutPage = (pageIndex) => {
50157
+ const cached = pageLayoutCache.get(pageIndex);
50158
+ if (cached) {
50159
+ return { page: cached, paginationMs: 0 };
50160
+ }
50161
+ const startedAt = thumbnailNow();
50162
+ const nodeIndexes = new Set(
50163
+ (pageSegments[pageIndex] ?? []).map((segment) => segment.nodeIndex)
50164
+ );
50165
+ const pageModel = {
50166
+ ...model,
50167
+ nodes: [
50168
+ ...headerNodes,
50169
+ ...model.nodes.filter(
50170
+ (_, nodeIndex) => nodeIndexes.has(nodeIndex)
50171
+ ),
50172
+ ...footerNodes
50173
+ ]
50174
+ };
50175
+ const page = layoutDocument(pageModel, layoutOptions)[0];
50176
+ if (!page) {
50177
+ throw new RangeError(`DOCX page index ${pageIndex} is unavailable.`);
50178
+ }
50179
+ pageLayoutCache.set(pageIndex, page);
50180
+ return { page, paginationMs: thumbnailNow() - startedAt };
50181
+ };
50182
+ const allowedPageIndexes = options.pageIndexes === void 0 ? void 0 : new Set(
50183
+ options.pageIndexes.filter((pageIndex) => Number.isFinite(pageIndex)).map((pageIndex) => Math.trunc(pageIndex))
50184
+ );
50185
+ const queue = new SerialIdleTaskQueue({ minTaskIntervalMs: 0 });
50186
+ let host;
50187
+ let root;
50188
+ let rootPromise;
50189
+ let requestId = 0;
50190
+ let disposed = false;
50191
+ const ensureRoot = async () => {
50192
+ if (disposed) {
50193
+ throw new Error("DOCX thumbnail renderer has been disposed.");
50194
+ }
50195
+ if (root) {
50196
+ return root;
50197
+ }
50198
+ if (!rootPromise) {
50199
+ rootPromise = (async () => {
50200
+ if (typeof document === "undefined") {
50201
+ throw new Error("DOCX thumbnail rendering requires a browser DOM.");
50202
+ }
50203
+ const nextHost = document.createElement("div");
50204
+ nextHost.setAttribute("data-docx-thumbnail-renderer", "true");
50205
+ Object.assign(nextHost.style, {
50206
+ position: "fixed",
50207
+ left: "-100000px",
50208
+ top: "0",
50209
+ overflow: "visible",
50210
+ pointerEvents: "none"
50211
+ });
50212
+ document.body.appendChild(nextHost);
50213
+ host = nextHost;
50214
+ const { createRoot } = await import("react-dom/client");
50215
+ const nextRoot = createRoot(nextHost);
50216
+ if (disposed) {
50217
+ nextRoot.unmount();
50218
+ nextHost.remove();
50219
+ throw new Error("DOCX thumbnail renderer has been disposed.");
50220
+ }
50221
+ root = nextRoot;
50222
+ return root;
50223
+ })();
50224
+ }
50225
+ return rootPromise;
50226
+ };
50227
+ const renderImmediately = async (pageIndex, renderOptions) => {
50228
+ const startedAt = thumbnailNow();
50229
+ if (!Number.isInteger(pageIndex) || pageIndex < 0 || pageIndex >= pageSegments.length) {
50230
+ throw new RangeError(`DOCX page index ${pageIndex} is out of range.`);
50231
+ }
50232
+ if (allowedPageIndexes && !allowedPageIndexes.has(pageIndex)) {
50233
+ throw new RangeError(`DOCX page index ${pageIndex} was not requested.`);
50234
+ }
50235
+ const resolvedPage = layoutPage(pageIndex);
50236
+ const renderPaginationMs = paginationMs + resolvedPage.paginationMs;
50237
+ const pageMountStartedAt = thumbnailNow();
50238
+ const activeRoot = await ensureRoot();
50239
+ flushSync2(() => {
50240
+ activeRoot.render(
50241
+ /* @__PURE__ */ jsx2(
50242
+ "section",
50243
+ {
50244
+ "data-docx-thumbnail-page-index": pageIndex,
50245
+ style: {
50246
+ width: layout.pageWidthPx,
50247
+ minHeight: layout.pageHeightPx,
50248
+ boxSizing: "border-box",
50249
+ padding: `${layout.marginsPx.top}px ${layout.marginsPx.right}px ${layout.marginsPx.bottom}px ${layout.marginsPx.left}px`,
50250
+ background: "#fff",
50251
+ display: "grid",
50252
+ gap: 8,
50253
+ alignContent: "start"
50254
+ },
50255
+ children: resolvedPage.page.blocks.map(renderBlock)
50256
+ }
50257
+ )
50258
+ );
50259
+ });
50260
+ const pageElement = host?.querySelector(
50261
+ `[data-docx-thumbnail-page-index="${pageIndex}"]`
50262
+ );
50263
+ if (!pageElement) {
50264
+ throw new Error("Failed to mount the requested DOCX page.");
50265
+ }
50266
+ const pageMountMs = thumbnailNow() - pageMountStartedAt;
50267
+ const resolution = resolveDocxPageThumbnailResolution({
50268
+ sourceWidthPx: layout.pageWidthPx,
50269
+ sourceHeightPx: layout.pageHeightPx,
50270
+ resolution: renderOptions.resolution ?? options.resolution,
50271
+ maxWidthPx: renderOptions.maxWidth,
50272
+ maxHeightPx: renderOptions.maxHeight,
50273
+ pixelRatio: renderOptions.pixelRatio ?? options.pixelRatio
50274
+ });
50275
+ const rasterStartedAt = thumbnailNow();
50276
+ const surface = await rasterizeDocxThumbnailSurface({
50277
+ pageElement,
50278
+ sourceWidthPx: layout.pageWidthPx,
50279
+ sourceHeightPx: layout.pageHeightPx,
50280
+ widthPx: resolution.widthPx,
50281
+ heightPx: resolution.heightPx,
50282
+ pixelWidthPx: resolution.pixelWidthPx,
50283
+ pixelHeightPx: resolution.pixelHeightPx
50284
+ });
50285
+ const rasterizationMs = thumbnailNow() - rasterStartedAt;
50286
+ const output = renderOptions.output ?? "canvas";
50287
+ const outputCanvas = renderOptions.canvas ?? surface;
50288
+ if (outputCanvas !== surface) {
50289
+ blitDocxThumbnailSurface(surface, outputCanvas, resolution);
50290
+ }
50291
+ const encodingStartedAt = thumbnailNow();
50292
+ let blob;
50293
+ let imageBitmap;
50294
+ if (output === "blob") {
50295
+ blob = await canvasToBlob(
50296
+ outputCanvas,
50297
+ renderOptions.mimeType ?? "image/png",
50298
+ renderOptions.quality
50299
+ );
50300
+ } else if (output === "imageBitmap") {
50301
+ if (typeof createImageBitmap !== "function") {
50302
+ throw new Error("ImageBitmap output is not supported in this browser.");
50303
+ }
50304
+ imageBitmap = await createImageBitmap(outputCanvas);
50305
+ }
50306
+ const encodingMs = thumbnailNow() - encodingStartedAt;
50307
+ return {
50308
+ pageIndex,
50309
+ pageCount: pageSegments.length,
50310
+ width: resolution.widthPx,
50311
+ height: resolution.heightPx,
50312
+ pixelWidth: resolution.pixelWidthPx,
50313
+ pixelHeight: resolution.pixelHeightPx,
50314
+ output,
50315
+ canvas: output === "canvas" ? outputCanvas : void 0,
50316
+ blob,
50317
+ imageBitmap,
50318
+ timings: {
50319
+ paginationMs: renderPaginationMs,
50320
+ pageMountMs,
50321
+ rasterizationMs,
50322
+ encodingMs,
50323
+ totalMs: paginationMs + thumbnailNow() - startedAt
50324
+ }
50325
+ };
50326
+ };
50327
+ return {
50328
+ document: documentModel,
50329
+ pageCount: pageSegments.length,
50330
+ renderPage(pageIndex, renderOptions = {}) {
50331
+ const scheduling = renderOptions.scheduling ?? options.scheduling ?? "idle";
50332
+ if (scheduling === "immediate") {
50333
+ return renderImmediately(pageIndex, renderOptions);
50334
+ }
50335
+ requestId += 1;
50336
+ let result;
50337
+ let failure;
50338
+ return queue.enqueue(
50339
+ `page:${pageIndex}:${requestId}`,
50340
+ async () => {
50341
+ try {
50342
+ result = await renderImmediately(pageIndex, renderOptions);
50343
+ } catch (error) {
50344
+ failure = error;
50345
+ }
50346
+ },
50347
+ { priority: 0 }
50348
+ ).then(() => {
50349
+ if (failure) {
50350
+ throw failure;
50351
+ }
50352
+ if (!result) {
50353
+ throw new Error("DOCX thumbnail request was cancelled.");
50354
+ }
50355
+ return result;
50356
+ });
50357
+ },
50358
+ dispose() {
50359
+ disposed = true;
50360
+ queue.clear();
50361
+ root?.unmount();
50362
+ root = void 0;
50363
+ rootPromise = void 0;
50364
+ host?.remove();
50365
+ host = void 0;
50366
+ }
50367
+ };
50368
+ }
49789
50369
  export {
49790
50370
  DEFAULT_MIN_PARAGRAPH_LINE_HEIGHT_PX,
49791
50371
  DEFAULT_PAGE_OVERFLOW_TOLERANCE_PX,
@@ -49809,6 +50389,7 @@ export {
49809
50389
  collectTableExplicitPageBreakInfo2 as collectTableExplicitPageBreakInfo,
49810
50390
  collectTopLevelExplicitPageBreakStartNodeIndexes2 as collectTopLevelExplicitPageBreakStartNodeIndexes,
49811
50391
  copyParagraphs,
50392
+ createDocxThumbnailRenderer,
49812
50393
  createMinimalDocxPackage,
49813
50394
  createParagraphComment,
49814
50395
  deepFreezeDocModel,
@@ -49831,6 +50412,7 @@ export {
49831
50412
  paragraphLetterheadFloatSideAtNodeIndex,
49832
50413
  paragraphStartsWithLastRenderedPageBreak2 as paragraphStartsWithLastRenderedPageBreak,
49833
50414
  parseDocx,
50415
+ parseDocxForViewer,
49834
50416
  parseParagraphsFromClipboard,
49835
50417
  parseSectionLayout,
49836
50418
  pasteParagraphs,