@office-open/docx 0.9.4 → 0.9.6

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.
@@ -1,5 +1,5 @@
1
1
  import { TargetModeType, ThemeColor, convertEmuToPixels, convertPixelsToEmu, convertToEmu, convertToTwip, convertUniversalMeasureToEmu, decimalNumber, eighthPointMeasureValue, hexColorValue, hpsMeasureValue, measurementOrPercentValue, pointMeasureValue, signedTwipsMeasureValue, toUint8Array, twipsMeasureValue, uCharHexNumber, uniqueId, uniqueNumericIdCreator, xsdVerticalMergeRev } from "@office-open/core";
2
- import { attr, attrBool, attrNum, children, element, escapeXml, findChild, findDeep, textOf } from "@office-open/xml";
2
+ import { attr, attrBool, attrNum, children, colorAttr, element, escapeXml, findChild, findDeep, textOf } from "@office-open/xml";
3
3
  import { calculateEffectExtent, createEffectDag, createScene3D, createShape3D, customGeometryDesc, effectListDesc, extractBlipFillMedia, fillDesc, outlineDesc, scene3DDesc, shape3DDesc, transform2DDesc } from "@office-open/core/drawingml";
4
4
  import { chartSpaceDesc } from "@office-open/core/chart";
5
5
  import { createDataModel } from "@office-open/core/smartart";
@@ -632,207 +632,1489 @@ const WidthType = {
632
632
  PERCENTAGE: "pct"
633
633
  };
634
634
  //#endregion
635
- //#region src/parts/drawing/text-wrap/text-wrapping.ts
635
+ //#region src/parts/paragraph/run/run-parse.ts
636
636
  /**
637
- * Enumeration of text wrapping types for floating drawings.
637
+ * Run properties parser for DOCX documents.
638
638
  *
639
- * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
639
+ * Parses w:rPr Element trees into RunPropertiesOptions objects.
640
640
  *
641
- * @publicApi
641
+ * @module
642
642
  */
643
- const TextWrappingType = {
644
- NONE: 0,
645
- SQUARE: 1,
646
- TIGHT: 2,
647
- TOP_AND_BOTTOM: 3,
648
- THROUGH: 4
649
- };
650
643
  /**
651
- * Enumeration of text wrapping sides for floating drawings.
652
- *
653
- * Specifies on which side(s) text can wrap around the drawing.
654
- *
655
- * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
656
- *
657
- * @publicApi
644
+ * Parse a w:rPr element into RunPropertiesOptions.
658
645
  */
659
- const TextWrappingSide = {
660
- /** Text wraps on both sides of the drawing */
661
- BOTH_SIDES: "bothSides",
662
- /** Text wraps only on the left side */
663
- LEFT: "left",
664
- /** Text wraps only on the right side */
665
- RIGHT: "right",
666
- /** Text wraps on the side with more space */
667
- LARGEST: "largest"
668
- };
669
- //#endregion
670
- //#region src/parts/drawing/text-wrap/wrap-tight.ts
646
+ function parseRunProperties(el) {
647
+ const opts = {};
648
+ const rStyle = findChild(el, "w:rStyle");
649
+ if (rStyle) opts.style = attr(rStyle, "w:val");
650
+ const font = findChild(el, "w:rFonts");
651
+ if (font) {
652
+ const ascii = attr(font, "w:ascii");
653
+ const eastAsia = attr(font, "w:eastAsia");
654
+ const hAnsi = attr(font, "w:hAnsi");
655
+ const cs = attr(font, "w:cs");
656
+ const hint = attr(font, "w:hint");
657
+ if (ascii && !eastAsia && !hAnsi && !cs) opts.font = hint ? {
658
+ name: ascii,
659
+ hint
660
+ } : ascii;
661
+ else {
662
+ const fontObj = {};
663
+ if (ascii) fontObj.ascii = ascii;
664
+ if (eastAsia) fontObj.eastAsia = eastAsia;
665
+ if (hAnsi) fontObj.hAnsi = hAnsi;
666
+ if (cs) fontObj.cs = cs;
667
+ if (hint) fontObj.hint = hint;
668
+ opts.font = fontObj;
669
+ }
670
+ }
671
+ const bold = findChild(el, "w:b");
672
+ if (bold) opts.bold = attrBool(bold, "w:val") ?? true;
673
+ const boldCs = findChild(el, "w:bCs");
674
+ if (boldCs) opts.boldComplexScript = attrBool(boldCs, "w:val") ?? true;
675
+ const italic = findChild(el, "w:i");
676
+ if (italic) opts.italic = attrBool(italic, "w:val") ?? true;
677
+ const italicCs = findChild(el, "w:iCs");
678
+ if (italicCs) opts.italicComplexScript = attrBool(italicCs, "w:val") ?? true;
679
+ const underline = findChild(el, "w:u");
680
+ if (underline) {
681
+ const ul = {};
682
+ const uType = attr(underline, "w:val");
683
+ if (uType) ul.type = uType;
684
+ const uColor = colorAttr(underline, "w:color");
685
+ if (uColor) ul.color = uColor;
686
+ opts.underline = ul;
687
+ }
688
+ for (const [name, optKey] of [
689
+ ["w:strike", "strike"],
690
+ ["w:dstrike", "doubleStrike"],
691
+ ["w:outline", "outline"],
692
+ ["w:shadow", "shadow"],
693
+ ["w:emboss", "emboss"],
694
+ ["w:imprint", "imprint"],
695
+ ["w:vanish", "vanish"],
696
+ ["w:webHidden", "webHidden"],
697
+ ["w:noProof", "noProof"],
698
+ ["w:snapToGrid", "snapToGrid"],
699
+ ["w:smallCaps", "smallCaps"],
700
+ ["w:caps", "allCaps"],
701
+ ["w:rtl", "rightToLeft"],
702
+ ["w:cs", "complexScript"],
703
+ ["w:specVanish", "specVanish"],
704
+ ["w:oMath", "math"]
705
+ ]) {
706
+ const child = findChild(el, name);
707
+ if (child) opts[optKey] = attrBool(child, "w:val") ?? true;
708
+ }
709
+ const color = findChild(el, "w:color");
710
+ if (color) {
711
+ const c = colorAttr(color, "w:val");
712
+ const themeColor = attr(color, "w:themeColor");
713
+ const themeTint = attr(color, "w:themeTint");
714
+ const themeShade = attr(color, "w:themeShade");
715
+ if (themeColor || themeTint || themeShade) {
716
+ const colorObj = {};
717
+ if (c) colorObj.val = c;
718
+ if (themeColor) colorObj.themeColor = themeColor;
719
+ if (themeTint) colorObj.themeTint = themeTint;
720
+ if (themeShade) colorObj.themeShade = themeShade;
721
+ opts.color = colorObj;
722
+ } else if (c) opts.color = c;
723
+ }
724
+ const sz = findChild(el, "w:sz");
725
+ if (sz) {
726
+ const halfPts = attrNum(sz, "w:val");
727
+ if (halfPts !== void 0) opts.size = halfPts / 2;
728
+ }
729
+ const szCs = findChild(el, "w:szCs");
730
+ if (szCs) {
731
+ const halfPts = attrNum(szCs, "w:val");
732
+ if (halfPts !== void 0) opts.sizeComplexScript = halfPts / 2;
733
+ }
734
+ const highlight = findChild(el, "w:highlight");
735
+ if (highlight) {
736
+ const val = attr(highlight, "w:val");
737
+ if (val) opts.highlight = val;
738
+ }
739
+ const highlightCs = findChild(el, "w:highlightCs");
740
+ if (highlightCs) {
741
+ const val = attr(highlightCs, "w:val");
742
+ if (val) opts.highlightComplexScript = val;
743
+ }
744
+ const vertAlign = findChild(el, "w:vertAlign");
745
+ if (vertAlign) {
746
+ const val = attr(vertAlign, "w:val");
747
+ if (val === "subscript") opts.subScript = true;
748
+ else if (val === "superscript") opts.superScript = true;
749
+ }
750
+ const effect = findChild(el, "w:effect");
751
+ if (effect) {
752
+ const val = attr(effect, "w:val");
753
+ if (val) opts.effect = val;
754
+ }
755
+ const emphasisMark = findChild(el, "w:em");
756
+ if (emphasisMark) {
757
+ const val = attr(emphasisMark, "w:val");
758
+ if (val) opts.emphasisMark = { type: val };
759
+ }
760
+ const spacing = findChild(el, "w:spacing");
761
+ if (spacing) {
762
+ const val = attrNum(spacing, "w:val");
763
+ if (val !== void 0) opts.characterSpacing = val;
764
+ }
765
+ const scale = findChild(el, "w:w");
766
+ if (scale) {
767
+ const val = attrNum(scale, "w:val");
768
+ if (val !== void 0) opts.scale = val;
769
+ }
770
+ const kern = findChild(el, "w:kern");
771
+ if (kern) {
772
+ const val = attrNum(kern, "w:val");
773
+ if (val !== void 0) opts.kern = val;
774
+ }
775
+ const position = findChild(el, "w:position");
776
+ if (position) {
777
+ const val = attr(position, "w:val");
778
+ if (val !== void 0) opts.position = val;
779
+ }
780
+ const fitText = findChild(el, "w:fitText");
781
+ if (fitText) {
782
+ const val = attrNum(fitText, "w:val");
783
+ if (val !== void 0) opts.fitText = val;
784
+ }
785
+ const lang = findChild(el, "w:lang");
786
+ if (lang) {
787
+ const langObj = {};
788
+ const val = attr(lang, "w:val");
789
+ if (val) langObj.value = val;
790
+ const eastAsia = attr(lang, "w:eastAsia");
791
+ if (eastAsia) langObj.eastAsia = eastAsia;
792
+ const bidi = attr(lang, "w:bidi");
793
+ if (bidi) langObj.bidirectional = bidi;
794
+ if (Object.keys(langObj).length > 0) opts.language = langObj;
795
+ }
796
+ const bdr = findChild(el, "w:bdr");
797
+ if (bdr) opts.border = parseBorder(bdr);
798
+ const shd = findChild(el, "w:shd");
799
+ if (shd) opts.shading = parseShading$1(shd);
800
+ const eastAsianLayout = findChild(el, "w:eastAsianLayout");
801
+ if (eastAsianLayout) opts.eastAsianLayout = parseEastAsianLayout(eastAsianLayout);
802
+ const contentPart = findChild(el, "w:contentPart");
803
+ if (contentPart) {
804
+ const rId = attr(contentPart, "r:id");
805
+ if (rId) opts.contentPartRId = rId;
806
+ }
807
+ const rPrChange = findChild(el, "w:rPrChange");
808
+ if (rPrChange) {
809
+ const rev = {};
810
+ const author = attr(rPrChange, "w:author");
811
+ if (author) rev.author = author;
812
+ const date = attr(rPrChange, "w:date");
813
+ if (date) rev.date = date;
814
+ const id = attrNum(rPrChange, "w:id");
815
+ if (id !== void 0) rev.id = id;
816
+ const innerRPr = findChild(rPrChange, "w:rPr");
817
+ if (innerRPr) Object.assign(rev, parseRunProperties(innerRPr));
818
+ if (Object.keys(rev).length > 0) opts.revision = rev;
819
+ }
820
+ return opts;
821
+ }
671
822
  /**
672
- * Wrap Tight module for DrawingML text wrapping.
673
- *
674
- * This module provides tight text wrapping for floating drawings
675
- * where text wraps closely around the image shape.
676
- *
677
- * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
678
- *
679
- * @module
823
+ * Parse a w:bdr element into BorderOptions.
680
824
  */
825
+ function parseBorder(el) {
826
+ const opts = {};
827
+ const style = attr(el, "w:val");
828
+ if (style) opts.style = style;
829
+ const color = colorAttr(el, "w:color");
830
+ if (color) opts.color = color;
831
+ const size = attrNum(el, "w:sz");
832
+ if (size !== void 0) opts.size = size;
833
+ const space = attrNum(el, "w:space");
834
+ if (space !== void 0) opts.space = space;
835
+ const shadow = attrBool(el, "w:shadow");
836
+ if (shadow !== void 0) opts.shadow = shadow;
837
+ const frame = attrBool(el, "w:frame");
838
+ if (frame !== void 0) opts.frame = frame;
839
+ return opts;
840
+ }
681
841
  /**
682
- * Creates a default rectangular wrap polygon matching the image extent.
683
- *
684
- * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
685
- *
686
- * ## XSD Schema
687
- * ```xml
688
- * <xsd:complexType name="CT_WrapPath">
689
- * <xsd:sequence>
690
- * <xsd:element name="start" type="a:CT_Point2D" minOccurs="1" maxOccurs="1"/>
691
- * <xsd:element name="lineTo" type="a:CT_Point2D" minOccurs="2" maxOccurs="unbounded"/>
692
- * </xsd:sequence>
693
- * <xsd:attribute name="edited" type="xsd:boolean" use="optional"/>
694
- * </xsd:complexType>
695
- * ```
842
+ * Parse a w:shd element into ShadingAttributesProperties.
696
843
  */
697
- const createWrapPolygon$1 = (cx, cy) => element("wp:wrapPolygon", { edited: "0" }, [
698
- `<wp:start x="0" y="0"/>`,
699
- `<wp:lineTo x="0" y="${-cy}"/>`,
700
- `<wp:lineTo x="${cx}" y="${-cy}"/>`,
701
- `<wp:lineTo x="${cx}" y="0"/>`,
702
- `<wp:lineTo x="0" y="0"/>`
703
- ]);
844
+ function parseShading$1(el) {
845
+ const opts = {};
846
+ const fill = colorAttr(el, "w:fill");
847
+ if (fill) opts.fill = fill;
848
+ const color = colorAttr(el, "w:color");
849
+ if (color) opts.color = color;
850
+ const type = attr(el, "w:val");
851
+ if (type) opts.type = type;
852
+ return opts;
853
+ }
704
854
  /**
705
- * Creates tight text wrapping for a floating drawing.
706
- *
707
- * WrapTight causes text to wrap closely around the contours
708
- * of the drawing rather than its rectangular bounding box.
709
- * A default rectangular wrap polygon matching the image extent is generated.
710
- *
711
- * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
712
- *
713
- * ## XSD Schema
714
- * ```xml
715
- * <xsd:complexType name="CT_WrapTight">
716
- * <xsd:sequence>
717
- * <xsd:element name="wrapPolygon" type="CT_WrapPath" minOccurs="1" maxOccurs="1"/>
718
- * </xsd:sequence>
719
- * <xsd:attribute name="wrapText" type="ST_WrapText" use="required"/>
720
- * <xsd:attribute name="distL" type="ST_WrapDistance"/>
721
- * <xsd:attribute name="distR" type="ST_WrapDistance"/>
722
- * </xsd:complexType>
723
- * ```
855
+ * Parse a w:eastAsianLayout element into EastAsianLayoutOptions.
724
856
  */
725
- const createWrapTight = (textWrapping, margins = {
726
- bottom: 0,
727
- left: 0,
728
- right: 0,
729
- top: 0
730
- }, extent) => element("wp:wrapTight", {
731
- distL: margins.left,
732
- distR: margins.right,
733
- wrapText: textWrapping.side || TextWrappingSide.BOTH_SIDES
734
- }, [createWrapPolygon$1(extent.x, extent.y)]);
735
- //#endregion
736
- //#region src/parts/drawing/text-wrap/wrap-through.ts
857
+ function parseEastAsianLayout(el) {
858
+ const opts = {};
859
+ const id = attrNum(el, "w:id");
860
+ if (id !== void 0) opts.id = id;
861
+ const combine = attrBool(el, "w:combine");
862
+ if (combine !== void 0) opts.combine = combine;
863
+ const combineBrackets = attr(el, "w:combineBrackets");
864
+ if (combineBrackets) opts.combineBrackets = combineBrackets;
865
+ const vert = attrBool(el, "w:vert");
866
+ if (vert !== void 0) opts.vert = vert;
867
+ const vertCompress = attrBool(el, "w:vertCompress");
868
+ if (vertCompress !== void 0) opts.vertCompress = vertCompress;
869
+ return opts;
870
+ }
871
+ /** Matches w:br[@w:type="page"] → PageBreak */
872
+ const PARSED_PAGE_BREAK = Symbol("PageBreak");
873
+ /** Matches w:br (line break) */
874
+ const PARSED_LINE_BREAK = Symbol("LineBreak");
875
+ /** Matches w:tab */
876
+ const PARSED_TAB = Symbol("Tab");
877
+ /** Matches w:cr */
878
+ const PARSED_CR = Symbol("CarriageReturn");
879
+ /** Matches w:noBreakHyphen */
880
+ const PARSED_NO_BREAK_HYPHEN = Symbol("NoBreakHyphen");
881
+ /** Matches w:softHyphen */
882
+ const PARSED_SOFT_HYPHEN = Symbol("SoftHyphen");
883
+ /** Matches w:footnoteRef — auto-generated by Footnote class */
884
+ const PARSED_FOOTNOTE_REF = Symbol("FootnoteRef");
885
+ /** Matches w:br[@w:type="column"] */
886
+ const PARSED_COLUMN_BREAK = Symbol("ColumnBreak");
887
+ /** Matches w:dayShort */
888
+ const PARSED_DAY_SHORT = Symbol("DayShort");
889
+ /** Matches w:monthShort */
890
+ const PARSED_MONTH_SHORT = Symbol("MonthShort");
891
+ /** Matches w:yearShort */
892
+ const PARSED_YEAR_SHORT = Symbol("YearShort");
893
+ /** Matches w:dayLong */
894
+ const PARSED_DAY_LONG = Symbol("DayLong");
895
+ /** Matches w:monthLong */
896
+ const PARSED_MONTH_LONG = Symbol("MonthLong");
897
+ /** Matches w:yearLong */
898
+ const PARSED_YEAR_LONG = Symbol("YearLong");
899
+ /** Matches w:annotationRef */
900
+ const PARSED_ANNOTATION_REF = Symbol("AnnotationRef");
901
+ /** Matches w:separator */
902
+ const PARSED_SEPARATOR = Symbol("Separator");
903
+ /** Matches w:continuationSeparator */
904
+ const PARSED_CONTINUATION_SEPARATOR = Symbol("ContinuationSeparator");
905
+ /** Matches w:pgNum */
906
+ const PARSED_PG_NUM = Symbol("PgNum");
907
+ /** Matches w:lastRenderedPageBreak */
908
+ const PARSED_LAST_RENDERED_PAGE_BREAK = Symbol("LastRenderedPageBreak");
737
909
  /**
738
- * Wrap Through module for DrawingML text wrapping.
739
- *
740
- * This module provides "through" text wrapping for floating drawings
741
- * where text wraps through the image contours, filling any concave areas.
742
- *
743
- * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
744
- *
745
- * @module
910
+ * Parse a w:r element into run data.
911
+ * Returns { properties, children } where children are parsed run content items.
746
912
  */
913
+ function parseRun(el, _ctx) {
914
+ const rPr = findChild(el, "w:rPr");
915
+ const properties = rPr ? parseRunProperties(rPr) : void 0;
916
+ const children = [];
917
+ for (const child of el.elements ?? []) switch (child.name) {
918
+ case "w:rPr": break;
919
+ case "w:t": {
920
+ const preserveSpace = attrBool(child, "xml:space");
921
+ let text = textOf(child);
922
+ if (preserveSpace && text) {}
923
+ children.push(text);
924
+ break;
925
+ }
926
+ case "w:delText": {
927
+ const text = textOf(child);
928
+ if (text) children.push(text);
929
+ break;
930
+ }
931
+ case "w:br": {
932
+ const brType = attr(child, "w:type");
933
+ if (brType === "page") children.push(PARSED_PAGE_BREAK);
934
+ else if (brType === "column") children.push(PARSED_COLUMN_BREAK);
935
+ else children.push(PARSED_LINE_BREAK);
936
+ break;
937
+ }
938
+ case "w:tab":
939
+ children.push(PARSED_TAB);
940
+ break;
941
+ case "w:cr":
942
+ children.push(PARSED_CR);
943
+ break;
944
+ case "w:noBreakHyphen":
945
+ children.push(PARSED_NO_BREAK_HYPHEN);
946
+ break;
947
+ case "w:softHyphen":
948
+ children.push(PARSED_SOFT_HYPHEN);
949
+ break;
950
+ case "w:commentReference": {
951
+ const id = attrNum(child, "w:id");
952
+ if (id !== void 0) children.push({ commentReference: id });
953
+ break;
954
+ }
955
+ case "w:drawing":
956
+ case "w:pict": break;
957
+ case "w:sym": {
958
+ const charVal = attr(child, "w:char");
959
+ const fontVal = attr(child, "w:font");
960
+ if (charVal) children.push({ symbolRun: {
961
+ char: charVal,
962
+ symbolfont: fontVal ?? "Wingdings"
963
+ } });
964
+ break;
965
+ }
966
+ case "w:footnoteReference": {
967
+ const id = attrNum(child, "w:id");
968
+ if (id !== void 0) children.push({ footnoteReference: id });
969
+ break;
970
+ }
971
+ case "w:endnoteReference": {
972
+ const id = attrNum(child, "w:id");
973
+ if (id !== void 0) children.push({ endnoteReference: id });
974
+ break;
975
+ }
976
+ case "w:footnoteRef":
977
+ case "w:endnoteRef":
978
+ children.push(PARSED_FOOTNOTE_REF);
979
+ break;
980
+ case "w:dayShort":
981
+ children.push(PARSED_DAY_SHORT);
982
+ break;
983
+ case "w:monthShort":
984
+ children.push(PARSED_MONTH_SHORT);
985
+ break;
986
+ case "w:yearShort":
987
+ children.push(PARSED_YEAR_SHORT);
988
+ break;
989
+ case "w:dayLong":
990
+ children.push(PARSED_DAY_LONG);
991
+ break;
992
+ case "w:monthLong":
993
+ children.push(PARSED_MONTH_LONG);
994
+ break;
995
+ case "w:yearLong":
996
+ children.push(PARSED_YEAR_LONG);
997
+ break;
998
+ case "w:annotationRef":
999
+ children.push(PARSED_ANNOTATION_REF);
1000
+ break;
1001
+ case "w:separator":
1002
+ children.push(PARSED_SEPARATOR);
1003
+ break;
1004
+ case "w:continuationSeparator":
1005
+ children.push(PARSED_CONTINUATION_SEPARATOR);
1006
+ break;
1007
+ case "w:pgNum":
1008
+ children.push(PARSED_PG_NUM);
1009
+ break;
1010
+ case "w:lastRenderedPageBreak":
1011
+ children.push(PARSED_LAST_RENDERED_PAGE_BREAK);
1012
+ break;
1013
+ default: break;
1014
+ }
1015
+ return {
1016
+ properties,
1017
+ children
1018
+ };
1019
+ }
747
1020
  /**
748
- * Creates a default rectangular wrap polygon matching the image extent.
749
- *
750
- * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
1021
+ * Convert parsed run data into an RunOptions suitable for the Document constructor.
1022
+ * Simplifies the parsed children into text + break format.
1023
+ * If the run contains only a commentReference, returns { commentReference: id } instead.
1024
+ * If the run only contains footnoteRef/endnoteRef (auto-generated), returns empty options.
751
1025
  *
752
- * ## XSD Schema
753
- * ```xml
754
- * <xsd:complexType name="CT_WrapPath">
755
- * <xsd:sequence>
756
- * <xsd:element name="start" type="a:CT_Point2D" minOccurs="1" maxOccurs="1"/>
757
- * <xsd:element name="lineTo" type="a:CT_Point2D" minOccurs="2" maxOccurs="unbounded"/>
758
- * </xsd:sequence>
759
- * <xsd:attribute name="edited" type="xsd:boolean" use="optional"/>
760
- * </xsd:complexType>
761
- * ```
1026
+ * When empty run elements (tab, noBreakHyphen, date fields, etc.) are present,
1027
+ * uses children[] format to preserve them for round-trip fidelity.
762
1028
  */
763
- const createWrapPolygon = (cx, cy) => element("wp:wrapPolygon", { edited: "0" }, [
764
- `<wp:start x="0" y="0"/>`,
765
- `<wp:lineTo x="0" y="${-cy}"/>`,
766
- `<wp:lineTo x="${cx}" y="${-cy}"/>`,
767
- `<wp:lineTo x="${cx}" y="0"/>`,
768
- `<wp:lineTo x="0" y="0"/>`
1029
+ /** Mapping from parse symbols to RunOptions child objects for empty elements. */
1030
+ const SYMBOL_TO_CHILD = new Map([
1031
+ [PARSED_TAB, { tab: true }],
1032
+ [PARSED_CR, { carriageReturn: true }],
1033
+ [PARSED_NO_BREAK_HYPHEN, { noBreakHyphen: true }],
1034
+ [PARSED_SOFT_HYPHEN, { softHyphen: true }],
1035
+ [PARSED_DAY_SHORT, { dayShort: true }],
1036
+ [PARSED_MONTH_SHORT, { monthShort: true }],
1037
+ [PARSED_YEAR_SHORT, { yearShort: true }],
1038
+ [PARSED_DAY_LONG, { dayLong: true }],
1039
+ [PARSED_MONTH_LONG, { monthLong: true }],
1040
+ [PARSED_YEAR_LONG, { yearLong: true }],
1041
+ [PARSED_ANNOTATION_REF, { annotationRef: true }],
1042
+ [PARSED_SEPARATOR, { separator: true }],
1043
+ [PARSED_CONTINUATION_SEPARATOR, { continuationSeparator: true }],
1044
+ [PARSED_PG_NUM, { pgNum: true }],
1045
+ [PARSED_LAST_RENDERED_PAGE_BREAK, { lastRenderedPageBreak: true }]
769
1046
  ]);
770
- /**
771
- * Creates "through" text wrapping for a floating drawing.
772
- *
773
- * WrapThrough is similar to WrapTight but allows text to wrap through
774
- * the concave portions of the drawing shape (e.g., the inside of the letter "O").
775
- * A default rectangular wrap polygon matching the image extent is generated.
776
- *
777
- * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
778
- *
779
- * ## XSD Schema
780
- * ```xml
781
- * <xsd:complexType name="CT_WrapThrough">
782
- * <xsd:sequence>
783
- * <xsd:element name="wrapPolygon" type="CT_WrapPath" minOccurs="1" maxOccurs="1"/>
784
- * </xsd:sequence>
785
- * <xsd:attribute name="wrapText" type="ST_WrapText" use="required"/>
786
- * <xsd:attribute name="distL" type="ST_WrapDistance"/>
787
- * <xsd:attribute name="distR" type="ST_WrapDistance"/>
788
- * </xsd:complexType>
789
- * ```
790
- */
791
- const createWrapThrough = (textWrapping, margins = {
792
- bottom: 0,
793
- left: 0,
794
- right: 0,
795
- top: 0
796
- }, extent) => element("wp:wrapThrough", {
797
- distL: margins.left,
798
- distR: margins.right,
799
- wrapText: textWrapping.side || TextWrappingSide.BOTH_SIDES
800
- }, [createWrapPolygon(extent.x, extent.y)]);
1047
+ function parsedRunToOptions(parsed) {
1048
+ const contentChildren = parsed.children.filter((c) => c !== PARSED_FOOTNOTE_REF);
1049
+ if (contentChildren.length === 0 && parsed.children.some((c) => c === PARSED_FOOTNOTE_REF)) return null;
1050
+ const opts = { ...parsed.properties };
1051
+ const isRefChild = (c) => typeof c === "object" && c !== null && ("commentReference" in c || "footnoteReference" in c || "endnoteReference" in c);
1052
+ const refChildren = contentChildren.filter(isRefChild);
1053
+ const nonRefChildren = contentChildren.filter((c) => !isRefChild(c));
1054
+ if (refChildren.length > 0 && nonRefChildren.length === 0) return refChildren[0];
1055
+ const symbolIdx = nonRefChildren.findIndex((c) => typeof c === "object" && c !== null && "symbolRun" in c);
1056
+ if (symbolIdx >= 0 && nonRefChildren.length === 1 && !parsed.properties) return nonRefChildren[symbolIdx];
1057
+ const textParts = [];
1058
+ let breakCount = 0;
1059
+ let hasPageBreak = false;
1060
+ let hasColumnBreak = false;
1061
+ const extraChildren = [];
1062
+ for (const child of nonRefChildren) if (typeof child === "string") textParts.push(child);
1063
+ else if (child === PARSED_LINE_BREAK) breakCount++;
1064
+ else if (child === PARSED_PAGE_BREAK) hasPageBreak = true;
1065
+ else if (child === PARSED_COLUMN_BREAK) hasColumnBreak = true;
1066
+ else {
1067
+ const mapped = SYMBOL_TO_CHILD.get(child);
1068
+ if (mapped) extraChildren.push(mapped);
1069
+ }
1070
+ if (extraChildren.length > 0) {
1071
+ const children = [];
1072
+ for (const child of nonRefChildren) if (typeof child === "string") children.push(child);
1073
+ else if (child === PARSED_LINE_BREAK) children.push({ break: 1 });
1074
+ else if (child === PARSED_PAGE_BREAK) children.push({ pageBreak: true });
1075
+ else if (child === PARSED_COLUMN_BREAK) children.push({ columnBreak: true });
1076
+ else {
1077
+ const mapped = SYMBOL_TO_CHILD.get(child);
1078
+ if (mapped) children.push(mapped);
1079
+ }
1080
+ opts.children = children;
1081
+ } else {
1082
+ if (textParts.length > 0) opts.text = textParts.join("");
1083
+ if (breakCount > 0) opts.break = breakCount;
1084
+ if (hasPageBreak) opts.pageBreak = true;
1085
+ if (hasColumnBreak) opts.columnBreak = true;
1086
+ }
1087
+ if (Object.keys(opts).length === 0 && textParts.length === 0 && breakCount === 0 && !hasPageBreak && !hasColumnBreak && extraChildren.length === 0) return null;
1088
+ return opts;
1089
+ }
801
1090
  //#endregion
802
- //#region src/shared/constants.ts
1091
+ //#region src/parts/paragraph/stringify.ts
803
1092
  /**
804
- * Shared constants for WordprocessingML documents.
1093
+ * Direct XML string builders for paragraph and run properties.
805
1094
  *
806
- * Provides alignment, number format, and space type constants
807
- * used across multiple document components.
1095
+ * Replaces `buildParagraphProperties() + xml()` and `buildRunProperties() + xml()`
1096
+ * with direct string concatenation — zero intermediate IXmlableObject allocation,
1097
+ * zero recursive xml() traversal. Follows PPTX/XLSX pattern.
808
1098
  *
809
1099
  * @module
810
1100
  */
811
- /**
812
- * Horizontal alignment options for floating drawings.
813
- *
814
- * Reference: https://www.datypic.com/sc/ooxml/t-wp_ST_AlignH.html
815
- *
816
- * @publicApi
817
- */
818
- const HorizontalPositionAlign = {
819
- CENTER: "center",
820
- INSIDE: "inside",
821
- LEFT: "left",
822
- OUTSIDE: "outside",
823
- RIGHT: "right"
824
- };
825
- /**
826
- * Vertical alignment options for floating drawings.
827
- *
828
- * Reference: https://www.datypic.com/sc/ooxml/t-wp_ST_AlignV.html
829
- *
830
- * @publicApi
831
- */
832
- const VerticalPositionAlign = {
833
- BOTTOM: "bottom",
834
- CENTER: "center",
835
- INSIDE: "inside",
1101
+ /** On/off: `<w:name/>` for true, `<w:name w:val="0"/>` for false */
1102
+ function onOff(name, val) {
1103
+ return val ? `<${name}/>` : `<${name} w:val="0"/>`;
1104
+ }
1105
+ /** Build attrs string from key-value pairs, skipping undefined */
1106
+ function attrParts(attrs) {
1107
+ const parts = [];
1108
+ for (const [key, val] of Object.entries(attrs)) if (val !== void 0) parts.push(`${key}="${val}"`);
1109
+ return parts.join(" ");
1110
+ }
1111
+ function borderStr(name, opts) {
1112
+ return `<${name} ${attrParts({
1113
+ "w:val": opts.style,
1114
+ "w:color": opts.color !== void 0 ? hexColorValue(opts.color) : void 0,
1115
+ "w:sz": opts.size !== void 0 ? eighthPointMeasureValue(opts.size) : void 0,
1116
+ "w:space": opts.space !== void 0 ? pointMeasureValue(opts.space) : void 0,
1117
+ "w:themeColor": opts.themeColor,
1118
+ "w:themeTint": opts.themeTint !== void 0 ? uCharHexNumber(opts.themeTint) : void 0,
1119
+ "w:themeShade": opts.themeShade !== void 0 ? uCharHexNumber(opts.themeShade) : void 0,
1120
+ "w:shadow": opts.shadow !== void 0 ? opts.shadow ? 1 : 0 : void 0,
1121
+ "w:frame": opts.frame !== void 0 ? opts.frame ? 1 : 0 : void 0
1122
+ })}/>`;
1123
+ }
1124
+ function shadingStr(opts) {
1125
+ return `<w:shd ${attrParts({
1126
+ "w:val": opts.type ?? "clear",
1127
+ "w:color": opts.color !== void 0 ? hexColorValue(opts.color) : void 0,
1128
+ "w:fill": opts.fill !== void 0 ? hexColorValue(opts.fill) : void 0,
1129
+ "w:themeColor": opts.themeColor,
1130
+ "w:themeTint": opts.themeTint !== void 0 ? uCharHexNumber(opts.themeTint) : void 0,
1131
+ "w:themeShade": opts.themeShade !== void 0 ? uCharHexNumber(opts.themeShade) : void 0,
1132
+ "w:themeFill": opts.themeFill,
1133
+ "w:themeFillTint": opts.themeFillTint !== void 0 ? uCharHexNumber(opts.themeFillTint) : void 0,
1134
+ "w:themeFillShade": opts.themeFillShade !== void 0 ? uCharHexNumber(opts.themeFillShade) : void 0
1135
+ })}/>`;
1136
+ }
1137
+ function spacingStr(opts) {
1138
+ return `<w:spacing ${attrParts({
1139
+ "w:after": opts.after,
1140
+ "w:afterAutospacing": opts.afterAutoSpacing !== void 0 ? opts.afterAutoSpacing ? 1 : 0 : void 0,
1141
+ "w:afterLines": opts.afterLines !== void 0 ? decimalNumber(opts.afterLines) : void 0,
1142
+ "w:before": opts.before,
1143
+ "w:beforeAutospacing": opts.beforeAutoSpacing !== void 0 ? opts.beforeAutoSpacing ? 1 : 0 : void 0,
1144
+ "w:beforeLines": opts.beforeLines !== void 0 ? decimalNumber(opts.beforeLines) : void 0,
1145
+ "w:line": opts.line,
1146
+ "w:lineRule": opts.lineRule
1147
+ })}/>`;
1148
+ }
1149
+ function indentStr(opts) {
1150
+ return `<w:ind ${attrParts({
1151
+ "w:start": opts.start !== void 0 ? signedTwipsMeasureValue(opts.start) : void 0,
1152
+ "w:startChars": opts.startChars !== void 0 ? decimalNumber(opts.startChars) : void 0,
1153
+ "w:end": opts.end !== void 0 ? signedTwipsMeasureValue(opts.end) : void 0,
1154
+ "w:endChars": opts.endChars !== void 0 ? decimalNumber(opts.endChars) : void 0,
1155
+ "w:left": opts.left !== void 0 ? signedTwipsMeasureValue(opts.left) : void 0,
1156
+ "w:leftChars": opts.leftChars !== void 0 ? decimalNumber(opts.leftChars) : void 0,
1157
+ "w:right": opts.right !== void 0 ? signedTwipsMeasureValue(opts.right) : void 0,
1158
+ "w:rightChars": opts.rightChars !== void 0 ? decimalNumber(opts.rightChars) : void 0,
1159
+ "w:hanging": opts.hanging !== void 0 ? twipsMeasureValue(opts.hanging) : void 0,
1160
+ "w:hangingChars": opts.hangingChars !== void 0 ? decimalNumber(opts.hangingChars) : void 0,
1161
+ "w:firstLine": opts.firstLine !== void 0 ? twipsMeasureValue(opts.firstLine) : void 0,
1162
+ "w:firstLineChars": opts.firstLineChars !== void 0 ? decimalNumber(opts.firstLineChars) : void 0
1163
+ })}/>`;
1164
+ }
1165
+ function tabStopsStr(defs) {
1166
+ return `<w:tabs>${defs.map(({ type, position, leader }) => {
1167
+ return `<w:tab ${attrParts({
1168
+ "w:val": type,
1169
+ "w:pos": position,
1170
+ "w:leader": leader
1171
+ })}/>`;
1172
+ }).join("")}</w:tabs>`;
1173
+ }
1174
+ function cnfStyleStr$1(opts) {
1175
+ return `<w:cnfStyle ${attrParts({
1176
+ "w:firstRow": opts.firstRow ? "1" : "0",
1177
+ "w:lastRow": opts.lastRow ? "1" : "0",
1178
+ "w:firstColumn": opts.firstColumn ? "1" : "0",
1179
+ "w:lastColumn": opts.lastColumn ? "1" : "0",
1180
+ "w:oddVBand": opts.oddVBand ? "1" : "0",
1181
+ "w:evenVBand": opts.evenVBand ? "1" : "0",
1182
+ "w:oddHBand": opts.oddHBand ? "1" : "0",
1183
+ "w:evenHBand": opts.evenHBand ? "1" : "0",
1184
+ "w:firstRowFirstColumn": opts.firstRowFirstColumn ? "1" : "0",
1185
+ "w:firstRowLastColumn": opts.firstRowLastColumn ? "1" : "0",
1186
+ "w:lastRowFirstColumn": opts.lastRowFirstColumn ? "1" : "0",
1187
+ "w:lastRowLastColumn": opts.lastRowLastColumn ? "1" : "0"
1188
+ })}/>`;
1189
+ }
1190
+ function framePrStr(opts) {
1191
+ const alignment = opts.alignment;
1192
+ const position = opts.position;
1193
+ return `<w:framePr ${attrParts({
1194
+ "w:xAlign": alignment?.x,
1195
+ "w:yAlign": alignment?.y,
1196
+ "w:hAnchor": opts.anchor?.horizontal,
1197
+ "w:anchorLock": opts.anchorLock,
1198
+ "w:vAnchor": opts.anchor?.vertical,
1199
+ "w:dropCap": opts.dropCap,
1200
+ "w:h": opts.height,
1201
+ "w:lines": opts.lines,
1202
+ "w:hRule": opts.rule,
1203
+ "w:hSpace": opts.space?.horizontal,
1204
+ "w:vSpace": opts.space?.vertical,
1205
+ "w:w": opts.width,
1206
+ "w:wrap": opts.wrap,
1207
+ "w:x": position?.x,
1208
+ "w:y": position?.y
1209
+ })}/>`;
1210
+ }
1211
+ function numPrStr(numberId, indentLevel, numberingChange) {
1212
+ const idVal = typeof numberId === "string" ? `{${numberId}}` : numberId;
1213
+ const parts = [`<w:ilvl w:val="${Math.min(indentLevel, 9)}"/>`, `<w:numId w:val="${idVal}"/>`];
1214
+ if (numberingChange) {
1215
+ const a = attrParts({
1216
+ "w:original": numberingChange.original,
1217
+ "w:id": numberingChange.id,
1218
+ "w:author": numberingChange.author,
1219
+ "w:date": numberingChange.date
1220
+ });
1221
+ parts.push(`<w:numberingChange ${a}/>`);
1222
+ }
1223
+ return `<w:numPr>${parts.join("")}</w:numPr>`;
1224
+ }
1225
+ function colorStr(colorOrOptions) {
1226
+ if (typeof colorOrOptions === "string") return `<w:color w:val="${hexColorValue(colorOrOptions)}"/>`;
1227
+ const opts = colorOrOptions;
1228
+ return `<w:color ${attrParts({
1229
+ "w:val": opts.val !== void 0 ? hexColorValue(opts.val) : void 0,
1230
+ "w:themeColor": opts.themeColor,
1231
+ "w:themeTint": opts.themeTint !== void 0 ? uCharHexNumber(opts.themeTint) : void 0,
1232
+ "w:themeShade": opts.themeShade !== void 0 ? uCharHexNumber(opts.themeShade) : void 0
1233
+ })}/>`;
1234
+ }
1235
+ function runFontsStr(nameOrAttrs, hint) {
1236
+ if (typeof nameOrAttrs === "string") return `<w:rFonts ${attrParts({
1237
+ "w:ascii": nameOrAttrs,
1238
+ "w:cs": nameOrAttrs,
1239
+ "w:eastAsia": nameOrAttrs,
1240
+ "w:hAnsi": nameOrAttrs,
1241
+ "w:hint": hint
1242
+ })}/>`;
1243
+ const attrs = nameOrAttrs;
1244
+ return `<w:rFonts ${attrParts({
1245
+ "w:ascii": attrs.ascii,
1246
+ "w:asciiTheme": attrs.asciiTheme,
1247
+ "w:cs": attrs.cs,
1248
+ "w:cstheme": attrs.cstheme,
1249
+ "w:eastAsia": attrs.eastAsia,
1250
+ "w:eastAsiaTheme": attrs.eastAsiaTheme,
1251
+ "w:hAnsi": attrs.hAnsi,
1252
+ "w:hAnsiTheme": attrs.hAnsiTheme,
1253
+ "w:hint": attrs.hint
1254
+ })}/>`;
1255
+ }
1256
+ function underlineStr(type, color) {
1257
+ return `<w:u ${attrParts({
1258
+ "w:val": type ?? "single",
1259
+ "w:color": color !== void 0 ? hexColorValue(color) : void 0
1260
+ })}/>`;
1261
+ }
1262
+ function eastAsianLayoutStr(opts) {
1263
+ return `<w:eastAsianLayout ${attrParts({
1264
+ "w:id": opts.id !== void 0 ? decimalNumber(opts.id) : void 0,
1265
+ "w:combine": opts.combine !== void 0 ? opts.combine ? 1 : 0 : void 0,
1266
+ "w:combineBrackets": opts.combineBrackets,
1267
+ "w:vert": opts.vert !== void 0 ? opts.vert ? 1 : 0 : void 0,
1268
+ "w:vertCompress": opts.vertCompress !== void 0 ? opts.vertCompress ? 1 : 0 : void 0
1269
+ })}/>`;
1270
+ }
1271
+ function languageStr(opts) {
1272
+ return `<w:lang ${attrParts({
1273
+ "w:val": opts.value,
1274
+ "w:eastAsia": opts.eastAsia,
1275
+ "w:bidi": opts.bidirectional
1276
+ })}/>`;
1277
+ }
1278
+ /**
1279
+ * Build `<w:pPr>` XML string directly from options — zero IXmlableObject allocation.
1280
+ *
1281
+ * Replaces `buildParagraphProperties() + xml()` with a single-pass string builder.
1282
+ */
1283
+ function stringifyParagraphProperties(options) {
1284
+ const numberingReferences = [];
1285
+ if (!options) return {
1286
+ xml: void 0,
1287
+ numberingReferences
1288
+ };
1289
+ const parts = [];
1290
+ if (options.heading) parts.push(`<w:pStyle w:val="${escapeXml(options.heading)}"/>`);
1291
+ if (options.bullet) parts.push("<w:pStyle w:val=\"ListParagraph\"/>");
1292
+ if (options.numbering) {
1293
+ if (!options.style && !options.heading) {
1294
+ if (!options.numbering.custom) parts.push("<w:pStyle w:val=\"ListParagraph\"/>");
1295
+ }
1296
+ }
1297
+ if (options.style) parts.push(`<w:pStyle w:val="${escapeXml(options.style)}"/>`);
1298
+ if (options.keepNext !== void 0) parts.push(onOff("w:keepNext", options.keepNext));
1299
+ if (options.keepLines !== void 0) parts.push(onOff("w:keepLines", options.keepLines));
1300
+ if (options.pageBreakBefore) parts.push("<w:pageBreakBefore/>");
1301
+ if (options.frame) parts.push(framePrStr(options.frame));
1302
+ if (options.widowControl !== void 0) parts.push(onOff("w:widowControl", options.widowControl));
1303
+ if (options.bullet) parts.push(`<w:numPr><w:ilvl w:val="${Math.min(options.bullet.level, 9)}"/><w:numId w:val="1"/></w:numPr>`);
1304
+ if (options.numbering) {
1305
+ numberingReferences.push({
1306
+ instance: options.numbering.instance ?? 0,
1307
+ reference: options.numbering.reference
1308
+ });
1309
+ const numId = `${options.numbering.reference}-${options.numbering.instance ?? 0}`;
1310
+ parts.push(numPrStr(numId, options.numbering.level, options.numbering.numberingChange));
1311
+ } else if (options.numbering === false) parts.push(numPrStr(0, 0));
1312
+ if (options.border) {
1313
+ const bParts = [];
1314
+ if (options.border.top) bParts.push(borderStr("w:top", options.border.top));
1315
+ if (options.border.left) bParts.push(borderStr("w:left", options.border.left));
1316
+ if (options.border.bottom) bParts.push(borderStr("w:bottom", options.border.bottom));
1317
+ if (options.border.right) bParts.push(borderStr("w:right", options.border.right));
1318
+ if (options.border.between) bParts.push(borderStr("w:between", options.border.between));
1319
+ if (options.border.bar) bParts.push(borderStr("w:bar", options.border.bar));
1320
+ if (bParts.length) parts.push(`<w:pBdr>${bParts.join("")}</w:pBdr>`);
1321
+ }
1322
+ if (options.thematicBreak) parts.push(`<w:pBdr>${borderStr("w:bottom", {
1323
+ color: "auto",
1324
+ size: 6,
1325
+ space: 1,
1326
+ style: BorderStyle.SINGLE
1327
+ })}</w:pBdr>`);
1328
+ if (options.shading) parts.push(shadingStr(options.shading));
1329
+ if (options.wordWrap) parts.push("<w:wordWrap w:val=\"0\"/>");
1330
+ if (options.overflowPunctuation) parts.push(onOff("w:overflowPunct", options.overflowPunctuation));
1331
+ const tabDefs = [
1332
+ ...options.rightTabStop !== void 0 ? [{
1333
+ position: options.rightTabStop,
1334
+ type: "right"
1335
+ }] : [],
1336
+ ...options.tabStops ? options.tabStops : [],
1337
+ ...options.leftTabStop !== void 0 ? [{
1338
+ position: options.leftTabStop,
1339
+ type: "left"
1340
+ }] : []
1341
+ ];
1342
+ if (tabDefs.length > 0) parts.push(tabStopsStr(tabDefs));
1343
+ if (options.bidirectional !== void 0) parts.push(onOff("w:bidi", options.bidirectional));
1344
+ if (options.spacing) parts.push(spacingStr(options.spacing));
1345
+ if (options.indent) parts.push(indentStr(options.indent));
1346
+ if (options.contextualSpacing !== void 0) parts.push(onOff("w:contextualSpacing", options.contextualSpacing));
1347
+ if (options.alignment) parts.push(`<w:jc w:val="${options.alignment}"/>`);
1348
+ if (options.outlineLevel !== void 0) parts.push(`<w:outlineLvl w:val="${options.outlineLevel}"/>`);
1349
+ if (options.divId !== void 0) parts.push(`<w:divId w:val="${options.divId}"/>`);
1350
+ if (options.cnfStyle) parts.push(cnfStyleStr$1(options.cnfStyle));
1351
+ if (options.suppressLineNumbers !== void 0) parts.push(onOff("w:suppressLineNumbers", options.suppressLineNumbers));
1352
+ if (options.autoSpaceEastAsianText !== void 0) parts.push(onOff("w:autoSpaceDN", options.autoSpaceEastAsianText));
1353
+ if (options.suppressAutoHyphens !== void 0) parts.push(onOff("w:suppressAutoHyphens", options.suppressAutoHyphens));
1354
+ if (options.adjustRightInd !== void 0) parts.push(onOff("w:adjustRightInd", options.adjustRightInd));
1355
+ if (options.snapToGrid !== void 0) parts.push(onOff("w:snapToGrid", options.snapToGrid));
1356
+ if (options.mirrorIndents !== void 0) parts.push(onOff("w:mirrorIndents", options.mirrorIndents));
1357
+ if (options.kinsoku !== void 0) parts.push(onOff("w:kinsoku", options.kinsoku));
1358
+ if (options.topLinePunct !== void 0) parts.push(onOff("w:topLinePunct", options.topLinePunct));
1359
+ if (options.autoSpaceDE !== void 0) parts.push(onOff("w:autoSpaceDE", options.autoSpaceDE));
1360
+ if (options.textAlignment !== void 0) parts.push(`<w:textAlignment w:val="${options.textAlignment}"/>`);
1361
+ if (options.textboxTightWrap !== void 0) parts.push(`<w:textboxTightWrap w:val="${options.textboxTightWrap}"/>`);
1362
+ if (options.textDirection !== void 0) parts.push(`<w:textDirection w:val="${options.textDirection}"/>`);
1363
+ if (options.suppressOverlap !== void 0) parts.push(onOff("w:suppressOverlap", options.suppressOverlap));
1364
+ if (options.run) {
1365
+ const inner = stringifyRunPropertiesInner(options.run);
1366
+ if (inner !== void 0) {
1367
+ const extra = [];
1368
+ const runOpts = options.run;
1369
+ if (runOpts.insertion) {
1370
+ const { id, author, date } = runOpts.insertion;
1371
+ extra.push(`<w:ins w:id="${id}" w:author="${escapeXml(author)}" w:date="${date}"/>`);
1372
+ }
1373
+ if (runOpts.deletion) {
1374
+ const { id, author, date } = runOpts.deletion;
1375
+ extra.push(`<w:del w:id="${id}" w:author="${escapeXml(author)}" w:date="${date}"/>`);
1376
+ }
1377
+ const body = inner + extra.join("");
1378
+ parts.push(`<w:rPr>${body}</w:rPr>`);
1379
+ }
1380
+ }
1381
+ if (options.revision) {
1382
+ const rev = options.revision;
1383
+ const { author: _a, date: _d, id: _i, ...originalProps } = rev;
1384
+ const inner = stringifyParagraphProperties({
1385
+ ...originalProps,
1386
+ includeIfEmpty: true
1387
+ });
1388
+ parts.push(`<w:pPrChange w:author="${escapeXml(rev.author)}" w:date="${rev.date}" w:id="${rev.id}">${inner.xml ?? "<w:pPr/>"}</w:pPrChange>`);
1389
+ }
1390
+ const body = parts.join("");
1391
+ return {
1392
+ xml: options.includeIfEmpty || body.length > 0 ? `<w:pPr>${body}</w:pPr>` : void 0,
1393
+ numberingReferences
1394
+ };
1395
+ }
1396
+ /**
1397
+ * Build the inner content of `<w:rPr>` as a string.
1398
+ * Returns undefined if no properties are set.
1399
+ */
1400
+ function stringifyRunPropertiesInner(opts) {
1401
+ if (!opts) return void 0;
1402
+ const parts = [];
1403
+ if (opts.style) parts.push(`<w:rStyle w:val="${escapeXml(opts.style)}"/>`);
1404
+ if (opts.font) if (typeof opts.font === "string") parts.push(runFontsStr(opts.font));
1405
+ else if ("name" in opts.font) parts.push(runFontsStr(opts.font.name, opts.font.hint));
1406
+ else parts.push(runFontsStr(opts.font));
1407
+ if (opts.bold !== void 0) parts.push(onOff("w:b", opts.bold));
1408
+ if ((opts.boldComplexScript === void 0 && opts.bold !== void 0 || opts.boldComplexScript) !== void 0) parts.push(onOff("w:bCs", opts.boldComplexScript ?? opts.bold));
1409
+ if (opts.italic !== void 0) parts.push(onOff("w:i", opts.italic));
1410
+ if ((opts.italicComplexScript === void 0 && opts.italic !== void 0 || opts.italicComplexScript) !== void 0) parts.push(onOff("w:iCs", opts.italicComplexScript ?? opts.italic));
1411
+ if (opts.smallCaps !== void 0) parts.push(onOff("w:smallCaps", opts.smallCaps));
1412
+ else if (opts.allCaps !== void 0) parts.push(onOff("w:caps", opts.allCaps));
1413
+ if (opts.strike !== void 0) parts.push(onOff("w:strike", opts.strike));
1414
+ if (opts.doubleStrike !== void 0) parts.push(onOff("w:dstrike", opts.doubleStrike));
1415
+ if (opts.emboss !== void 0) parts.push(onOff("w:emboss", opts.emboss));
1416
+ if (opts.imprint !== void 0) parts.push(onOff("w:imprint", opts.imprint));
1417
+ if (opts.outline !== void 0) parts.push(onOff("w:outline", opts.outline));
1418
+ if (opts.shadow !== void 0) parts.push(onOff("w:shadow", opts.shadow));
1419
+ if (opts.webHidden !== void 0) parts.push(onOff("w:webHidden", opts.webHidden));
1420
+ if (opts.noProof !== void 0) parts.push(onOff("w:noProof", opts.noProof));
1421
+ if (opts.snapToGrid !== void 0) parts.push(onOff("w:snapToGrid", opts.snapToGrid));
1422
+ if (opts.vanish) parts.push(onOff("w:vanish", opts.vanish));
1423
+ if (opts.color) parts.push(colorStr(opts.color));
1424
+ if (opts.characterSpacing) parts.push(`<w:spacing w:val="${signedTwipsMeasureValue(opts.characterSpacing)}"/>`);
1425
+ if (opts.scale !== void 0) parts.push(`<w:w w:val="${opts.scale}"/>`);
1426
+ if (opts.kern) parts.push(`<w:kern w:val="${hpsMeasureValue(opts.kern)}"/>`);
1427
+ if (opts.position) parts.push(`<w:position w:val="${opts.position}"/>`);
1428
+ if (opts.size !== void 0) parts.push(`<w:sz w:val="${hpsMeasureValue(opts.size * 2)}"/>`);
1429
+ const szCs = opts.sizeComplexScript === void 0 || opts.sizeComplexScript === true ? opts.size : opts.sizeComplexScript;
1430
+ if (szCs) parts.push(`<w:szCs w:val="${hpsMeasureValue(szCs * 2)}"/>`);
1431
+ if (opts.highlight) parts.push(`<w:highlight w:val="${opts.highlight}"/>`);
1432
+ if (opts.highlightComplexScript === true) {
1433
+ if (opts.highlight) parts.push(`<w:highlightCs w:val="${opts.highlight}"/>`);
1434
+ } else if (opts.highlightComplexScript !== void 0 && opts.highlightComplexScript !== false) parts.push(`<w:highlightCs w:val="${opts.highlightComplexScript}"/>`);
1435
+ if (opts.underline) parts.push(underlineStr(opts.underline.type, opts.underline.color));
1436
+ if (opts.effect) parts.push(`<w:effect w:val="${opts.effect}"/>`);
1437
+ if (opts.border) parts.push(borderStr("w:bdr", opts.border));
1438
+ if (opts.shading) parts.push(shadingStr(opts.shading));
1439
+ if (opts.subScript) parts.push("<w:vertAlign w:val=\"subscript\"/>");
1440
+ if (opts.superScript) parts.push("<w:vertAlign w:val=\"superscript\"/>");
1441
+ if (opts.rightToLeft !== void 0) parts.push(onOff("w:rtl", opts.rightToLeft));
1442
+ if (opts.emphasisMark) parts.push(`<w:em w:val="${opts.emphasisMark.type ?? "dot"}"/>`);
1443
+ if (opts.language) parts.push(languageStr(opts.language));
1444
+ if (opts.specVanish) parts.push("<w:specVanish/>");
1445
+ if (opts.math) parts.push(onOff("w:oMath", opts.math));
1446
+ if (opts.fitText !== void 0) parts.push(`<w:fitText w:val="${opts.fitText}"/>`);
1447
+ if (opts.complexScript !== void 0) parts.push(onOff("w:cs", opts.complexScript));
1448
+ if (opts.eastAsianLayout) parts.push(eastAsianLayoutStr(opts.eastAsianLayout));
1449
+ if (opts.contentPartRId) parts.push(`<w:contentPart r:id="${opts.contentPartRId}"/>`);
1450
+ if (opts.revision) {
1451
+ const rev = opts.revision;
1452
+ const { author: _a, date: _d, id: _i, ...originalProps } = rev;
1453
+ const inner = stringifyRunPropertiesInner(originalProps);
1454
+ parts.push(`<w:rPrChange w:author="${escapeXml(rev.author)}" w:date="${rev.date}" w:id="${rev.id}"><w:rPr>${inner ?? ""}</w:rPr></w:rPrChange>`);
1455
+ }
1456
+ return parts.length > 0 ? parts.join("") : void 0;
1457
+ }
1458
+ /**
1459
+ * Build `<w:rPr>` XML string directly from options — zero IXmlableObject allocation.
1460
+ *
1461
+ * Replaces `buildRunProperties() + xml()` with a single-pass string builder.
1462
+ */
1463
+ function stringifyRunProperties(opts) {
1464
+ const inner = stringifyRunPropertiesInner(opts);
1465
+ return inner ? `<w:rPr>${inner}</w:rPr>` : void 0;
1466
+ }
1467
+ //#endregion
1468
+ //#region src/parts/bodychildren.ts
1469
+ /**
1470
+ * Body-level child descriptors for DOCX.
1471
+ *
1472
+ * Provides descriptor-based stringification for section children.
1473
+ * All types use pure string builders — zero class instantiation, zero toXml().
1474
+ *
1475
+ * @module
1476
+ */
1477
+ function escapeAttr(s) {
1478
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1479
+ }
1480
+ const ALTCHUNK_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk";
1481
+ function wrapHtmlDocument(fragment) {
1482
+ if (/<(!DOCTYPE|html|HTML)/i.test(fragment)) return fragment;
1483
+ return `<!DOCTYPE html>\n<html><head><meta charset="utf-8"></head>\n<body>${fragment}</body></html>`;
1484
+ }
1485
+ const altChunkDesc = {
1486
+ kind: "custom",
1487
+ stringify(opts, ctx) {
1488
+ const relId = uniqueId();
1489
+ const extension = opts.extension;
1490
+ const partPath = `afchunks/afchunk${relId}.${extension}`;
1491
+ const rawData = typeof opts.data === "string" ? new TextEncoder().encode(opts.data) : opts.data;
1492
+ const data = opts.contentType === "text/html" && typeof opts.data === "string" ? new TextEncoder().encode(wrapHtmlDocument(opts.data)) : rawData;
1493
+ ctx.fileData.document.relationships.addRelationship(relId, ALTCHUNK_REL_TYPE, partPath);
1494
+ ctx.fileData.altChunks.addAltChunk(relId, {
1495
+ key: relId,
1496
+ data,
1497
+ path: partPath,
1498
+ extension,
1499
+ contentType: opts.contentType
1500
+ });
1501
+ const rId = `rId${relId}`;
1502
+ if (opts.matchSrc) return `<w:altChunk r:id="${rId}"><w:altChunkPr><w:matchSrc/></w:altChunkPr></w:altChunk>`;
1503
+ return `<w:altChunk r:id="${rId}"/>`;
1504
+ },
1505
+ parse(el, ctx) {
1506
+ const rId = attr(el, "r:id");
1507
+ const opts = {};
1508
+ const altChunkPr = findChild(el, "w:altChunkPr");
1509
+ if (altChunkPr && findChild(altChunkPr, "w:matchSrc")) opts.matchSrc = true;
1510
+ const dctx = ctx;
1511
+ if (rId) {
1512
+ const path = dctx.resolveRelationship(rId);
1513
+ if (path) {
1514
+ const data = dctx.getRaw(path);
1515
+ if (data) {
1516
+ opts.data = data;
1517
+ switch (path.split(".").pop() ?? "txt") {
1518
+ case "html":
1519
+ opts.contentType = "text/html";
1520
+ opts.extension = "html";
1521
+ break;
1522
+ case "rtf":
1523
+ opts.contentType = "application/rtf";
1524
+ opts.extension = "rtf";
1525
+ break;
1526
+ default:
1527
+ opts.contentType = "text/plain";
1528
+ opts.extension = "txt";
1529
+ break;
1530
+ }
1531
+ }
1532
+ }
1533
+ }
1534
+ return opts;
1535
+ }
1536
+ };
1537
+ const SUBDOC_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/subDocument";
1538
+ const subDocDesc = {
1539
+ kind: "custom",
1540
+ stringify(opts, ctx) {
1541
+ const relId = uniqueId();
1542
+ const partPath = `subdocs/subdoc${relId}.docx`;
1543
+ const data = typeof opts.data === "string" ? new TextEncoder().encode(opts.data) : opts.data;
1544
+ ctx.fileData.document.relationships.addRelationship(relId, SUBDOC_REL_TYPE, partPath);
1545
+ ctx.fileData.subDocs.addSubDoc(relId, {
1546
+ data,
1547
+ path: partPath
1548
+ });
1549
+ return `<w:subDoc r:id="rId${relId}"/>`;
1550
+ },
1551
+ parse(el, ctx) {
1552
+ const rId = attr(el, "r:id");
1553
+ const dctx = ctx;
1554
+ if (rId) {
1555
+ const path = dctx.resolveRelationship(rId);
1556
+ if (path) {
1557
+ const data = dctx.getRaw(path);
1558
+ if (data) return { data };
1559
+ }
1560
+ }
1561
+ return { data: new Uint8Array(0) };
1562
+ }
1563
+ };
1564
+ function escapeXml$1(s) {
1565
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1566
+ }
1567
+ function sdtListItemXml(item, forceValue) {
1568
+ const attrs = [];
1569
+ if (item.displayText !== void 0) attrs.push(`w:displayText="${escapeXml$1(item.displayText)}"`);
1570
+ const value = item.value ?? (forceValue ? item.displayText : void 0);
1571
+ if (value !== void 0) attrs.push(`w:value="${escapeXml$1(value)}"`);
1572
+ return `<w:listItem ${attrs.join(" ")}/>`;
1573
+ }
1574
+ function sdtListTypeXml(name, options) {
1575
+ const parts = [];
1576
+ if (options.items) for (const item of options.items) parts.push(sdtListItemXml(item, name === "w:dropDownList"));
1577
+ const attrs = [];
1578
+ if (options.lastValue !== void 0) attrs.push(`w:lastValue="${escapeXml$1(options.lastValue)}"`);
1579
+ const attrStr = attrs.length ? " " + attrs.join(" ") : "";
1580
+ return parts.length ? `<${name}${attrStr}>${parts.join("")}</${name}>` : `<${name}${attrStr}/>`;
1581
+ }
1582
+ function sdtDateXml(options) {
1583
+ const parts = [];
1584
+ if (options.dateFormat !== void 0) parts.push(`<w:dateFormat w:val="${escapeXml$1(options.dateFormat)}"/>`);
1585
+ if (options.languageId !== void 0) parts.push(`<w:lid w:val="${escapeXml$1(options.languageId)}"/>`);
1586
+ if (options.storeMappedDataAs !== void 0) parts.push(`<w:storeMappedDataAs w:val="${options.storeMappedDataAs}"/>`);
1587
+ if (options.calendar !== void 0) parts.push(`<w:calendar w:val="${options.calendar}"/>`);
1588
+ const attrs = [];
1589
+ if (options.fullDate !== void 0) attrs.push(`w:fullDate="${options.fullDate}"`);
1590
+ const attrStr = attrs.length ? " " + attrs.join(" ") : "";
1591
+ return parts.length ? `<w:date${attrStr}>${parts.join("")}</w:date>` : `<w:date${attrStr}/>`;
1592
+ }
1593
+ function sdtDataBindingXml(options) {
1594
+ const attrs = [`w:xpath="${escapeXml$1(options.xpath)}"`, `w:storeItemID="${escapeXml$1(options.storeItemID)}"`];
1595
+ if (options.prefixMappings !== void 0) attrs.push(`w:prefixMappings="${escapeXml$1(options.prefixMappings)}"`);
1596
+ return `<w:dataBinding ${attrs.join(" ")}/>`;
1597
+ }
1598
+ function sdtDocPartXml(name, options) {
1599
+ const parts = [];
1600
+ if (options.gallery !== void 0) parts.push(`<w:docPartGallery w:val="${escapeXml$1(options.gallery)}"/>`);
1601
+ if (options.category !== void 0) parts.push(`<w:docPartCategory w:val="${escapeXml$1(options.category)}"/>`);
1602
+ if (options.unique !== void 0) parts.push(options.unique ? "<w:docPartUnique/>" : "<w:docPartUnique w:val=\"0\"/>");
1603
+ return parts.length ? `<${name}>${parts.join("")}</${name}>` : `<${name}/>`;
1604
+ }
1605
+ function onOffAttr(name, val) {
1606
+ return val ? `<${name}/>` : `<${name} w:val="0"/>`;
1607
+ }
1608
+ const W14_NS = "xmlns:w14=\"http://schemas.microsoft.com/office/word/2010/wordml\"";
1609
+ /** Default symbol font for checkbox content controls (CT_SdtCheckboxSymbol). */
1610
+ const CHECKBOX_FONT = "MS Gothic";
1611
+ const DEFAULT_CHECKED = {
1612
+ val: "2612",
1613
+ font: CHECKBOX_FONT
1614
+ };
1615
+ const DEFAULT_UNCHECKED = {
1616
+ val: "2610",
1617
+ font: CHECKBOX_FONT
1618
+ };
1619
+ /**
1620
+ * Build a w14:checkbox element (Word 2010+ content control checkbox).
1621
+ *
1622
+ * Lives in the w14 extension namespace; emitted with an inline xmlns:w14 so it
1623
+ * is valid wherever w:sdtPr appears. validate.ts tolerates the w14 namespace.
1624
+ */
1625
+ function sdtCheckboxXml(opts) {
1626
+ const checked = opts.checkedState ?? DEFAULT_CHECKED;
1627
+ const unchecked = opts.uncheckedState ?? DEFAULT_UNCHECKED;
1628
+ return `<w14:checkbox ${W14_NS}>${(opts.checked ? "<w14:checked/>" : "<w14:checked w14:val=\"0\"/>") + `<w14:checkedState w14:val="${escapeXml$1(checked.val)}" w14:font="${escapeXml$1(checked.font ?? CHECKBOX_FONT)}"/><w14:uncheckedState w14:val="${escapeXml$1(unchecked.val)}" w14:font="${escapeXml$1(unchecked.font ?? CHECKBOX_FONT)}"/>`}</w14:checkbox>`;
1629
+ }
1630
+ /** Build the run that renders a checkbox content control's current state symbol. */
1631
+ function checkboxSymbolRunInner(cb) {
1632
+ const symbol = cb.checked ?? false ? cb.checkedState ?? DEFAULT_CHECKED : cb.uncheckedState ?? DEFAULT_UNCHECKED;
1633
+ const font = escapeXml$1(symbol.font ?? CHECKBOX_FONT);
1634
+ return `<w:r><w:rPr><w:rFonts w:ascii="${font}" w:hAnsi="${font}"/></w:rPr><w:t>${escapeXml$1(String.fromCodePoint(parseInt(symbol.val, 16)))}</w:t></w:r>`;
1635
+ }
1636
+ function stringifySdtPr(opts) {
1637
+ const parts = [];
1638
+ if (opts.alias !== void 0) parts.push(`<w:alias w:val="${escapeXml$1(opts.alias)}"/>`);
1639
+ if (opts.tag !== void 0) parts.push(`<w:tag w:val="${escapeXml$1(opts.tag)}"/>`);
1640
+ if (opts.id !== void 0) parts.push(`<w:id w:val="${opts.id}"/>`);
1641
+ if (opts.lock !== void 0) parts.push(`<w:lock w:val="${opts.lock}"/>`);
1642
+ if (opts.temporary !== void 0) parts.push(onOffAttr("w:temporary", opts.temporary));
1643
+ const effectiveShowingPlcHdr = opts.showingPlaceholder ?? false;
1644
+ if (opts.showingPlaceholder !== void 0 || effectiveShowingPlcHdr) parts.push(onOffAttr("w:showingPlcHdr", effectiveShowingPlcHdr));
1645
+ if (opts.dataBinding) parts.push(sdtDataBindingXml(opts.dataBinding));
1646
+ if (opts.label !== void 0) parts.push(`<w:label w:val="${opts.label}"/>`);
1647
+ if (opts.tabIndex !== void 0) parts.push(`<w:tabIndex w:val="${opts.tabIndex}"/>`);
1648
+ if (opts.equation) parts.push("<w:equation/>");
1649
+ else if (opts.comboBox) parts.push(sdtListTypeXml("w:comboBox", opts.comboBox));
1650
+ else if (opts.date) parts.push(sdtDateXml(opts.date));
1651
+ else if (opts.docPartObj) parts.push(sdtDocPartXml("w:docPartObj", opts.docPartObj));
1652
+ else if (opts.docPartList) parts.push(sdtDocPartXml("w:docPartList", opts.docPartList));
1653
+ else if (opts.dropDownList) parts.push(sdtListTypeXml("w:dropDownList", opts.dropDownList));
1654
+ else if (opts.picture) parts.push("<w:picture/>");
1655
+ else if (opts.richText) parts.push("<w:richText/>");
1656
+ else if (opts.text !== void 0) {
1657
+ const multiLine = opts.text.multiLine ?? false;
1658
+ parts.push(`<w:text w:multiLine="${multiLine}"/>`);
1659
+ } else if (opts.citation) parts.push("<w:citation/>");
1660
+ else if (opts.group) parts.push("<w:group/>");
1661
+ else if (opts.bibliography) parts.push("<w:bibliography/>");
1662
+ else if (opts.checkbox) parts.push(sdtCheckboxXml(opts.checkbox));
1663
+ return parts.length ? `<w:sdtPr>${parts.join("")}</w:sdtPr>` : "<w:sdtPr/>";
1664
+ }
1665
+ /**
1666
+ * Build the <w:sdt> shell shared by all four SDT levels (block/run/cell/row).
1667
+ * The caller supplies the sdtContent body; sdtPr/sdtEndPr are handled uniformly.
1668
+ */
1669
+ function stringifySdtShell(properties, endProperties, contentXml) {
1670
+ const endPrInner = endProperties ? stringifyRunPropertiesInner(endProperties) : void 0;
1671
+ const endPr = endPrInner ? `<w:sdtEndPr>${endPrInner}</w:sdtEndPr>` : "<w:sdtEndPr/>";
1672
+ const content = contentXml ? `<w:sdtContent>${contentXml}</w:sdtContent>` : "<w:sdtContent/>";
1673
+ return `<w:sdt>${stringifySdtPr(properties)}${endPr}${content}</w:sdt>`;
1674
+ }
1675
+ /** Parse w:sdtPr element into SdtPropertiesOptions. */
1676
+ function parseSdtPr(el) {
1677
+ const opts = {};
1678
+ const alias = findChild(el, "w:alias");
1679
+ if (alias) opts.alias = attr(alias, "w:val");
1680
+ const tag = findChild(el, "w:tag");
1681
+ if (tag) {
1682
+ const val = attr(tag, "w:val");
1683
+ if (val) opts.tag = val;
1684
+ }
1685
+ const id = findChild(el, "w:id");
1686
+ if (id) {
1687
+ const val = attrNum(id, "w:val");
1688
+ if (val !== void 0) opts.id = val;
1689
+ }
1690
+ const lock = findChild(el, "w:lock");
1691
+ if (lock) {
1692
+ const val = attr(lock, "w:val");
1693
+ if (val) opts.lock = val;
1694
+ }
1695
+ const temporary = findChild(el, "w:temporary");
1696
+ if (temporary) opts.temporary = attrBool(temporary, "w:val") ?? true;
1697
+ const showingPlcHdr = findChild(el, "w:showingPlcHdr");
1698
+ if (showingPlcHdr) opts.showingPlaceholder = attrBool(showingPlcHdr, "w:val") ?? true;
1699
+ const label = findChild(el, "w:label");
1700
+ if (label) {
1701
+ const val = attrNum(label, "w:val");
1702
+ if (val !== void 0) opts.label = val;
1703
+ }
1704
+ const tabIndex = findChild(el, "w:tabIndex");
1705
+ if (tabIndex) {
1706
+ const val = attrNum(tabIndex, "w:val");
1707
+ if (val !== void 0) opts.tabIndex = val;
1708
+ }
1709
+ const dataBinding = findChild(el, "w:dataBinding");
1710
+ if (dataBinding) opts.dataBinding = {
1711
+ xpath: attr(dataBinding, "w:xpath") ?? "",
1712
+ storeItemID: attr(dataBinding, "w:storeItemID") ?? "",
1713
+ prefixMappings: attr(dataBinding, "w:prefixMappings")
1714
+ };
1715
+ if (findChild(el, "w:equation")) opts.equation = true;
1716
+ else if (findChild(el, "w:comboBox")) {
1717
+ const comboBox = findChild(el, "w:comboBox");
1718
+ const items = [];
1719
+ for (const li of children(comboBox, "w:listItem")) items.push({
1720
+ displayText: attr(li, "w:displayText"),
1721
+ value: attr(li, "w:value")
1722
+ });
1723
+ opts.comboBox = {
1724
+ items: items.length > 0 ? items : void 0,
1725
+ lastValue: attr(comboBox, "w:lastValue")
1726
+ };
1727
+ } else if (findChild(el, "w:date")) {
1728
+ const date = findChild(el, "w:date");
1729
+ const dateOpts = {};
1730
+ const dateFormat = findChild(date, "w:dateFormat");
1731
+ if (dateFormat) dateOpts.dateFormat = textOf(dateFormat);
1732
+ const lid = findChild(date, "w:lid");
1733
+ if (lid) dateOpts.languageId = textOf(lid);
1734
+ const storeMapped = findChild(date, "w:storeMappedDataAs");
1735
+ if (storeMapped) dateOpts.storeMappedDataAs = attr(storeMapped, "w:val");
1736
+ const calendar = findChild(date, "w:calendar");
1737
+ if (calendar) dateOpts.calendar = attr(calendar, "w:val");
1738
+ const fullDate = attr(date, "w:fullDate");
1739
+ if (fullDate) dateOpts.fullDate = fullDate;
1740
+ opts.date = dateOpts;
1741
+ } else if (findChild(el, "w:docPartObj")) {
1742
+ const dp = findChild(el, "w:docPartObj");
1743
+ const dpObj = {};
1744
+ const gallery = findChild(dp, "w:docPartGallery");
1745
+ if (gallery) dpObj.gallery = attr(gallery, "w:val");
1746
+ const category = findChild(dp, "w:docPartCategory");
1747
+ if (category) dpObj.category = attr(category, "w:val");
1748
+ if (findChild(dp, "w:docPartUnique")) dpObj.unique = true;
1749
+ opts.docPartObj = dpObj;
1750
+ } else if (findChild(el, "w:docPartList")) {
1751
+ const dp = findChild(el, "w:docPartList");
1752
+ const dpObj = {};
1753
+ const gallery = findChild(dp, "w:docPartGallery");
1754
+ if (gallery) dpObj.gallery = attr(gallery, "w:val");
1755
+ const category = findChild(dp, "w:docPartCategory");
1756
+ if (category) dpObj.category = attr(category, "w:val");
1757
+ if (findChild(dp, "w:docPartUnique")) dpObj.unique = true;
1758
+ opts.docPartList = dpObj;
1759
+ } else if (findChild(el, "w:dropDownList")) {
1760
+ const ddl = findChild(el, "w:dropDownList");
1761
+ const items = [];
1762
+ for (const li of children(ddl, "w:listItem")) items.push({
1763
+ displayText: attr(li, "w:displayText"),
1764
+ value: attr(li, "w:value")
1765
+ });
1766
+ opts.dropDownList = {
1767
+ items: items.length > 0 ? items : void 0,
1768
+ lastValue: attr(ddl, "w:lastValue")
1769
+ };
1770
+ } else if (findChild(el, "w:picture")) opts.picture = true;
1771
+ else if (findChild(el, "w:richText")) opts.richText = true;
1772
+ else if (findChild(el, "w:text")) opts.text = { multiLine: attrBool(findChild(el, "w:text"), "w:multiLine") };
1773
+ else if (findChild(el, "w:citation")) opts.citation = true;
1774
+ else if (findChild(el, "w:group")) opts.group = true;
1775
+ else if (findChild(el, "w:bibliography")) opts.bibliography = true;
1776
+ else if (findChild(el, "w14:checkbox")) {
1777
+ const cb = findChild(el, "w14:checkbox");
1778
+ const cbObj = {};
1779
+ const checked = findChild(cb, "w14:checked");
1780
+ if (checked) cbObj.checked = attrBool(checked, "w14:val") ?? true;
1781
+ const checkedState = findChild(cb, "w14:checkedState");
1782
+ if (checkedState) cbObj.checkedState = {
1783
+ val: attr(checkedState, "w14:val") ?? "",
1784
+ font: attr(checkedState, "w14:font")
1785
+ };
1786
+ const uncheckedState = findChild(cb, "w14:uncheckedState");
1787
+ if (uncheckedState) cbObj.uncheckedState = {
1788
+ val: attr(uncheckedState, "w14:val") ?? "",
1789
+ font: attr(uncheckedState, "w14:font")
1790
+ };
1791
+ opts.checkbox = cbObj;
1792
+ }
1793
+ return opts;
1794
+ }
1795
+ /** Parse w:customXmlPr element into CustomXmlPrOptions. */
1796
+ function parseCustomXmlPr(el) {
1797
+ const opts = {};
1798
+ const placeholder = findChild(el, "w:placeholder");
1799
+ if (placeholder) {
1800
+ const val = attr(placeholder, "w:val");
1801
+ if (val) opts.placeholder = val;
1802
+ }
1803
+ const attributes = [];
1804
+ for (const child of el.elements ?? []) {
1805
+ if (child.name !== "w:attr") continue;
1806
+ const name = attr(child, "w:name");
1807
+ const val = attr(child, "w:val");
1808
+ if (name && val) {
1809
+ const attrOpts = {
1810
+ name,
1811
+ val
1812
+ };
1813
+ const uriVal = attr(child, "w:uri");
1814
+ if (uriVal) attrOpts.uri = uriVal;
1815
+ attributes.push(attrOpts);
1816
+ }
1817
+ }
1818
+ if (attributes.length > 0) opts.attributes = attributes;
1819
+ return opts;
1820
+ }
1821
+ /** Body child element parsing callback for SDT/customXml content. */
1822
+ let _parseBodyChild;
1823
+ /** Register the body child parser (called from parse/body.ts to break circular dependency). */
1824
+ function setBodyParseChild(parser) {
1825
+ _parseBodyChild = parser;
1826
+ }
1827
+ function parseBodyChildren(elements, ctx) {
1828
+ if (!_parseBodyChild) return [];
1829
+ const result = [];
1830
+ for (const el of elements) result.push(_parseBodyChild(el, ctx));
1831
+ return result;
1832
+ }
1833
+ const sdtBlockDesc = {
1834
+ kind: "custom",
1835
+ stringify(opts, ctx) {
1836
+ const parts = ["<w:sdt>"];
1837
+ parts.push(stringifySdtPr(opts.properties));
1838
+ const endPrInner = opts.endProperties ? stringifyRunPropertiesInner(opts.endProperties) : void 0;
1839
+ parts.push(endPrInner ? `<w:sdtEndPr>${endPrInner}</w:sdtEndPr>` : "<w:sdtEndPr/>");
1840
+ if (opts.properties.checkbox) parts.push(`<w:sdtContent><w:p>${checkboxSymbolRunInner(opts.properties.checkbox)}</w:p></w:sdtContent>`);
1841
+ else if (opts.children && opts.children.length > 0) {
1842
+ const contentParts = [];
1843
+ for (const child of opts.children) contentParts.push(ctx.stringifyChild(child, ctx));
1844
+ const contentBody = contentParts.join("");
1845
+ parts.push(contentBody ? `<w:sdtContent>${contentBody}</w:sdtContent>` : "<w:sdtContent/>");
1846
+ }
1847
+ parts.push("</w:sdt>");
1848
+ return parts.join("");
1849
+ },
1850
+ parse(el, ctx) {
1851
+ const dctx = ctx;
1852
+ const sdtPr = findChild(el, "w:sdtPr");
1853
+ const properties = sdtPr ? parseSdtPr(sdtPr) : {};
1854
+ let endProperties;
1855
+ const sdtEndPr = findChild(el, "w:sdtEndPr");
1856
+ if (sdtEndPr) endProperties = parseRunProperties(sdtEndPr);
1857
+ const sdtContent = findChild(el, "w:sdtContent");
1858
+ let childList;
1859
+ if (sdtContent && sdtContent.elements?.length) {
1860
+ childList = parseBodyChildren(sdtContent.elements, dctx);
1861
+ if (childList.length === 0) childList = void 0;
1862
+ }
1863
+ return {
1864
+ properties,
1865
+ children: childList,
1866
+ endProperties
1867
+ };
1868
+ }
1869
+ };
1870
+ function buildCustomXmlPrXml(pr) {
1871
+ const parts = ["<w:customXmlPr>"];
1872
+ if (pr.placeholder !== void 0) parts.push(`<w:placeholder w:val="${escapeAttr(pr.placeholder)}"/>`);
1873
+ if (pr.attributes) for (const attr of pr.attributes) {
1874
+ const attrParts = [`w:name="${escapeAttr(attr.name)}"`, `w:val="${escapeAttr(attr.val)}"`];
1875
+ if (attr.uri !== void 0) attrParts.push(`w:uri="${escapeAttr(attr.uri)}"`);
1876
+ parts.push(`<w:attr ${attrParts.join(" ")}/>`);
1877
+ }
1878
+ parts.push("</w:customXmlPr>");
1879
+ return parts.join("");
1880
+ }
1881
+ /**
1882
+ * Serialize the common customXml shell (element/uri/customXmlPr) wrapping
1883
+ * arbitrary content. Shared by all four customXml levels (block/run/row/cell).
1884
+ */
1885
+ function stringifyCustomXmlShell(opts, contentXml) {
1886
+ const attrs = [`w:element="${escapeAttr(opts.element)}"`];
1887
+ if (opts.uri !== void 0) attrs.push(`w:uri="${escapeAttr(opts.uri)}"`);
1888
+ const prXml = opts.customXmlPr ? buildCustomXmlPrXml(opts.customXmlPr) : "";
1889
+ return `<w:customXml ${attrs.join(" ")}>${prXml}${contentXml}</w:customXml>`;
1890
+ }
1891
+ const customXmlBlockDesc = {
1892
+ kind: "custom",
1893
+ stringify(opts, ctx) {
1894
+ const contentParts = [];
1895
+ if (opts.children) for (const child of opts.children) contentParts.push(ctx.stringifyChild(child, ctx));
1896
+ return stringifyCustomXmlShell(opts, contentParts.join(""));
1897
+ },
1898
+ parse(el, ctx) {
1899
+ const dctx = ctx;
1900
+ const opts = {};
1901
+ const element = attr(el, "w:element");
1902
+ if (element) opts.element = element;
1903
+ const uri = attr(el, "w:uri");
1904
+ if (uri) opts.uri = uri;
1905
+ const xmlPr = findChild(el, "w:customXmlPr");
1906
+ if (xmlPr) opts.customXmlPr = parseCustomXmlPr(xmlPr);
1907
+ const childList = [];
1908
+ for (const child of el.elements ?? []) {
1909
+ if (child.name === "w:customXmlPr") continue;
1910
+ if (_parseBodyChild) childList.push(_parseBodyChild(child, dctx));
1911
+ }
1912
+ if (childList.length > 0) opts.children = childList;
1913
+ return opts;
1914
+ }
1915
+ };
1916
+ //#endregion
1917
+ //#region src/parts/drawing/text-wrap/text-wrapping.ts
1918
+ /**
1919
+ * Enumeration of text wrapping types for floating drawings.
1920
+ *
1921
+ * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
1922
+ *
1923
+ * @publicApi
1924
+ */
1925
+ const TextWrappingType = {
1926
+ NONE: 0,
1927
+ SQUARE: 1,
1928
+ TIGHT: 2,
1929
+ TOP_AND_BOTTOM: 3,
1930
+ THROUGH: 4
1931
+ };
1932
+ /**
1933
+ * Enumeration of text wrapping sides for floating drawings.
1934
+ *
1935
+ * Specifies on which side(s) text can wrap around the drawing.
1936
+ *
1937
+ * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
1938
+ *
1939
+ * @publicApi
1940
+ */
1941
+ const TextWrappingSide = {
1942
+ /** Text wraps on both sides of the drawing */
1943
+ BOTH_SIDES: "bothSides",
1944
+ /** Text wraps only on the left side */
1945
+ LEFT: "left",
1946
+ /** Text wraps only on the right side */
1947
+ RIGHT: "right",
1948
+ /** Text wraps on the side with more space */
1949
+ LARGEST: "largest"
1950
+ };
1951
+ //#endregion
1952
+ //#region src/parts/drawing/text-wrap/wrap-tight.ts
1953
+ /**
1954
+ * Wrap Tight module for DrawingML text wrapping.
1955
+ *
1956
+ * This module provides tight text wrapping for floating drawings
1957
+ * where text wraps closely around the image shape.
1958
+ *
1959
+ * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
1960
+ *
1961
+ * @module
1962
+ */
1963
+ /**
1964
+ * Creates a default rectangular wrap polygon matching the image extent.
1965
+ *
1966
+ * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
1967
+ *
1968
+ * ## XSD Schema
1969
+ * ```xml
1970
+ * <xsd:complexType name="CT_WrapPath">
1971
+ * <xsd:sequence>
1972
+ * <xsd:element name="start" type="a:CT_Point2D" minOccurs="1" maxOccurs="1"/>
1973
+ * <xsd:element name="lineTo" type="a:CT_Point2D" minOccurs="2" maxOccurs="unbounded"/>
1974
+ * </xsd:sequence>
1975
+ * <xsd:attribute name="edited" type="xsd:boolean" use="optional"/>
1976
+ * </xsd:complexType>
1977
+ * ```
1978
+ */
1979
+ const createWrapPolygon$1 = (cx, cy) => element("wp:wrapPolygon", { edited: "0" }, [
1980
+ `<wp:start x="0" y="0"/>`,
1981
+ `<wp:lineTo x="0" y="${-cy}"/>`,
1982
+ `<wp:lineTo x="${cx}" y="${-cy}"/>`,
1983
+ `<wp:lineTo x="${cx}" y="0"/>`,
1984
+ `<wp:lineTo x="0" y="0"/>`
1985
+ ]);
1986
+ /**
1987
+ * Creates tight text wrapping for a floating drawing.
1988
+ *
1989
+ * WrapTight causes text to wrap closely around the contours
1990
+ * of the drawing rather than its rectangular bounding box.
1991
+ * A default rectangular wrap polygon matching the image extent is generated.
1992
+ *
1993
+ * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
1994
+ *
1995
+ * ## XSD Schema
1996
+ * ```xml
1997
+ * <xsd:complexType name="CT_WrapTight">
1998
+ * <xsd:sequence>
1999
+ * <xsd:element name="wrapPolygon" type="CT_WrapPath" minOccurs="1" maxOccurs="1"/>
2000
+ * </xsd:sequence>
2001
+ * <xsd:attribute name="wrapText" type="ST_WrapText" use="required"/>
2002
+ * <xsd:attribute name="distL" type="ST_WrapDistance"/>
2003
+ * <xsd:attribute name="distR" type="ST_WrapDistance"/>
2004
+ * </xsd:complexType>
2005
+ * ```
2006
+ */
2007
+ const createWrapTight = (textWrapping, margins = {
2008
+ bottom: 0,
2009
+ left: 0,
2010
+ right: 0,
2011
+ top: 0
2012
+ }, extent) => element("wp:wrapTight", {
2013
+ distL: margins.left,
2014
+ distR: margins.right,
2015
+ wrapText: textWrapping.side || TextWrappingSide.BOTH_SIDES
2016
+ }, [createWrapPolygon$1(extent.x, extent.y)]);
2017
+ //#endregion
2018
+ //#region src/parts/drawing/text-wrap/wrap-through.ts
2019
+ /**
2020
+ * Wrap Through module for DrawingML text wrapping.
2021
+ *
2022
+ * This module provides "through" text wrapping for floating drawings
2023
+ * where text wraps through the image contours, filling any concave areas.
2024
+ *
2025
+ * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
2026
+ *
2027
+ * @module
2028
+ */
2029
+ /**
2030
+ * Creates a default rectangular wrap polygon matching the image extent.
2031
+ *
2032
+ * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
2033
+ *
2034
+ * ## XSD Schema
2035
+ * ```xml
2036
+ * <xsd:complexType name="CT_WrapPath">
2037
+ * <xsd:sequence>
2038
+ * <xsd:element name="start" type="a:CT_Point2D" minOccurs="1" maxOccurs="1"/>
2039
+ * <xsd:element name="lineTo" type="a:CT_Point2D" minOccurs="2" maxOccurs="unbounded"/>
2040
+ * </xsd:sequence>
2041
+ * <xsd:attribute name="edited" type="xsd:boolean" use="optional"/>
2042
+ * </xsd:complexType>
2043
+ * ```
2044
+ */
2045
+ const createWrapPolygon = (cx, cy) => element("wp:wrapPolygon", { edited: "0" }, [
2046
+ `<wp:start x="0" y="0"/>`,
2047
+ `<wp:lineTo x="0" y="${-cy}"/>`,
2048
+ `<wp:lineTo x="${cx}" y="${-cy}"/>`,
2049
+ `<wp:lineTo x="${cx}" y="0"/>`,
2050
+ `<wp:lineTo x="0" y="0"/>`
2051
+ ]);
2052
+ /**
2053
+ * Creates "through" text wrapping for a floating drawing.
2054
+ *
2055
+ * WrapThrough is similar to WrapTight but allows text to wrap through
2056
+ * the concave portions of the drawing shape (e.g., the inside of the letter "O").
2057
+ * A default rectangular wrap polygon matching the image extent is generated.
2058
+ *
2059
+ * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
2060
+ *
2061
+ * ## XSD Schema
2062
+ * ```xml
2063
+ * <xsd:complexType name="CT_WrapThrough">
2064
+ * <xsd:sequence>
2065
+ * <xsd:element name="wrapPolygon" type="CT_WrapPath" minOccurs="1" maxOccurs="1"/>
2066
+ * </xsd:sequence>
2067
+ * <xsd:attribute name="wrapText" type="ST_WrapText" use="required"/>
2068
+ * <xsd:attribute name="distL" type="ST_WrapDistance"/>
2069
+ * <xsd:attribute name="distR" type="ST_WrapDistance"/>
2070
+ * </xsd:complexType>
2071
+ * ```
2072
+ */
2073
+ const createWrapThrough = (textWrapping, margins = {
2074
+ bottom: 0,
2075
+ left: 0,
2076
+ right: 0,
2077
+ top: 0
2078
+ }, extent) => element("wp:wrapThrough", {
2079
+ distL: margins.left,
2080
+ distR: margins.right,
2081
+ wrapText: textWrapping.side || TextWrappingSide.BOTH_SIDES
2082
+ }, [createWrapPolygon(extent.x, extent.y)]);
2083
+ //#endregion
2084
+ //#region src/shared/constants.ts
2085
+ /**
2086
+ * Shared constants for WordprocessingML documents.
2087
+ *
2088
+ * Provides alignment, number format, and space type constants
2089
+ * used across multiple document components.
2090
+ *
2091
+ * @module
2092
+ */
2093
+ /**
2094
+ * Horizontal alignment options for floating drawings.
2095
+ *
2096
+ * Reference: https://www.datypic.com/sc/ooxml/t-wp_ST_AlignH.html
2097
+ *
2098
+ * @publicApi
2099
+ */
2100
+ const HorizontalPositionAlign = {
2101
+ CENTER: "center",
2102
+ INSIDE: "inside",
2103
+ LEFT: "left",
2104
+ OUTSIDE: "outside",
2105
+ RIGHT: "right"
2106
+ };
2107
+ /**
2108
+ * Vertical alignment options for floating drawings.
2109
+ *
2110
+ * Reference: https://www.datypic.com/sc/ooxml/t-wp_ST_AlignV.html
2111
+ *
2112
+ * @publicApi
2113
+ */
2114
+ const VerticalPositionAlign = {
2115
+ BOTTOM: "bottom",
2116
+ CENTER: "center",
2117
+ INSIDE: "inside",
836
2118
  OUTSIDE: "outside",
837
2119
  TOP: "top"
838
2120
  };
@@ -2081,681 +3363,304 @@ function stringifyMathInput(value) {
2081
3363
  let pr = "";
2082
3364
  if (opts.properties) {
2083
3365
  const p = opts.properties;
2084
- const parts = [];
2085
- if (p.baseJc) parts.push(`<m:baseJc m:val="${p.baseJc}"/>`);
2086
- if (p.maxDist) parts.push("<m:maxDist m:val=\"1\"/>");
2087
- if (p.objDist) parts.push("<m:objDist m:val=\"1\"/>");
2088
- if (p.rSpRule) parts.push(`<m:rSpRule m:val="${p.rSpRule}"/>`);
2089
- if (p.rSp) parts.push(`<m:rSp m:val="${p.rSp}"/>`);
2090
- if (parts.length) pr = `<m:eqArrPr>${parts.join("")}</m:eqArrPr>`;
2091
- }
2092
- const rows = opts.rows.map((row) => `<m:e>${stringifyChildren(row)}</m:e>`).join("");
2093
- return `<m:eqArr>${pr}${rows}</m:eqArr>`;
2094
- }
2095
- if ("accent" in value) {
2096
- const opts = value.accent;
2097
- return `<m:acc>${opts.accentCharacter ? `<m:accPr><m:chr m:val="${opts.accentCharacter}"/></m:accPr>` : ""}<m:e>${stringifyChildren(opts.children)}</m:e></m:acc>`;
2098
- }
2099
- if ("bar" in value) {
2100
- const opts = value.bar;
2101
- return `<m:bar><m:barPr><m:pos m:val="${opts.type}"/></m:barPr><m:e>${stringifyChildren(opts.children)}</m:e></m:bar>`;
2102
- }
2103
- if ("text" in value) return `<m:r>${value.properties ? mathRunPropsStr(value.properties) : ""}<m:t>${escapeXml(value.text)}</m:t></m:r>`;
2104
- return "";
2105
- }
2106
- function stringifyNAry(opts, chr) {
2107
- const hasSub = opts.subScript && opts.subScript.length > 0;
2108
- const hasSup = opts.superScript && opts.superScript.length > 0;
2109
- const prParts = [`<m:chr m:val="${chr}"/>`];
2110
- if (!hasSub) prParts.push("<m:subHide m:val=\"1\"/>");
2111
- if (!hasSup) prParts.push("<m:supHide m:val=\"1\"/>");
2112
- return `<m:nary>${`<m:naryPr>${prParts.join("")}</m:naryPr>`}${hasSub ? `<m:sub>${stringifyChildren(opts.subScript)}</m:sub>` : "<m:sub/>"}${hasSup ? `<m:sup>${stringifyChildren(opts.superScript)}</m:sup>` : "<m:sup/>"}<m:e>${stringifyChildren(opts.children)}</m:e></m:nary>`;
2113
- }
2114
- function stringifyDelimiters(children, begChr, endChr) {
2115
- return `<m:d><m:dPr><m:begChr m:val="${begChr}"/><m:endChr m:val="${endChr}"/></m:dPr><m:e>${stringifyChildren(children)}</m:e></m:d>`;
2116
- }
2117
- function bracketChildren(v) {
2118
- if (Array.isArray(v)) return v;
2119
- return v.children;
2120
- }
2121
- function stringifyMath(children) {
2122
- return `<m:oMath>${children.map((c) => stringifyMathInput(c)).join("")}</m:oMath>`;
2123
- }
2124
- /**
2125
- * Parse all math children from an m:oMath (or similar container) element.
2126
- */
2127
- function parseMathChildren(el) {
2128
- const result = [];
2129
- for (const child of el.elements ?? []) {
2130
- const parsed = parseMathElement(child);
2131
- if (parsed !== void 0) result.push(parsed);
2132
- }
2133
- return result;
2134
- }
2135
- function parseMathElement(el) {
2136
- switch (el.name) {
2137
- case "m:r": return parseMathRun(el);
2138
- case "m:f": return parseMathFraction(el);
2139
- case "m:rad": return parseMathRadical(el);
2140
- case "m:sSup": return parseMathSuperScript(el);
2141
- case "m:sSub": return parseMathSubScript(el);
2142
- case "m:sSubSup": return parseMathSubSuperScript(el);
2143
- case "m:nary": return parseMathNAry(el);
2144
- case "m:func": return parseMathFunction(el);
2145
- case "m:d": return parseMathDelimiter(el);
2146
- case "m:m": return parseMathMatrix(el);
2147
- case "m:acc": return parseMathAccent(el);
2148
- case "m:bar": return parseMathBar(el);
2149
- case "m:borderBox": return { borderBox: { children: parseMathArg(el, "m:e") } };
2150
- case "m:box": return { box: { children: parseMathArg(el, "m:e") } };
2151
- case "m:groupChr": return { groupChr: { children: parseMathArg(el, "m:e") } };
2152
- case "m:phant": return { phant: { children: parseMathArg(el, "m:e") } };
2153
- case "m:eqArr": return parseMathEqArr(el);
2154
- case "m:limLow": return parseMathLimitLower(el);
2155
- case "m:limUpp": return parseMathLimitUpper(el);
2156
- case "m:rPr":
2157
- case "m:fPr":
2158
- case "m:radPr":
2159
- case "m:sSupPr":
2160
- case "m:sSubPr":
2161
- case "m:sSubSupPr":
2162
- case "m:naryPr":
2163
- case "m:funcPr":
2164
- case "m:dPr":
2165
- case "m:mPr":
2166
- case "m:accPr":
2167
- case "m:barPr":
2168
- case "m:borderBoxPr":
2169
- case "m:boxPr":
2170
- case "m:groupChrPr":
2171
- case "m:phantPr":
2172
- case "m:eqArrPr":
2173
- case "m:limLowPr":
2174
- case "m:limUppPr":
2175
- case "m:ctrlPr": return;
2176
- default: return;
3366
+ const parts = [];
3367
+ if (p.baseJc) parts.push(`<m:baseJc m:val="${p.baseJc}"/>`);
3368
+ if (p.maxDist) parts.push("<m:maxDist m:val=\"1\"/>");
3369
+ if (p.objDist) parts.push("<m:objDist m:val=\"1\"/>");
3370
+ if (p.rSpRule) parts.push(`<m:rSpRule m:val="${p.rSpRule}"/>`);
3371
+ if (p.rSp) parts.push(`<m:rSp m:val="${p.rSp}"/>`);
3372
+ if (parts.length) pr = `<m:eqArrPr>${parts.join("")}</m:eqArrPr>`;
3373
+ }
3374
+ const rows = opts.rows.map((row) => `<m:e>${stringifyChildren(row)}</m:e>`).join("");
3375
+ return `<m:eqArr>${pr}${rows}</m:eqArr>`;
2177
3376
  }
2178
- }
2179
- function parseMathRun(el) {
2180
- return textOf(findChild(el, "m:t")) ?? "";
2181
- }
2182
- function parseMathFraction(el) {
2183
- return { fraction: {
2184
- numerator: parseMathArg(el, "m:num"),
2185
- denominator: parseMathArg(el, "m:den")
2186
- } };
2187
- }
2188
- function parseMathRadical(el) {
2189
- const degree = parseMathArg(el, "m:deg");
2190
- return { radical: {
2191
- children: parseMathArg(el, "m:e"),
2192
- ...degree.length > 0 ? { degree } : {}
2193
- } };
2194
- }
2195
- function parseMathSuperScript(el) {
2196
- return { superScript: {
2197
- children: parseMathArg(el, "m:e"),
2198
- superScript: parseMathArg(el, "m:sup")
2199
- } };
2200
- }
2201
- function parseMathSubScript(el) {
2202
- return { subScript: {
2203
- children: parseMathArg(el, "m:e"),
2204
- subScript: parseMathArg(el, "m:sub")
2205
- } };
2206
- }
2207
- function parseMathSubSuperScript(el) {
2208
- return { subSuperScript: {
2209
- children: parseMathArg(el, "m:e"),
2210
- subScript: parseMathArg(el, "m:sub"),
2211
- superScript: parseMathArg(el, "m:sup")
2212
- } };
2213
- }
2214
- function parseMathNAry(el) {
2215
- const naryPr = findChild(el, "m:naryPr");
2216
- const chrEl = naryPr ? findChild(naryPr, "m:chr") : void 0;
2217
- const chrVal = chrEl ? attr(chrEl, "m:val") : void 0;
2218
- const baseChildren = parseMathArg(el, "m:e");
2219
- const sub = parseMathArg(el, "m:sub");
2220
- const sup = parseMathArg(el, "m:sup");
2221
- const common = {
2222
- children: baseChildren,
2223
- ...sub.length > 0 ? { subScript: sub } : {},
2224
- ...sup.length > 0 ? { superScript: sup } : {}
2225
- };
2226
- if (chrVal === "∑") return { sum: common };
2227
- return { integral: common };
2228
- }
2229
- function parseMathFunction(el) {
2230
- return { function: {
2231
- name: parseMathArg(el, "m:fName"),
2232
- children: parseMathArg(el, "m:e")
2233
- } };
2234
- }
2235
- function parseMathDelimiter(el) {
2236
- const dPr = findChild(el, "m:dPr");
2237
- const begChrEl = dPr ? findChild(dPr, "m:begChr") : void 0;
2238
- const begChr = begChrEl ? attr(begChrEl, "m:val") : "(";
2239
- const mathChildren = parseMathArg(el, "m:e");
2240
- switch (begChr) {
2241
- case "[": return { squareBrackets: mathChildren };
2242
- case "{": return { curlyBrackets: mathChildren };
2243
- case "<":
2244
- case "⟨": return { angledBrackets: mathChildren };
2245
- default: return { roundBrackets: mathChildren };
3377
+ if ("accent" in value) {
3378
+ const opts = value.accent;
3379
+ return `<m:acc>${opts.accentCharacter ? `<m:accPr><m:chr m:val="${opts.accentCharacter}"/></m:accPr>` : ""}<m:e>${stringifyChildren(opts.children)}</m:e></m:acc>`;
2246
3380
  }
3381
+ if ("bar" in value) {
3382
+ const opts = value.bar;
3383
+ return `<m:bar><m:barPr><m:pos m:val="${opts.type}"/></m:barPr><m:e>${stringifyChildren(opts.children)}</m:e></m:bar>`;
3384
+ }
3385
+ if ("text" in value) return `<m:r>${value.properties ? mathRunPropsStr(value.properties) : ""}<m:t>${escapeXml(value.text)}</m:t></m:r>`;
3386
+ return "";
2247
3387
  }
2248
- function parseMathMatrix(el) {
2249
- const rows = [];
2250
- for (const mr of children(el, "m:mr")) rows.push(parseMathArg(mr, "m:e"));
2251
- return { matrix: { rows } };
2252
- }
2253
- function parseMathAccent(el) {
2254
- const accPr = findChild(el, "m:accPr");
2255
- const chrEl = accPr ? findChild(accPr, "m:chr") : void 0;
2256
- const accentChar = chrEl ? attr(chrEl, "m:val") : void 0;
2257
- return { accent: {
2258
- children: parseMathArg(el, "m:e"),
2259
- ...accentChar ? { accentCharacter: accentChar } : {}
2260
- } };
2261
- }
2262
- function parseMathBar(el) {
2263
- const barPr = findChild(el, "m:barPr");
2264
- const posEl = barPr ? findChild(barPr, "m:pos") : void 0;
2265
- const pos = posEl ? attr(posEl, "m:val") : "top";
2266
- return { bar: {
2267
- children: parseMathArg(el, "m:e"),
2268
- type: pos ?? "top"
2269
- } };
2270
- }
2271
- function parseMathEqArr(el) {
2272
- const rows = [];
2273
- for (const e of children(el, "m:e")) rows.push(parseMathChildren(e));
2274
- return { eqArr: { rows } };
3388
+ function stringifyNAry(opts, chr) {
3389
+ const hasSub = opts.subScript && opts.subScript.length > 0;
3390
+ const hasSup = opts.superScript && opts.superScript.length > 0;
3391
+ const prParts = [`<m:chr m:val="${chr}"/>`];
3392
+ if (!hasSub) prParts.push("<m:subHide m:val=\"1\"/>");
3393
+ if (!hasSup) prParts.push("<m:supHide m:val=\"1\"/>");
3394
+ return `<m:nary>${`<m:naryPr>${prParts.join("")}</m:naryPr>`}${hasSub ? `<m:sub>${stringifyChildren(opts.subScript)}</m:sub>` : "<m:sub/>"}${hasSup ? `<m:sup>${stringifyChildren(opts.superScript)}</m:sup>` : "<m:sup/>"}<m:e>${stringifyChildren(opts.children)}</m:e></m:nary>`;
2275
3395
  }
2276
- function parseMathLimitLower(el) {
2277
- return { limitLower: {
2278
- children: parseMathArg(el, "m:e"),
2279
- limit: parseMathArg(el, "m:lim")
2280
- } };
3396
+ function stringifyDelimiters(children, begChr, endChr) {
3397
+ return `<m:d><m:dPr><m:begChr m:val="${begChr}"/><m:endChr m:val="${endChr}"/></m:dPr><m:e>${stringifyChildren(children)}</m:e></m:d>`;
2281
3398
  }
2282
- function parseMathLimitUpper(el) {
2283
- return { limitUpper: {
2284
- children: parseMathArg(el, "m:e"),
2285
- limit: parseMathArg(el, "m:lim")
2286
- } };
3399
+ function bracketChildren(v) {
3400
+ if (Array.isArray(v)) return v;
3401
+ return v.children;
2287
3402
  }
2288
- function parseMathArg(parent, childName) {
2289
- const container = findChild(parent, childName);
2290
- if (!container) return [];
2291
- return parseMathChildren(container);
3403
+ function stringifyMath(children) {
3404
+ return `<m:oMath>${children.map((c) => stringifyMathInput(c)).join("")}</m:oMath>`;
2292
3405
  }
2293
- //#endregion
2294
- //#region src/parts/paragraph/run/field.ts
2295
- /**
2296
- * Field module for WordprocessingML documents.
2297
- *
2298
- * This module provides support for complex fields, which are regions of text
2299
- * that can contain dynamic content such as page numbers, dates, or mail merge fields.
2300
- * Fields are delimited by field character elements (begin, separate, end).
2301
- *
2302
- * Reference: http://officeopenxml.com/WPrun.php
2303
- *
2304
- * @module
2305
- */
2306
- /**
2307
- * Field character types that delimit field regions.
2308
- *
2309
- * @internal
2310
- */
2311
- const FieldCharacterType = {
2312
- BEGIN: "begin",
2313
- END: "end",
2314
- SEPARATE: "separate"
2315
- };
2316
- /**
2317
- * Creates a field character element.
2318
- *
2319
- * ## XSD Schema
2320
- * ```xml
2321
- * <xsd:complexType name="CT_FldChar">
2322
- * <xsd:sequence>
2323
- * <xsd:element name="fldData" type="CT_Text" minOccurs="0"/>
2324
- * <xsd:element name="ffData" type="CT_FFData" minOccurs="0"/>
2325
- * <xsd:element name="numberingChange" type="CT_TrackChangeNumbering" minOccurs="0"/>
2326
- * </xsd:sequence>
2327
- * <xsd:attribute name="fldCharType" type="ST_FldCharType" use="required"/>
2328
- * <xsd:attribute name="fldLock" type="s:ST_OnOff"/>
2329
- * <xsd:attribute name="dirty" type="s:ST_OnOff"/>
2330
- * </xsd:complexType>
2331
- * ```
2332
- * @internal
2333
- */
2334
- const createFieldChar = (type, dirty, ffData, fldData, fieldLock) => {
2335
- const children = [];
2336
- if (fldData !== void 0) children.push(element("w:fldData", { "xml:space": "preserve" }, [fldData]));
2337
- if (ffData) children.push(ffData);
2338
- return element("w:fldChar", {
2339
- "w:dirty": dirty,
2340
- "w:fldLock": fieldLock,
2341
- "w:fldCharType": type
2342
- }, children.length > 0 ? children : void 0);
2343
- };
2344
- /**
2345
- * Creates the beginning of a complex field.
2346
- *
2347
- * The Begin element marks the start of a field. A field consists of a begin character,
2348
- * field instructions, an optional separate character, field result, and an end character.
2349
- *
2350
- * For form fields, pass `formField` to embed `w:ffData` within the begin `w:fldChar`.
2351
- *
2352
- * @param dirty - Whether the field should be recalculated
2353
- * @param formField - Optional form field data to embed in the begin character
2354
- *
2355
- * @example
2356
- * ```typescript
2357
- * // Simple field begin
2358
- * createBegin();
2359
- *
2360
- * // Form field (checkbox)
2361
- * createBegin(false, {
2362
- * name: "Check1",
2363
- * checkBox: { checked: true, sizeAuto: true },
2364
- * });
2365
- * ```
2366
- */
2367
- const createBegin = (dirty, formField, fieldLock) => createFieldChar(FieldCharacterType.BEGIN, dirty, formField ? createFormFieldData(formField) : void 0, void 0, fieldLock);
2368
- /**
2369
- * Creates the separator between field code and field result in a complex field.
2370
- *
2371
- * The Separate element divides the field code (instructions) from the field result
2372
- * (the computed value).
2373
- */
2374
- const createSeparate = (dirty) => createFieldChar(FieldCharacterType.SEPARATE, dirty);
2375
3406
  /**
2376
- * Creates the end of a complex field.
2377
- *
2378
- * The End element marks the end of a field. Every field that begins with a Begin
2379
- * element must be terminated with an End element.
2380
- */
2381
- const createEnd = (dirty) => createFieldChar(FieldCharacterType.END, dirty);
2382
- //#endregion
2383
- //#region src/parts/paragraph/stringify.ts
2384
- /**
2385
- * Direct XML string builders for paragraph and run properties.
2386
- *
2387
- * Replaces `buildParagraphProperties() + xml()` and `buildRunProperties() + xml()`
2388
- * with direct string concatenation — zero intermediate IXmlableObject allocation,
2389
- * zero recursive xml() traversal. Follows PPTX/XLSX pattern.
2390
- *
2391
- * @module
3407
+ * Parse all math children from an m:oMath (or similar container) element.
2392
3408
  */
2393
- /** On/off: `<w:name/>` for true, `<w:name w:val="0"/>` for false */
2394
- function onOff(name, val) {
2395
- return val ? `<${name}/>` : `<${name} w:val="0"/>`;
3409
+ function parseMathChildren(el) {
3410
+ const result = [];
3411
+ for (const child of el.elements ?? []) {
3412
+ const parsed = parseMathElement(child);
3413
+ if (parsed !== void 0) result.push(parsed);
3414
+ }
3415
+ return result;
3416
+ }
3417
+ function parseMathElement(el) {
3418
+ switch (el.name) {
3419
+ case "m:r": return parseMathRun(el);
3420
+ case "m:f": return parseMathFraction(el);
3421
+ case "m:rad": return parseMathRadical(el);
3422
+ case "m:sSup": return parseMathSuperScript(el);
3423
+ case "m:sSub": return parseMathSubScript(el);
3424
+ case "m:sSubSup": return parseMathSubSuperScript(el);
3425
+ case "m:nary": return parseMathNAry(el);
3426
+ case "m:func": return parseMathFunction(el);
3427
+ case "m:d": return parseMathDelimiter(el);
3428
+ case "m:m": return parseMathMatrix(el);
3429
+ case "m:acc": return parseMathAccent(el);
3430
+ case "m:bar": return parseMathBar(el);
3431
+ case "m:borderBox": return { borderBox: { children: parseMathArg(el, "m:e") } };
3432
+ case "m:box": return { box: { children: parseMathArg(el, "m:e") } };
3433
+ case "m:groupChr": return { groupChr: { children: parseMathArg(el, "m:e") } };
3434
+ case "m:phant": return { phant: { children: parseMathArg(el, "m:e") } };
3435
+ case "m:eqArr": return parseMathEqArr(el);
3436
+ case "m:limLow": return parseMathLimitLower(el);
3437
+ case "m:limUpp": return parseMathLimitUpper(el);
3438
+ case "m:rPr":
3439
+ case "m:fPr":
3440
+ case "m:radPr":
3441
+ case "m:sSupPr":
3442
+ case "m:sSubPr":
3443
+ case "m:sSubSupPr":
3444
+ case "m:naryPr":
3445
+ case "m:funcPr":
3446
+ case "m:dPr":
3447
+ case "m:mPr":
3448
+ case "m:accPr":
3449
+ case "m:barPr":
3450
+ case "m:borderBoxPr":
3451
+ case "m:boxPr":
3452
+ case "m:groupChrPr":
3453
+ case "m:phantPr":
3454
+ case "m:eqArrPr":
3455
+ case "m:limLowPr":
3456
+ case "m:limUppPr":
3457
+ case "m:ctrlPr": return;
3458
+ default: return;
3459
+ }
2396
3460
  }
2397
- /** Build attrs string from key-value pairs, skipping undefined */
2398
- function attrParts(attrs) {
2399
- const parts = [];
2400
- for (const [key, val] of Object.entries(attrs)) if (val !== void 0) parts.push(`${key}="${val}"`);
2401
- return parts.join(" ");
3461
+ function parseMathRun(el) {
3462
+ return textOf(findChild(el, "m:t")) ?? "";
2402
3463
  }
2403
- function borderStr(name, opts) {
2404
- return `<${name} ${attrParts({
2405
- "w:val": opts.style,
2406
- "w:color": opts.color !== void 0 ? hexColorValue(opts.color) : void 0,
2407
- "w:sz": opts.size !== void 0 ? eighthPointMeasureValue(opts.size) : void 0,
2408
- "w:space": opts.space !== void 0 ? pointMeasureValue(opts.space) : void 0,
2409
- "w:themeColor": opts.themeColor,
2410
- "w:themeTint": opts.themeTint !== void 0 ? uCharHexNumber(opts.themeTint) : void 0,
2411
- "w:themeShade": opts.themeShade !== void 0 ? uCharHexNumber(opts.themeShade) : void 0,
2412
- "w:shadow": opts.shadow !== void 0 ? opts.shadow ? 1 : 0 : void 0,
2413
- "w:frame": opts.frame !== void 0 ? opts.frame ? 1 : 0 : void 0
2414
- })}/>`;
3464
+ function parseMathFraction(el) {
3465
+ return { fraction: {
3466
+ numerator: parseMathArg(el, "m:num"),
3467
+ denominator: parseMathArg(el, "m:den")
3468
+ } };
2415
3469
  }
2416
- function shadingStr(opts) {
2417
- return `<w:shd ${attrParts({
2418
- "w:val": opts.type ?? "clear",
2419
- "w:color": opts.color !== void 0 ? hexColorValue(opts.color) : void 0,
2420
- "w:fill": opts.fill !== void 0 ? hexColorValue(opts.fill) : void 0,
2421
- "w:themeColor": opts.themeColor,
2422
- "w:themeTint": opts.themeTint !== void 0 ? uCharHexNumber(opts.themeTint) : void 0,
2423
- "w:themeShade": opts.themeShade !== void 0 ? uCharHexNumber(opts.themeShade) : void 0,
2424
- "w:themeFill": opts.themeFill,
2425
- "w:themeFillTint": opts.themeFillTint !== void 0 ? uCharHexNumber(opts.themeFillTint) : void 0,
2426
- "w:themeFillShade": opts.themeFillShade !== void 0 ? uCharHexNumber(opts.themeFillShade) : void 0
2427
- })}/>`;
3470
+ function parseMathRadical(el) {
3471
+ const degree = parseMathArg(el, "m:deg");
3472
+ return { radical: {
3473
+ children: parseMathArg(el, "m:e"),
3474
+ ...degree.length > 0 ? { degree } : {}
3475
+ } };
2428
3476
  }
2429
- function spacingStr(opts) {
2430
- return `<w:spacing ${attrParts({
2431
- "w:after": opts.after,
2432
- "w:afterAutospacing": opts.afterAutoSpacing !== void 0 ? opts.afterAutoSpacing ? 1 : 0 : void 0,
2433
- "w:afterLines": opts.afterLines !== void 0 ? decimalNumber(opts.afterLines) : void 0,
2434
- "w:before": opts.before,
2435
- "w:beforeAutospacing": opts.beforeAutoSpacing !== void 0 ? opts.beforeAutoSpacing ? 1 : 0 : void 0,
2436
- "w:beforeLines": opts.beforeLines !== void 0 ? decimalNumber(opts.beforeLines) : void 0,
2437
- "w:line": opts.line,
2438
- "w:lineRule": opts.lineRule
2439
- })}/>`;
3477
+ function parseMathSuperScript(el) {
3478
+ return { superScript: {
3479
+ children: parseMathArg(el, "m:e"),
3480
+ superScript: parseMathArg(el, "m:sup")
3481
+ } };
2440
3482
  }
2441
- function indentStr(opts) {
2442
- return `<w:ind ${attrParts({
2443
- "w:start": opts.start !== void 0 ? signedTwipsMeasureValue(opts.start) : void 0,
2444
- "w:startChars": opts.startChars !== void 0 ? decimalNumber(opts.startChars) : void 0,
2445
- "w:end": opts.end !== void 0 ? signedTwipsMeasureValue(opts.end) : void 0,
2446
- "w:endChars": opts.endChars !== void 0 ? decimalNumber(opts.endChars) : void 0,
2447
- "w:left": opts.left !== void 0 ? signedTwipsMeasureValue(opts.left) : void 0,
2448
- "w:leftChars": opts.leftChars !== void 0 ? decimalNumber(opts.leftChars) : void 0,
2449
- "w:right": opts.right !== void 0 ? signedTwipsMeasureValue(opts.right) : void 0,
2450
- "w:rightChars": opts.rightChars !== void 0 ? decimalNumber(opts.rightChars) : void 0,
2451
- "w:hanging": opts.hanging !== void 0 ? twipsMeasureValue(opts.hanging) : void 0,
2452
- "w:hangingChars": opts.hangingChars !== void 0 ? decimalNumber(opts.hangingChars) : void 0,
2453
- "w:firstLine": opts.firstLine !== void 0 ? twipsMeasureValue(opts.firstLine) : void 0,
2454
- "w:firstLineChars": opts.firstLineChars !== void 0 ? decimalNumber(opts.firstLineChars) : void 0
2455
- })}/>`;
3483
+ function parseMathSubScript(el) {
3484
+ return { subScript: {
3485
+ children: parseMathArg(el, "m:e"),
3486
+ subScript: parseMathArg(el, "m:sub")
3487
+ } };
2456
3488
  }
2457
- function tabStopsStr(defs) {
2458
- return `<w:tabs>${defs.map(({ type, position, leader }) => {
2459
- return `<w:tab ${attrParts({
2460
- "w:val": type,
2461
- "w:pos": position,
2462
- "w:leader": leader
2463
- })}/>`;
2464
- }).join("")}</w:tabs>`;
3489
+ function parseMathSubSuperScript(el) {
3490
+ return { subSuperScript: {
3491
+ children: parseMathArg(el, "m:e"),
3492
+ subScript: parseMathArg(el, "m:sub"),
3493
+ superScript: parseMathArg(el, "m:sup")
3494
+ } };
2465
3495
  }
2466
- function cnfStyleStr(opts) {
2467
- return `<w:cnfStyle ${attrParts({
2468
- "w:firstRow": opts.firstRow ? "1" : "0",
2469
- "w:lastRow": opts.lastRow ? "1" : "0",
2470
- "w:firstColumn": opts.firstColumn ? "1" : "0",
2471
- "w:lastColumn": opts.lastColumn ? "1" : "0",
2472
- "w:oddVBand": opts.oddVBand ? "1" : "0",
2473
- "w:evenVBand": opts.evenVBand ? "1" : "0",
2474
- "w:oddHBand": opts.oddHBand ? "1" : "0",
2475
- "w:evenHBand": opts.evenHBand ? "1" : "0",
2476
- "w:firstRowFirstColumn": opts.firstRowFirstColumn ? "1" : "0",
2477
- "w:firstRowLastColumn": opts.firstRowLastColumn ? "1" : "0",
2478
- "w:lastRowFirstColumn": opts.lastRowFirstColumn ? "1" : "0",
2479
- "w:lastRowLastColumn": opts.lastRowLastColumn ? "1" : "0"
2480
- })}/>`;
3496
+ function parseMathNAry(el) {
3497
+ const naryPr = findChild(el, "m:naryPr");
3498
+ const chrEl = naryPr ? findChild(naryPr, "m:chr") : void 0;
3499
+ const chrVal = chrEl ? attr(chrEl, "m:val") : void 0;
3500
+ const baseChildren = parseMathArg(el, "m:e");
3501
+ const sub = parseMathArg(el, "m:sub");
3502
+ const sup = parseMathArg(el, "m:sup");
3503
+ const common = {
3504
+ children: baseChildren,
3505
+ ...sub.length > 0 ? { subScript: sub } : {},
3506
+ ...sup.length > 0 ? { superScript: sup } : {}
3507
+ };
3508
+ if (chrVal === "") return { sum: common };
3509
+ return { integral: common };
2481
3510
  }
2482
- function framePrStr(opts) {
2483
- const alignment = opts.alignment;
2484
- const position = opts.position;
2485
- return `<w:framePr ${attrParts({
2486
- "w:xAlign": alignment?.x,
2487
- "w:yAlign": alignment?.y,
2488
- "w:hAnchor": opts.anchor?.horizontal,
2489
- "w:anchorLock": opts.anchorLock,
2490
- "w:vAnchor": opts.anchor?.vertical,
2491
- "w:dropCap": opts.dropCap,
2492
- "w:h": opts.height,
2493
- "w:lines": opts.lines,
2494
- "w:hRule": opts.rule,
2495
- "w:hSpace": opts.space?.horizontal,
2496
- "w:vSpace": opts.space?.vertical,
2497
- "w:w": opts.width,
2498
- "w:wrap": opts.wrap,
2499
- "w:x": position?.x,
2500
- "w:y": position?.y
2501
- })}/>`;
3511
+ function parseMathFunction(el) {
3512
+ return { function: {
3513
+ name: parseMathArg(el, "m:fName"),
3514
+ children: parseMathArg(el, "m:e")
3515
+ } };
2502
3516
  }
2503
- function numPrStr(numberId, indentLevel, numberingChange) {
2504
- const idVal = typeof numberId === "string" ? `{${numberId}}` : numberId;
2505
- const parts = [`<w:ilvl w:val="${Math.min(indentLevel, 9)}"/>`, `<w:numId w:val="${idVal}"/>`];
2506
- if (numberingChange) {
2507
- const a = attrParts({
2508
- "w:original": numberingChange.original,
2509
- "w:id": numberingChange.id,
2510
- "w:author": numberingChange.author,
2511
- "w:date": numberingChange.date
2512
- });
2513
- parts.push(`<w:numberingChange ${a}/>`);
3517
+ function parseMathDelimiter(el) {
3518
+ const dPr = findChild(el, "m:dPr");
3519
+ const begChrEl = dPr ? findChild(dPr, "m:begChr") : void 0;
3520
+ const begChr = begChrEl ? attr(begChrEl, "m:val") : "(";
3521
+ const mathChildren = parseMathArg(el, "m:e");
3522
+ switch (begChr) {
3523
+ case "[": return { squareBrackets: mathChildren };
3524
+ case "{": return { curlyBrackets: mathChildren };
3525
+ case "<":
3526
+ case "⟨": return { angledBrackets: mathChildren };
3527
+ default: return { roundBrackets: mathChildren };
2514
3528
  }
2515
- return `<w:numPr>${parts.join("")}</w:numPr>`;
2516
3529
  }
2517
- function colorStr(colorOrOptions) {
2518
- if (typeof colorOrOptions === "string") return `<w:color w:val="${hexColorValue(colorOrOptions)}"/>`;
2519
- const opts = colorOrOptions;
2520
- return `<w:color ${attrParts({
2521
- "w:val": opts.val !== void 0 ? hexColorValue(opts.val) : void 0,
2522
- "w:themeColor": opts.themeColor,
2523
- "w:themeTint": opts.themeTint !== void 0 ? uCharHexNumber(opts.themeTint) : void 0,
2524
- "w:themeShade": opts.themeShade !== void 0 ? uCharHexNumber(opts.themeShade) : void 0
2525
- })}/>`;
3530
+ function parseMathMatrix(el) {
3531
+ const rows = [];
3532
+ for (const mr of children(el, "m:mr")) rows.push(parseMathArg(mr, "m:e"));
3533
+ return { matrix: { rows } };
2526
3534
  }
2527
- function runFontsStr(nameOrAttrs, hint) {
2528
- if (typeof nameOrAttrs === "string") return `<w:rFonts ${attrParts({
2529
- "w:ascii": nameOrAttrs,
2530
- "w:cs": nameOrAttrs,
2531
- "w:eastAsia": nameOrAttrs,
2532
- "w:hAnsi": nameOrAttrs,
2533
- "w:hint": hint
2534
- })}/>`;
2535
- const attrs = nameOrAttrs;
2536
- return `<w:rFonts ${attrParts({
2537
- "w:ascii": attrs.ascii,
2538
- "w:asciiTheme": attrs.asciiTheme,
2539
- "w:cs": attrs.cs,
2540
- "w:cstheme": attrs.cstheme,
2541
- "w:eastAsia": attrs.eastAsia,
2542
- "w:eastAsiaTheme": attrs.eastAsiaTheme,
2543
- "w:hAnsi": attrs.hAnsi,
2544
- "w:hAnsiTheme": attrs.hAnsiTheme,
2545
- "w:hint": attrs.hint
2546
- })}/>`;
3535
+ function parseMathAccent(el) {
3536
+ const accPr = findChild(el, "m:accPr");
3537
+ const chrEl = accPr ? findChild(accPr, "m:chr") : void 0;
3538
+ const accentChar = chrEl ? attr(chrEl, "m:val") : void 0;
3539
+ return { accent: {
3540
+ children: parseMathArg(el, "m:e"),
3541
+ ...accentChar ? { accentCharacter: accentChar } : {}
3542
+ } };
3543
+ }
3544
+ function parseMathBar(el) {
3545
+ const barPr = findChild(el, "m:barPr");
3546
+ const posEl = barPr ? findChild(barPr, "m:pos") : void 0;
3547
+ const pos = posEl ? attr(posEl, "m:val") : "top";
3548
+ return { bar: {
3549
+ children: parseMathArg(el, "m:e"),
3550
+ type: pos ?? "top"
3551
+ } };
2547
3552
  }
2548
- function underlineStr(type, color) {
2549
- return `<w:u ${attrParts({
2550
- "w:val": type ?? "single",
2551
- "w:color": color !== void 0 ? hexColorValue(color) : void 0
2552
- })}/>`;
3553
+ function parseMathEqArr(el) {
3554
+ const rows = [];
3555
+ for (const e of children(el, "m:e")) rows.push(parseMathChildren(e));
3556
+ return { eqArr: { rows } };
2553
3557
  }
2554
- function eastAsianLayoutStr(opts) {
2555
- return `<w:eastAsianLayout ${attrParts({
2556
- "w:id": opts.id !== void 0 ? decimalNumber(opts.id) : void 0,
2557
- "w:combine": opts.combine !== void 0 ? opts.combine ? 1 : 0 : void 0,
2558
- "w:combineBrackets": opts.combineBrackets,
2559
- "w:vert": opts.vert !== void 0 ? opts.vert ? 1 : 0 : void 0,
2560
- "w:vertCompress": opts.vertCompress !== void 0 ? opts.vertCompress ? 1 : 0 : void 0
2561
- })}/>`;
3558
+ function parseMathLimitLower(el) {
3559
+ return { limitLower: {
3560
+ children: parseMathArg(el, "m:e"),
3561
+ limit: parseMathArg(el, "m:lim")
3562
+ } };
2562
3563
  }
2563
- function languageStr(opts) {
2564
- return `<w:lang ${attrParts({
2565
- "w:val": opts.value,
2566
- "w:eastAsia": opts.eastAsia,
2567
- "w:bidi": opts.bidirectional
2568
- })}/>`;
3564
+ function parseMathLimitUpper(el) {
3565
+ return { limitUpper: {
3566
+ children: parseMathArg(el, "m:e"),
3567
+ limit: parseMathArg(el, "m:lim")
3568
+ } };
3569
+ }
3570
+ function parseMathArg(parent, childName) {
3571
+ const container = findChild(parent, childName);
3572
+ if (!container) return [];
3573
+ return parseMathChildren(container);
2569
3574
  }
3575
+ //#endregion
3576
+ //#region src/parts/paragraph/run/field.ts
2570
3577
  /**
2571
- * Build `<w:pPr>` XML string directly from options — zero IXmlableObject allocation.
3578
+ * Field module for WordprocessingML documents.
2572
3579
  *
2573
- * Replaces `buildParagraphProperties() + xml()` with a single-pass string builder.
3580
+ * This module provides support for complex fields, which are regions of text
3581
+ * that can contain dynamic content such as page numbers, dates, or mail merge fields.
3582
+ * Fields are delimited by field character elements (begin, separate, end).
3583
+ *
3584
+ * Reference: http://officeopenxml.com/WPrun.php
3585
+ *
3586
+ * @module
2574
3587
  */
2575
- function stringifyParagraphProperties(options) {
2576
- const numberingReferences = [];
2577
- if (!options) return {
2578
- xml: void 0,
2579
- numberingReferences
2580
- };
2581
- const parts = [];
2582
- if (options.heading) parts.push(`<w:pStyle w:val="${escapeXml(options.heading)}"/>`);
2583
- if (options.bullet) parts.push("<w:pStyle w:val=\"ListParagraph\"/>");
2584
- if (options.numbering) {
2585
- if (!options.style && !options.heading) {
2586
- if (!options.numbering.custom) parts.push("<w:pStyle w:val=\"ListParagraph\"/>");
2587
- }
2588
- }
2589
- if (options.style) parts.push(`<w:pStyle w:val="${escapeXml(options.style)}"/>`);
2590
- if (options.keepNext !== void 0) parts.push(onOff("w:keepNext", options.keepNext));
2591
- if (options.keepLines !== void 0) parts.push(onOff("w:keepLines", options.keepLines));
2592
- if (options.pageBreakBefore) parts.push("<w:pageBreakBefore/>");
2593
- if (options.frame) parts.push(framePrStr(options.frame));
2594
- if (options.widowControl !== void 0) parts.push(onOff("w:widowControl", options.widowControl));
2595
- if (options.bullet) parts.push(`<w:numPr><w:ilvl w:val="${Math.min(options.bullet.level, 9)}"/><w:numId w:val="1"/></w:numPr>`);
2596
- if (options.numbering) {
2597
- numberingReferences.push({
2598
- instance: options.numbering.instance ?? 0,
2599
- reference: options.numbering.reference
2600
- });
2601
- const numId = `${options.numbering.reference}-${options.numbering.instance ?? 0}`;
2602
- parts.push(numPrStr(numId, options.numbering.level, options.numbering.numberingChange));
2603
- } else if (options.numbering === false) parts.push(numPrStr(0, 0));
2604
- if (options.border) {
2605
- const bParts = [];
2606
- if (options.border.top) bParts.push(borderStr("w:top", options.border.top));
2607
- if (options.border.left) bParts.push(borderStr("w:left", options.border.left));
2608
- if (options.border.bottom) bParts.push(borderStr("w:bottom", options.border.bottom));
2609
- if (options.border.right) bParts.push(borderStr("w:right", options.border.right));
2610
- if (options.border.between) bParts.push(borderStr("w:between", options.border.between));
2611
- if (options.border.bar) bParts.push(borderStr("w:bar", options.border.bar));
2612
- if (bParts.length) parts.push(`<w:pBdr>${bParts.join("")}</w:pBdr>`);
2613
- }
2614
- if (options.thematicBreak) parts.push(`<w:pBdr>${borderStr("w:bottom", {
2615
- color: "auto",
2616
- size: 6,
2617
- space: 1,
2618
- style: BorderStyle.SINGLE
2619
- })}</w:pBdr>`);
2620
- if (options.shading) parts.push(shadingStr(options.shading));
2621
- if (options.wordWrap) parts.push("<w:wordWrap w:val=\"0\"/>");
2622
- if (options.overflowPunctuation) parts.push(onOff("w:overflowPunct", options.overflowPunctuation));
2623
- const tabDefs = [
2624
- ...options.rightTabStop !== void 0 ? [{
2625
- position: options.rightTabStop,
2626
- type: "right"
2627
- }] : [],
2628
- ...options.tabStops ? options.tabStops : [],
2629
- ...options.leftTabStop !== void 0 ? [{
2630
- position: options.leftTabStop,
2631
- type: "left"
2632
- }] : []
2633
- ];
2634
- if (tabDefs.length > 0) parts.push(tabStopsStr(tabDefs));
2635
- if (options.bidirectional !== void 0) parts.push(onOff("w:bidi", options.bidirectional));
2636
- if (options.spacing) parts.push(spacingStr(options.spacing));
2637
- if (options.indent) parts.push(indentStr(options.indent));
2638
- if (options.contextualSpacing !== void 0) parts.push(onOff("w:contextualSpacing", options.contextualSpacing));
2639
- if (options.alignment) parts.push(`<w:jc w:val="${options.alignment}"/>`);
2640
- if (options.outlineLevel !== void 0) parts.push(`<w:outlineLvl w:val="${options.outlineLevel}"/>`);
2641
- if (options.divId !== void 0) parts.push(`<w:divId w:val="${options.divId}"/>`);
2642
- if (options.cnfStyle) parts.push(cnfStyleStr(options.cnfStyle));
2643
- if (options.suppressLineNumbers !== void 0) parts.push(onOff("w:suppressLineNumbers", options.suppressLineNumbers));
2644
- if (options.autoSpaceEastAsianText !== void 0) parts.push(onOff("w:autoSpaceDN", options.autoSpaceEastAsianText));
2645
- if (options.suppressAutoHyphens !== void 0) parts.push(onOff("w:suppressAutoHyphens", options.suppressAutoHyphens));
2646
- if (options.adjustRightInd !== void 0) parts.push(onOff("w:adjustRightInd", options.adjustRightInd));
2647
- if (options.snapToGrid !== void 0) parts.push(onOff("w:snapToGrid", options.snapToGrid));
2648
- if (options.mirrorIndents !== void 0) parts.push(onOff("w:mirrorIndents", options.mirrorIndents));
2649
- if (options.kinsoku !== void 0) parts.push(onOff("w:kinsoku", options.kinsoku));
2650
- if (options.topLinePunct !== void 0) parts.push(onOff("w:topLinePunct", options.topLinePunct));
2651
- if (options.autoSpaceDE !== void 0) parts.push(onOff("w:autoSpaceDE", options.autoSpaceDE));
2652
- if (options.textAlignment !== void 0) parts.push(`<w:textAlignment w:val="${options.textAlignment}"/>`);
2653
- if (options.textboxTightWrap !== void 0) parts.push(`<w:textboxTightWrap w:val="${options.textboxTightWrap}"/>`);
2654
- if (options.textDirection !== void 0) parts.push(`<w:textDirection w:val="${options.textDirection}"/>`);
2655
- if (options.suppressOverlap !== void 0) parts.push(onOff("w:suppressOverlap", options.suppressOverlap));
2656
- if (options.run) {
2657
- const inner = stringifyRunPropertiesInner(options.run);
2658
- if (inner !== void 0) {
2659
- const extra = [];
2660
- const runOpts = options.run;
2661
- if (runOpts.insertion) {
2662
- const { id, author, date } = runOpts.insertion;
2663
- extra.push(`<w:ins w:id="${id}" w:author="${escapeXml(author)}" w:date="${date}"/>`);
2664
- }
2665
- if (runOpts.deletion) {
2666
- const { id, author, date } = runOpts.deletion;
2667
- extra.push(`<w:del w:id="${id}" w:author="${escapeXml(author)}" w:date="${date}"/>`);
2668
- }
2669
- const body = inner + extra.join("");
2670
- parts.push(`<w:rPr>${body}</w:rPr>`);
2671
- }
2672
- }
2673
- if (options.revision) {
2674
- const rev = options.revision;
2675
- const { author: _a, date: _d, id: _i, ...originalProps } = rev;
2676
- const inner = stringifyParagraphProperties({
2677
- ...originalProps,
2678
- includeIfEmpty: true
2679
- });
2680
- parts.push(`<w:pPrChange w:author="${escapeXml(rev.author)}" w:date="${rev.date}" w:id="${rev.id}">${inner.xml ?? "<w:pPr/>"}</w:pPrChange>`);
2681
- }
2682
- const body = parts.join("");
2683
- return {
2684
- xml: options.includeIfEmpty || body.length > 0 ? `<w:pPr>${body}</w:pPr>` : void 0,
2685
- numberingReferences
2686
- };
2687
- }
2688
3588
  /**
2689
- * Build the inner content of `<w:rPr>` as a string.
2690
- * Returns undefined if no properties are set.
3589
+ * Field character types that delimit field regions.
3590
+ *
3591
+ * @internal
2691
3592
  */
2692
- function stringifyRunPropertiesInner(opts) {
2693
- if (!opts) return void 0;
2694
- const parts = [];
2695
- if (opts.style) parts.push(`<w:rStyle w:val="${escapeXml(opts.style)}"/>`);
2696
- if (opts.font) if (typeof opts.font === "string") parts.push(runFontsStr(opts.font));
2697
- else if ("name" in opts.font) parts.push(runFontsStr(opts.font.name, opts.font.hint));
2698
- else parts.push(runFontsStr(opts.font));
2699
- if (opts.bold !== void 0) parts.push(onOff("w:b", opts.bold));
2700
- if ((opts.boldComplexScript === void 0 && opts.bold !== void 0 || opts.boldComplexScript) !== void 0) parts.push(onOff("w:bCs", opts.boldComplexScript ?? opts.bold));
2701
- if (opts.italic !== void 0) parts.push(onOff("w:i", opts.italic));
2702
- if ((opts.italicComplexScript === void 0 && opts.italic !== void 0 || opts.italicComplexScript) !== void 0) parts.push(onOff("w:iCs", opts.italicComplexScript ?? opts.italic));
2703
- if (opts.smallCaps !== void 0) parts.push(onOff("w:smallCaps", opts.smallCaps));
2704
- else if (opts.allCaps !== void 0) parts.push(onOff("w:caps", opts.allCaps));
2705
- if (opts.strike !== void 0) parts.push(onOff("w:strike", opts.strike));
2706
- if (opts.doubleStrike !== void 0) parts.push(onOff("w:dstrike", opts.doubleStrike));
2707
- if (opts.emboss !== void 0) parts.push(onOff("w:emboss", opts.emboss));
2708
- if (opts.imprint !== void 0) parts.push(onOff("w:imprint", opts.imprint));
2709
- if (opts.outline !== void 0) parts.push(onOff("w:outline", opts.outline));
2710
- if (opts.shadow !== void 0) parts.push(onOff("w:shadow", opts.shadow));
2711
- if (opts.webHidden !== void 0) parts.push(onOff("w:webHidden", opts.webHidden));
2712
- if (opts.noProof !== void 0) parts.push(onOff("w:noProof", opts.noProof));
2713
- if (opts.snapToGrid !== void 0) parts.push(onOff("w:snapToGrid", opts.snapToGrid));
2714
- if (opts.vanish) parts.push(onOff("w:vanish", opts.vanish));
2715
- if (opts.color) parts.push(colorStr(opts.color));
2716
- if (opts.characterSpacing) parts.push(`<w:spacing w:val="${signedTwipsMeasureValue(opts.characterSpacing)}"/>`);
2717
- if (opts.scale !== void 0) parts.push(`<w:w w:val="${opts.scale}"/>`);
2718
- if (opts.kern) parts.push(`<w:kern w:val="${hpsMeasureValue(opts.kern)}"/>`);
2719
- if (opts.position) parts.push(`<w:position w:val="${opts.position}"/>`);
2720
- if (opts.size !== void 0) parts.push(`<w:sz w:val="${hpsMeasureValue(opts.size * 2)}"/>`);
2721
- const szCs = opts.sizeComplexScript === void 0 || opts.sizeComplexScript === true ? opts.size : opts.sizeComplexScript;
2722
- if (szCs) parts.push(`<w:szCs w:val="${hpsMeasureValue(szCs * 2)}"/>`);
2723
- if (opts.highlight) parts.push(`<w:highlight w:val="${opts.highlight}"/>`);
2724
- if (opts.highlightComplexScript === true) {
2725
- if (opts.highlight) parts.push(`<w:highlightCs w:val="${opts.highlight}"/>`);
2726
- } else if (opts.highlightComplexScript !== void 0 && opts.highlightComplexScript !== false) parts.push(`<w:highlightCs w:val="${opts.highlightComplexScript}"/>`);
2727
- if (opts.underline) parts.push(underlineStr(opts.underline.type, opts.underline.color));
2728
- if (opts.effect) parts.push(`<w:effect w:val="${opts.effect}"/>`);
2729
- if (opts.border) parts.push(borderStr("w:bdr", opts.border));
2730
- if (opts.shading) parts.push(shadingStr(opts.shading));
2731
- if (opts.subScript) parts.push("<w:vertAlign w:val=\"subscript\"/>");
2732
- if (opts.superScript) parts.push("<w:vertAlign w:val=\"superscript\"/>");
2733
- if (opts.rightToLeft !== void 0) parts.push(onOff("w:rtl", opts.rightToLeft));
2734
- if (opts.emphasisMark) parts.push(`<w:em w:val="${opts.emphasisMark.type ?? "dot"}"/>`);
2735
- if (opts.language) parts.push(languageStr(opts.language));
2736
- if (opts.specVanish) parts.push("<w:specVanish/>");
2737
- if (opts.math) parts.push(onOff("w:oMath", opts.math));
2738
- if (opts.fitText !== void 0) parts.push(`<w:fitText w:val="${opts.fitText}"/>`);
2739
- if (opts.complexScript !== void 0) parts.push(onOff("w:cs", opts.complexScript));
2740
- if (opts.eastAsianLayout) parts.push(eastAsianLayoutStr(opts.eastAsianLayout));
2741
- if (opts.contentPartRId) parts.push(`<w:contentPart r:id="${opts.contentPartRId}"/>`);
2742
- if (opts.revision) {
2743
- const rev = opts.revision;
2744
- const { author: _a, date: _d, id: _i, ...originalProps } = rev;
2745
- const inner = stringifyRunPropertiesInner(originalProps);
2746
- parts.push(`<w:rPrChange w:author="${escapeXml(rev.author)}" w:date="${rev.date}" w:id="${rev.id}"><w:rPr>${inner ?? ""}</w:rPr></w:rPrChange>`);
2747
- }
2748
- return parts.length > 0 ? parts.join("") : void 0;
2749
- }
3593
+ const FieldCharacterType = {
3594
+ BEGIN: "begin",
3595
+ END: "end",
3596
+ SEPARATE: "separate"
3597
+ };
3598
+ /**
3599
+ * Creates a field character element.
3600
+ *
3601
+ * ## XSD Schema
3602
+ * ```xml
3603
+ * <xsd:complexType name="CT_FldChar">
3604
+ * <xsd:sequence>
3605
+ * <xsd:element name="fldData" type="CT_Text" minOccurs="0"/>
3606
+ * <xsd:element name="ffData" type="CT_FFData" minOccurs="0"/>
3607
+ * <xsd:element name="numberingChange" type="CT_TrackChangeNumbering" minOccurs="0"/>
3608
+ * </xsd:sequence>
3609
+ * <xsd:attribute name="fldCharType" type="ST_FldCharType" use="required"/>
3610
+ * <xsd:attribute name="fldLock" type="s:ST_OnOff"/>
3611
+ * <xsd:attribute name="dirty" type="s:ST_OnOff"/>
3612
+ * </xsd:complexType>
3613
+ * ```
3614
+ * @internal
3615
+ */
3616
+ const createFieldChar = (type, dirty, ffData, fldData, fieldLock) => {
3617
+ const children = [];
3618
+ if (fldData !== void 0) children.push(element("w:fldData", { "xml:space": "preserve" }, [fldData]));
3619
+ if (ffData) children.push(ffData);
3620
+ return element("w:fldChar", {
3621
+ "w:dirty": dirty,
3622
+ "w:fldLock": fieldLock,
3623
+ "w:fldCharType": type
3624
+ }, children.length > 0 ? children : void 0);
3625
+ };
2750
3626
  /**
2751
- * Build `<w:rPr>` XML string directly from options — zero IXmlableObject allocation.
3627
+ * Creates the beginning of a complex field.
2752
3628
  *
2753
- * Replaces `buildRunProperties() + xml()` with a single-pass string builder.
3629
+ * The Begin element marks the start of a field. A field consists of a begin character,
3630
+ * field instructions, an optional separate character, field result, and an end character.
3631
+ *
3632
+ * For form fields, pass `formField` to embed `w:ffData` within the begin `w:fldChar`.
3633
+ *
3634
+ * @param dirty - Whether the field should be recalculated
3635
+ * @param formField - Optional form field data to embed in the begin character
3636
+ *
3637
+ * @example
3638
+ * ```typescript
3639
+ * // Simple field begin
3640
+ * createBegin();
3641
+ *
3642
+ * // Form field (checkbox)
3643
+ * createBegin(false, {
3644
+ * name: "Check1",
3645
+ * checkBox: { checked: true, sizeAuto: true },
3646
+ * });
3647
+ * ```
2754
3648
  */
2755
- function stringifyRunProperties(opts) {
2756
- const inner = stringifyRunPropertiesInner(opts);
2757
- return inner ? `<w:rPr>${inner}</w:rPr>` : void 0;
2758
- }
3649
+ const createBegin = (dirty, formField, fieldLock) => createFieldChar(FieldCharacterType.BEGIN, dirty, formField ? createFormFieldData(formField) : void 0, void 0, fieldLock);
3650
+ /**
3651
+ * Creates the separator between field code and field result in a complex field.
3652
+ *
3653
+ * The Separate element divides the field code (instructions) from the field result
3654
+ * (the computed value).
3655
+ */
3656
+ const createSeparate = (dirty) => createFieldChar(FieldCharacterType.SEPARATE, dirty);
3657
+ /**
3658
+ * Creates the end of a complex field.
3659
+ *
3660
+ * The End element marks the end of a field. Every field that begins with a Begin
3661
+ * element must be terminated with an End element.
3662
+ */
3663
+ const createEnd = (dirty) => createFieldChar(FieldCharacterType.END, dirty);
2759
3664
  //#endregion
2760
3665
  //#region src/parts/inline.ts
2761
3666
  /**
@@ -3115,26 +4020,30 @@ function stringifyChildDispatch(child, ctx) {
3115
4020
  }
3116
4021
  if ("customXml" in child) {
3117
4022
  const cx = child.customXml;
3118
- const attrs = [`w:element="${escapeXml(cx.element)}"`];
3119
- if (cx.uri) attrs.push(`w:uri="${escapeXml(cx.uri)}"`);
3120
- const parts = [];
3121
- if (cx.customXmlPr) {
3122
- const prParts = [];
3123
- if (cx.customXmlPr.placeholder) prParts.push(`<w:placeholder w:val="${escapeXml(cx.customXmlPr.placeholder)}"/>`);
3124
- if (cx.customXmlPr.attrs?.length) for (const a of cx.customXmlPr.attrs) {
3125
- const aa = [`w:name="${escapeXml(a.name)}"`, `w:val="${escapeXml(a.val)}"`];
3126
- if (a.uri) aa.push(`w:uri="${escapeXml(a.uri)}"`);
3127
- prParts.push(`<w:attr ${aa.join(" ")}/>`);
3128
- }
3129
- if (prParts.length) parts.push(`<w:customXmlPr>${prParts.join("")}</w:customXmlPr>`);
3130
- }
3131
- if (cx.children) for (const c of cx.children) if (typeof c === "string") parts.push(stringifyRunInline({ text: c }, ctx));
4023
+ const contentParts = [];
4024
+ if (cx.children) for (const c of cx.children) if (typeof c === "string") contentParts.push(stringifyRunInline({ text: c }, ctx));
3132
4025
  else {
3133
4026
  const jr = stringifyChildDispatch(c, ctx);
3134
- if (jr !== void 0) parts.push(Array.isArray(jr) ? jr.join("") : jr);
3135
- else parts.push(stringifyRunInline(c, ctx));
4027
+ if (jr !== void 0) contentParts.push(Array.isArray(jr) ? jr.join("") : jr);
4028
+ else contentParts.push(stringifyRunInline(c, ctx));
4029
+ }
4030
+ return stringifyCustomXmlShell(cx, contentParts.join(""));
4031
+ }
4032
+ if ("sdt" in child) {
4033
+ const s = child.sdt;
4034
+ let contentXml = "";
4035
+ if (s.properties.checkbox) contentXml = checkboxSymbolRunInner(s.properties.checkbox);
4036
+ else if (s.children && s.children.length > 0) {
4037
+ const cparts = [];
4038
+ for (const c of s.children) if (typeof c === "string") cparts.push(stringifyRunInline({ text: c }, ctx));
4039
+ else {
4040
+ const jr = stringifyChildDispatch(c, ctx);
4041
+ if (jr !== void 0) cparts.push(Array.isArray(jr) ? jr.join("") : jr);
4042
+ else if ("text" in c || "children" in c || "break" in c) cparts.push(stringifyRunInline(c, ctx));
4043
+ }
4044
+ contentXml = cparts.join("");
3136
4045
  }
3137
- return `<w:customXml ${attrs.join(" ")}>${parts.join("")}</w:customXml>`;
4046
+ return stringifySdtShell(s.properties, s.endProperties, contentXml);
3138
4047
  }
3139
4048
  }
3140
4049
  /** Serialize children of Dir/Bdo containers. */
@@ -3175,6 +4084,151 @@ function stringifyParagraphInline(opts, ctx) {
3175
4084
  return body ? `<w:p>${body}</w:p>` : "<w:p/>";
3176
4085
  }
3177
4086
  //#endregion
4087
+ //#region src/parts/sdt/sdt-parse.ts
4088
+ /**
4089
+ * Structured Document Tag parser for DOCX documents.
4090
+ *
4091
+ * Parses w:sdt elements into SdtPropertiesOptions + children.
4092
+ *
4093
+ * @module
4094
+ */
4095
+ /**
4096
+ * Parse w:sdtPr element into SdtPropertiesOptions.
4097
+ */
4098
+ function parseSdtProperties(el) {
4099
+ const opts = {};
4100
+ const alias = findChild(el, "w:alias");
4101
+ if (alias) opts.alias = attr(alias, "w:val");
4102
+ const tag = findChild(el, "w:tag");
4103
+ if (tag) {
4104
+ const val = attr(tag, "w:val");
4105
+ if (val) opts.tag = val;
4106
+ }
4107
+ const id = findChild(el, "w:id");
4108
+ if (id) {
4109
+ const val = attrNum(id, "w:val");
4110
+ if (val !== void 0) opts.id = val;
4111
+ }
4112
+ const lock = findChild(el, "w:lock");
4113
+ if (lock) {
4114
+ const val = attr(lock, "w:val");
4115
+ if (val) opts.lock = val;
4116
+ }
4117
+ const temporary = findChild(el, "w:temporary");
4118
+ if (temporary) opts.temporary = attrBool(temporary, "w:val") ?? true;
4119
+ const showingPlcHdr = findChild(el, "w:showingPlcHdr");
4120
+ if (showingPlcHdr) opts.showingPlaceholder = attrBool(showingPlcHdr, "w:val") ?? true;
4121
+ const label = findChild(el, "w:label");
4122
+ if (label) {
4123
+ const val = attrNum(label, "w:val");
4124
+ if (val !== void 0) opts.label = val;
4125
+ }
4126
+ const tabIndex = findChild(el, "w:tabIndex");
4127
+ if (tabIndex) {
4128
+ const val = attrNum(tabIndex, "w:val");
4129
+ if (val !== void 0) opts.tabIndex = val;
4130
+ }
4131
+ const dataBinding = findChild(el, "w:dataBinding");
4132
+ if (dataBinding) opts.dataBinding = {
4133
+ xpath: attr(dataBinding, "w:xpath") ?? "",
4134
+ storeItemID: attr(dataBinding, "w:storeItemID") ?? "",
4135
+ prefixMappings: attr(dataBinding, "w:prefixMappings")
4136
+ };
4137
+ if (findChild(el, "w:equation")) opts.equation = true;
4138
+ else if (findChild(el, "w:comboBox")) {
4139
+ const comboBox = findChild(el, "w:comboBox");
4140
+ const items = [];
4141
+ for (const li of children(comboBox, "w:listItem")) items.push({
4142
+ displayText: attr(li, "w:displayText"),
4143
+ value: attr(li, "w:value")
4144
+ });
4145
+ opts.comboBox = {
4146
+ items: items.length > 0 ? items : void 0,
4147
+ lastValue: attr(comboBox, "w:lastValue")
4148
+ };
4149
+ } else if (findChild(el, "w:date")) {
4150
+ const date = findChild(el, "w:date");
4151
+ const dateOpts = {};
4152
+ const dateFormat = findChild(date, "w:dateFormat");
4153
+ if (dateFormat) dateOpts.dateFormat = textOf(dateFormat);
4154
+ const lid = findChild(date, "w:lid");
4155
+ if (lid) dateOpts.languageId = textOf(lid);
4156
+ const storeMapped = findChild(date, "w:storeMappedDataAs");
4157
+ if (storeMapped) dateOpts.storeMappedDataAs = attr(storeMapped, "w:val");
4158
+ const calendar = findChild(date, "w:calendar");
4159
+ if (calendar) dateOpts.calendar = attr(calendar, "w:val");
4160
+ const fullDate = attr(date, "w:fullDate");
4161
+ if (fullDate) dateOpts.fullDate = fullDate;
4162
+ opts.date = dateOpts;
4163
+ } else if (findChild(el, "w:dropDownList")) {
4164
+ const ddl = findChild(el, "w:dropDownList");
4165
+ const items = [];
4166
+ for (const li of children(ddl, "w:listItem")) items.push({
4167
+ displayText: attr(li, "w:displayText"),
4168
+ value: attr(li, "w:value")
4169
+ });
4170
+ opts.dropDownList = {
4171
+ items: items.length > 0 ? items : void 0,
4172
+ lastValue: attr(ddl, "w:lastValue")
4173
+ };
4174
+ } else if (findChild(el, "w:picture")) opts.picture = true;
4175
+ else if (findChild(el, "w:richText")) opts.richText = true;
4176
+ else if (findChild(el, "w:text")) opts.text = { multiLine: attrBool(findChild(el, "w:text"), "w:multiLine") };
4177
+ else if (findChild(el, "w:citation")) opts.citation = true;
4178
+ else if (findChild(el, "w:group")) opts.group = true;
4179
+ else if (findChild(el, "w:bibliography")) opts.bibliography = true;
4180
+ else if (findChild(el, "w:docPartObj")) {
4181
+ const dp = findChild(el, "w:docPartObj");
4182
+ opts.docPartObj = {};
4183
+ const gallery = findChild(dp, "w:docPartGallery");
4184
+ if (gallery) opts.docPartObj.gallery = attr(gallery, "w:val");
4185
+ const category = findChild(dp, "w:docPartCategory");
4186
+ if (category) opts.docPartObj.category = attr(category, "w:val");
4187
+ } else if (findChild(el, "w:docPartList")) {
4188
+ const dp = findChild(el, "w:docPartList");
4189
+ opts.docPartList = {};
4190
+ const gallery = findChild(dp, "w:docPartGallery");
4191
+ if (gallery) opts.docPartList.gallery = attr(gallery, "w:val");
4192
+ const category = findChild(dp, "w:docPartCategory");
4193
+ if (category) opts.docPartList.category = attr(category, "w:val");
4194
+ } else if (findChild(el, "w14:checkbox")) {
4195
+ const cb = findChild(el, "w14:checkbox");
4196
+ const cbObj = {};
4197
+ const checked = findChild(cb, "w14:checked");
4198
+ if (checked) cbObj.checked = attrBool(checked, "w14:val") ?? true;
4199
+ const checkedState = findChild(cb, "w14:checkedState");
4200
+ if (checkedState) cbObj.checkedState = {
4201
+ val: attr(checkedState, "w14:val") ?? "",
4202
+ font: attr(checkedState, "w14:font")
4203
+ };
4204
+ const uncheckedState = findChild(cb, "w14:uncheckedState");
4205
+ if (uncheckedState) cbObj.uncheckedState = {
4206
+ val: attr(uncheckedState, "w14:val") ?? "",
4207
+ font: attr(uncheckedState, "w14:font")
4208
+ };
4209
+ opts.checkbox = cbObj;
4210
+ }
4211
+ return opts;
4212
+ }
4213
+ /**
4214
+ * Parse a block-level w:sdt element.
4215
+ * Returns an object suitable for the { sdt: ... } SectionChild variant.
4216
+ */
4217
+ function parseSdtBlock(el, ctx, parseChildren) {
4218
+ const sdtPr = findChild(el, "w:sdtPr");
4219
+ const properties = sdtPr ? parseSdtProperties(sdtPr) : {};
4220
+ const sdtContent = findChild(el, "w:sdtContent");
4221
+ let childList;
4222
+ if (sdtContent) {
4223
+ childList = parseChildren(sdtContent.elements ?? [], ctx);
4224
+ if (childList.length === 0) childList = void 0;
4225
+ }
4226
+ return {
4227
+ properties,
4228
+ children: childList
4229
+ };
4230
+ }
4231
+ //#endregion
3178
4232
  //#region src/parts/table/stringify.ts
3179
4233
  /**
3180
4234
  * Direct XML string builders for table properties.
@@ -3195,24 +4249,20 @@ function tableWidthStr(name, opts) {
3195
4249
  })}/>`;
3196
4250
  }
3197
4251
  function cellMarginChildrenStr(opts) {
3198
- const unitType = opts.marginUnitType ?? WidthType.DXA;
3199
4252
  const parts = [];
3200
- if (opts.top !== void 0) parts.push(tableWidthStr("w:top", {
3201
- size: opts.top,
3202
- type: unitType
3203
- }));
3204
- if (opts.left !== void 0) parts.push(tableWidthStr("w:left", {
3205
- size: opts.left,
3206
- type: unitType
3207
- }));
3208
- if (opts.bottom !== void 0) parts.push(tableWidthStr("w:bottom", {
3209
- size: opts.bottom,
3210
- type: unitType
3211
- }));
3212
- if (opts.right !== void 0) parts.push(tableWidthStr("w:right", {
3213
- size: opts.right,
3214
- type: unitType
3215
- }));
4253
+ const side = (name, w) => {
4254
+ if (w === void 0) return;
4255
+ parts.push(tableWidthStr(name, {
4256
+ size: w.size,
4257
+ type: w.type ?? WidthType.DXA
4258
+ }));
4259
+ };
4260
+ side("w:top", opts.top);
4261
+ side("w:start", opts.start);
4262
+ side("w:left", opts.left);
4263
+ side("w:bottom", opts.bottom);
4264
+ side("w:end", opts.end);
4265
+ side("w:right", opts.right);
3216
4266
  return parts.join("");
3217
4267
  }
3218
4268
  function cellMarginStr(tag, opts) {
@@ -3237,6 +4287,8 @@ function cellBordersStr(opts) {
3237
4287
  if (opts.bottom) parts.push(borderStr("w:bottom", opts.bottom));
3238
4288
  if (opts.end) parts.push(borderStr("w:end", opts.end));
3239
4289
  if (opts.right) parts.push(borderStr("w:right", opts.right));
4290
+ if (opts.insideHorizontal) parts.push(borderStr("w:insideH", opts.insideHorizontal));
4291
+ if (opts.insideVertical) parts.push(borderStr("w:insideV", opts.insideVertical));
3240
4292
  if (opts.topLeftToBottomRight) parts.push(borderStr("w:tl2br", opts.topLeftToBottomRight));
3241
4293
  if (opts.topRightToBottomLeft) parts.push(borderStr("w:tr2bl", opts.topRightToBottomLeft));
3242
4294
  return parts.length > 0 ? `<w:tcBorders>${parts.join("")}</w:tcBorders>` : void 0;
@@ -3265,6 +4317,23 @@ function tableLookStr(opts) {
3265
4317
  "w:noVBand": opts.noVBand
3266
4318
  })}/>`;
3267
4319
  }
4320
+ function cnfStyleStr(opts) {
4321
+ return `<w:cnfStyle ${attrParts({
4322
+ "w:val": opts.val,
4323
+ "w:firstRow": opts.firstRow,
4324
+ "w:lastRow": opts.lastRow,
4325
+ "w:firstColumn": opts.firstColumn,
4326
+ "w:lastColumn": opts.lastColumn,
4327
+ "w:oddVBand": opts.oddVBand,
4328
+ "w:evenVBand": opts.evenVBand,
4329
+ "w:oddHBand": opts.oddHBand,
4330
+ "w:evenHBand": opts.evenHBand,
4331
+ "w:firstRowFirstColumn": opts.firstRowFirstColumn,
4332
+ "w:firstRowLastColumn": opts.firstRowLastColumn,
4333
+ "w:lastRowFirstColumn": opts.lastRowFirstColumn,
4334
+ "w:lastRowLastColumn": opts.lastRowLastColumn
4335
+ })}/>`;
4336
+ }
3268
4337
  function changeAttrStr(tag, opts) {
3269
4338
  return `<${tag} ${attrParts({
3270
4339
  "w:author": opts.author,
@@ -3285,7 +4354,7 @@ function cellMergeStr(opts) {
3285
4354
  function cellSpacingStr(opts) {
3286
4355
  return `<w:tblCellSpacing ${attrParts({
3287
4356
  "w:type": opts.type,
3288
- "w:w": measurementOrPercentValue(opts.value)
4357
+ "w:w": measurementOrPercentValue(opts.size)
3289
4358
  })}/>`;
3290
4359
  }
3291
4360
  function stringifyTablePropertiesChangeInner(options) {
@@ -3311,6 +4380,7 @@ function stringifyTablePropertiesInner(options) {
3311
4380
  if (options.styleColBandSize !== void 0) parts.push(`<w:tblStyleColBandSize w:val="${options.styleColBandSize}"/>`);
3312
4381
  if (options.width) parts.push(tableWidthStr("w:tblW", options.width));
3313
4382
  if (options.alignment) parts.push(`<w:jc w:val="${options.alignment}"/>`);
4383
+ if (options.cellSpacing) parts.push(cellSpacingStr(options.cellSpacing));
3314
4384
  if (options.indent) parts.push(tableWidthStr("w:tblInd", options.indent));
3315
4385
  if (options.borders) {
3316
4386
  const bs = tableBordersStr(options.borders);
@@ -3323,7 +4393,6 @@ function stringifyTablePropertiesInner(options) {
3323
4393
  if (cm) parts.push(cm);
3324
4394
  }
3325
4395
  if (options.tableLook) parts.push(tableLookStr(options.tableLook));
3326
- if (options.cellSpacing) parts.push(cellSpacingStr(options.cellSpacing));
3327
4396
  if (options.caption !== void 0) parts.push(`<w:tblCaption w:val="${options.caption}"/>`);
3328
4397
  if (options.description !== void 0) parts.push(`<w:tblDescription w:val="${options.description}"/>`);
3329
4398
  if (options.revision) parts.push(stringifyTablePropertiesChangeInner(options.revision));
@@ -3346,13 +4415,7 @@ function stringifyTableRowPropertiesChangeInner(options) {
3346
4415
  }
3347
4416
  function stringifyTableRowPropertiesInner(options) {
3348
4417
  const parts = [];
3349
- if (options.cnfStyle !== void 0) {
3350
- const a = attrParts({
3351
- "w:val": options.cnfStyle.val,
3352
- "w:changed": options.cnfStyle.changed
3353
- });
3354
- parts.push(`<w:cnfStyle ${a}/>`);
3355
- }
4418
+ if (options.cnfStyle !== void 0) parts.push(cnfStyleStr(options.cnfStyle));
3356
4419
  if (options.divId !== void 0) parts.push(`<w:divId w:val="${options.divId}"/>`);
3357
4420
  if (options.gridBefore !== void 0) parts.push(`<w:gridBefore w:val="${options.gridBefore}"/>`);
3358
4421
  if (options.gridAfter !== void 0) parts.push(`<w:gridAfter w:val="${options.gridAfter}"/>`);
@@ -3392,15 +4455,11 @@ function stringifyTableCellPropertiesChangeInner(options) {
3392
4455
  }
3393
4456
  function stringifyTableCellPropertiesInner(options) {
3394
4457
  const parts = [];
3395
- if (options.cnfStyle !== void 0) {
3396
- const a = attrParts({
3397
- "w:val": options.cnfStyle.val,
3398
- "w:changed": options.cnfStyle.changed
3399
- });
3400
- parts.push(`<w:cnfStyle ${a}/>`);
3401
- }
4458
+ if (options.cnfStyle !== void 0) parts.push(cnfStyleStr(options.cnfStyle));
3402
4459
  if (options.width) parts.push(tableWidthStr("w:tcW", options.width));
3403
4460
  if (options.columnSpan) parts.push(`<w:gridSpan w:val="${options.columnSpan}"/>`);
4461
+ if (options.horizontalMerge !== void 0) if (options.horizontalMerge === "restart") parts.push(`<w:hMerge w:val="restart"/>`);
4462
+ else parts.push(`<w:hMerge/>`);
3404
4463
  if (options.verticalMerge) parts.push(`<w:vMerge w:val="${options.verticalMerge}"/>`);
3405
4464
  else if (options.rowSpan && options.rowSpan > 1) parts.push(`<w:vMerge w:val="${VerticalMergeType.RESTART}"/>`);
3406
4465
  if (options.borders) {
@@ -3408,16 +4467,14 @@ function stringifyTableCellPropertiesInner(options) {
3408
4467
  if (bs) parts.push(bs);
3409
4468
  }
3410
4469
  if (options.shading) parts.push(shadingStr(options.shading));
4470
+ if (options.noWrap !== void 0) parts.push(onOff("w:noWrap", options.noWrap));
3411
4471
  if (options.margins) {
3412
4472
  const cm = cellMarginStr("w:tcMar", options.margins);
3413
4473
  if (cm) parts.push(cm);
3414
4474
  }
3415
4475
  if (options.textDirection) parts.push(`<w:textDirection w:val="${options.textDirection}"/>`);
3416
- if (options.verticalAlign) parts.push(`<w:vAlign w:val="${options.verticalAlign}"/>`);
3417
- if (options.horizontalMerge !== void 0) if (options.horizontalMerge === "restart") parts.push(`<w:hMerge w:val="restart"/>`);
3418
- else parts.push(`<w:hMerge/>`);
3419
- if (options.noWrap !== void 0) parts.push(onOff("w:noWrap", options.noWrap));
3420
4476
  if (options.fitText !== void 0) parts.push(onOff("w:tcFitText", options.fitText));
4477
+ if (options.verticalAlign) parts.push(`<w:vAlign w:val="${options.verticalAlign}"/>`);
3421
4478
  if (options.hideMark !== void 0) parts.push(onOff("w:hideMark", options.hideMark));
3422
4479
  if (options.headers !== void 0) {
3423
4480
  const headerParts = options.headers.map((h) => `<w:header w:val="${h}"/>`).join("");
@@ -3425,15 +4482,15 @@ function stringifyTableCellPropertiesInner(options) {
3425
4482
  }
3426
4483
  if (options.insertion) parts.push(changeAttrStr("w:cellIns", options.insertion));
3427
4484
  if (options.deletion) parts.push(changeAttrStr("w:cellDel", options.deletion));
3428
- if (options.revision) parts.push(stringifyTableCellPropertiesChangeInner(options.revision));
3429
4485
  if (options.cellMerge) parts.push(cellMergeStr(options.cellMerge));
4486
+ if (options.revision) parts.push(stringifyTableCellPropertiesChangeInner(options.revision));
3430
4487
  return parts.join("");
3431
4488
  }
3432
4489
  function stringifyTableCellProperties(options) {
3433
4490
  const inner = stringifyTableCellPropertiesInner(options);
3434
4491
  if (options.includeIfEmpty || inner) return `<w:tcPr>${inner}</w:tcPr>`;
3435
4492
  }
3436
- function stringifyTablePropertyExceptions(options) {
4493
+ function stringifyTablePropertyExceptionsInner(options) {
3437
4494
  const parts = [];
3438
4495
  if (options.width) parts.push(tableWidthStr("w:tblW", options.width));
3439
4496
  if (options.alignment) parts.push(`<w:jc w:val="${options.alignment}"/>`);
@@ -3452,15 +4509,18 @@ function stringifyTablePropertyExceptions(options) {
3452
4509
  if (options.tableLook) parts.push(tableLookStr(options.tableLook));
3453
4510
  if (options.tblPrExChange) {
3454
4511
  const change = options.tblPrExChange;
3455
- const attrs = {
4512
+ const a = attrParts({
3456
4513
  "w:author": change.author,
4514
+ "w:date": change.date,
3457
4515
  "w:id": change.id
3458
- };
3459
- if (change.date !== void 0) attrs["w:date"] = change.date;
3460
- const a = attrParts(attrs);
3461
- parts.push(`<w:tblPrExChange ${a}/>`);
4516
+ });
4517
+ const revInner = stringifyTablePropertyExceptionsInner(change);
4518
+ parts.push(`<w:tblPrExChange ${a}><w:tblPrEx>${revInner}</w:tblPrEx></w:tblPrExChange>`);
3462
4519
  }
3463
- return `<w:tblPrEx>${parts.join("")}</w:tblPrEx>`;
4520
+ return parts.join("");
4521
+ }
4522
+ function stringifyTablePropertyExceptions(options) {
4523
+ return `<w:tblPrEx>${stringifyTablePropertyExceptionsInner(options)}</w:tblPrEx>`;
3464
4524
  }
3465
4525
  //#endregion
3466
4526
  //#region src/parts/table/descriptor.ts
@@ -3526,7 +4586,15 @@ function stringifyTableRow(row, ctx, extraCells) {
3526
4586
  const trPr = stringifyTableRowProperties(row);
3527
4587
  if (trPr) parts.push(trPr);
3528
4588
  const prefixCount = parts.length;
3529
- for (const cell of row.cells) parts.push(stringifyTableCell(cell, ctx));
4589
+ for (const cell of row.cells) if ("sdt" in cell) {
4590
+ const s = cell.sdt;
4591
+ const contentXml = (s.cells ?? []).map((c) => stringifyTableCell(c, ctx)).join("");
4592
+ parts.push(stringifySdtShell(s.properties, s.endProperties, contentXml));
4593
+ } else if ("customXml" in cell) {
4594
+ const cx = cell.customXml;
4595
+ const contentXml = (cx.children ?? []).map((c) => stringifyTableCell(c, ctx)).join("");
4596
+ parts.push(stringifyCustomXmlShell(cx, contentXml));
4597
+ } else parts.push(stringifyTableCell(cell, ctx));
3530
4598
  if (extraCells && extraCells.length > 0) for (const { cell, columnIndex } of extraCells) {
3531
4599
  const insertIdx = findInsertIndex(row.cells, columnIndex, prefixCount);
3532
4600
  parts.splice(insertIdx, 0, stringifyTableCell(cell, ctx));
@@ -3540,10 +4608,20 @@ function stringifyTableRow(row, ctx, extraCells) {
3540
4608
  const body = parts.join("");
3541
4609
  return body ? `<w:tr${attr}>${body}</w:tr>` : attr ? `<w:tr${attr}/>` : "<w:tr/>";
3542
4610
  }
4611
+ /** Type guard: a plain row (not SDT/customXml-wrapped). */
4612
+ function isPlainRow(r) {
4613
+ return !("sdt" in r) && !("customXml" in r);
4614
+ }
4615
+ /** Type guard: a plain cell (not SDT/customXml-wrapped). */
4616
+ function isPlainCell(c) {
4617
+ return !("sdt" in c) && !("customXml" in c);
4618
+ }
3543
4619
  function findInsertIndex(cells, columnIndex, prefixCount) {
3544
4620
  let colIdx = 0;
3545
4621
  for (let i = 0; i < cells.length; i++) {
3546
- const { columnSpan } = getCellSpans(cells[i]);
4622
+ const c = cells[i];
4623
+ if (!isPlainCell(c)) continue;
4624
+ const { columnSpan } = getCellSpans(c);
3547
4625
  colIdx += columnSpan;
3548
4626
  if (colIdx > columnIndex) return i + prefixCount;
3549
4627
  }
@@ -3555,9 +4633,12 @@ function findInsertIndex(cells, columnIndex, prefixCount) {
3555
4633
  function computeVerticalMergeCells(rows) {
3556
4634
  const extraMap = /* @__PURE__ */ new Map();
3557
4635
  for (let ri = 0; ri < rows.length - 1; ri++) {
3558
- const cells = rows[ri].cells;
4636
+ const row = rows[ri];
4637
+ if (!isPlainRow(row)) continue;
4638
+ const cells = row.cells;
3559
4639
  let colIdx = 0;
3560
4640
  for (const cell of cells) {
4641
+ if (!isPlainCell(cell)) continue;
3561
4642
  const typedCell = cell;
3562
4643
  const { columnSpan, rowSpan } = getCellSpans(typedCell);
3563
4644
  if (rowSpan > 1) {
@@ -3579,6 +4660,137 @@ function computeVerticalMergeCells(rows) {
3579
4660
  }
3580
4661
  return extraMap;
3581
4662
  }
4663
+ /** Parse a w:tblCellMar / w:tcMar container into TableCellMarginOptions. */
4664
+ function parseCellMargins(marginEl) {
4665
+ const margins = {};
4666
+ for (const side of [
4667
+ "top",
4668
+ "start",
4669
+ "left",
4670
+ "bottom",
4671
+ "end",
4672
+ "right"
4673
+ ]) {
4674
+ const sideEl = findChild(marginEl, `w:${side}`);
4675
+ if (sideEl) {
4676
+ const size = attrNum(sideEl, "w:w");
4677
+ if (size !== void 0) {
4678
+ const type = attr(sideEl, "w:type");
4679
+ margins[side] = type ? {
4680
+ size,
4681
+ type
4682
+ } : { size };
4683
+ }
4684
+ }
4685
+ }
4686
+ if (Object.keys(margins).length === 0) return void 0;
4687
+ return margins;
4688
+ }
4689
+ /** Parse a w:shd (CT_Shd) element into ShadingAttributesProperties. */
4690
+ function parseShading(shd) {
4691
+ const shading = {};
4692
+ const fill = attr(shd, "w:fill");
4693
+ if (fill) shading.fill = fill;
4694
+ const color = attr(shd, "w:color");
4695
+ if (color) shading.color = color;
4696
+ const val = attr(shd, "w:val");
4697
+ if (val) shading.type = val;
4698
+ const themeColor = attr(shd, "w:themeColor");
4699
+ if (themeColor && THEME_COLORS.includes(themeColor)) shading.themeColor = themeColor;
4700
+ const themeTint = attr(shd, "w:themeTint");
4701
+ if (themeTint) shading.themeTint = themeTint;
4702
+ const themeShade = attr(shd, "w:themeShade");
4703
+ if (themeShade) shading.themeShade = themeShade;
4704
+ const themeFill = attr(shd, "w:themeFill");
4705
+ if (themeFill && THEME_COLORS.includes(themeFill)) shading.themeFill = themeFill;
4706
+ const themeFillTint = attr(shd, "w:themeFillTint");
4707
+ if (themeFillTint) shading.themeFillTint = themeFillTint;
4708
+ const themeFillShade = attr(shd, "w:themeFillShade");
4709
+ if (themeFillShade) shading.themeFillShade = themeFillShade;
4710
+ if (Object.keys(shading).length === 0) return void 0;
4711
+ return shading;
4712
+ }
4713
+ /** Parse a w:cnfStyle (CT_Cnf) element into CnfStyleOptions. */
4714
+ function parseCnfStyle(cnfEl) {
4715
+ const cnf = {};
4716
+ const val = attr(cnfEl, "w:val");
4717
+ if (val) cnf.val = val;
4718
+ const firstRow = attrBool(cnfEl, "w:firstRow");
4719
+ if (firstRow !== void 0) cnf.firstRow = firstRow;
4720
+ const lastRow = attrBool(cnfEl, "w:lastRow");
4721
+ if (lastRow !== void 0) cnf.lastRow = lastRow;
4722
+ const firstColumn = attrBool(cnfEl, "w:firstColumn");
4723
+ if (firstColumn !== void 0) cnf.firstColumn = firstColumn;
4724
+ const lastColumn = attrBool(cnfEl, "w:lastColumn");
4725
+ if (lastColumn !== void 0) cnf.lastColumn = lastColumn;
4726
+ const oddVBand = attrBool(cnfEl, "w:oddVBand");
4727
+ if (oddVBand !== void 0) cnf.oddVBand = oddVBand;
4728
+ const evenVBand = attrBool(cnfEl, "w:evenVBand");
4729
+ if (evenVBand !== void 0) cnf.evenVBand = evenVBand;
4730
+ const oddHBand = attrBool(cnfEl, "w:oddHBand");
4731
+ if (oddHBand !== void 0) cnf.oddHBand = oddHBand;
4732
+ const evenHBand = attrBool(cnfEl, "w:evenHBand");
4733
+ if (evenHBand !== void 0) cnf.evenHBand = evenHBand;
4734
+ const firstRowFirstColumn = attrBool(cnfEl, "w:firstRowFirstColumn");
4735
+ if (firstRowFirstColumn !== void 0) cnf.firstRowFirstColumn = firstRowFirstColumn;
4736
+ const firstRowLastColumn = attrBool(cnfEl, "w:firstRowLastColumn");
4737
+ if (firstRowLastColumn !== void 0) cnf.firstRowLastColumn = firstRowLastColumn;
4738
+ const lastRowFirstColumn = attrBool(cnfEl, "w:lastRowFirstColumn");
4739
+ if (lastRowFirstColumn !== void 0) cnf.lastRowFirstColumn = lastRowFirstColumn;
4740
+ const lastRowLastColumn = attrBool(cnfEl, "w:lastRowLastColumn");
4741
+ if (lastRowLastColumn !== void 0) cnf.lastRowLastColumn = lastRowLastColumn;
4742
+ if (Object.keys(cnf).length === 0) return void 0;
4743
+ return cnf;
4744
+ }
4745
+ /**
4746
+ * Parse a w:tblPrEx (CT_TblPrEx) element into TablePropertyExOptions.
4747
+ * CT_TblPrExBase shares its child elements with CT_TblPrBase, so this reuses
4748
+ * parseTablePropertiesEl and maps the table-level margins field to cellMargin.
4749
+ */
4750
+ function parseTablePropertyExceptions(el) {
4751
+ const base = parseTablePropertiesEl(el);
4752
+ const opts = {};
4753
+ if (base.width !== void 0) opts.width = base.width;
4754
+ if (base.indent !== void 0) opts.indent = base.indent;
4755
+ if (base.layout !== void 0) opts.layout = base.layout;
4756
+ if (base.borders !== void 0) opts.borders = base.borders;
4757
+ if (base.shading !== void 0) opts.shading = base.shading;
4758
+ if (base.alignment !== void 0) opts.alignment = base.alignment;
4759
+ if (base.margins !== void 0) opts.cellMargin = base.margins;
4760
+ if (base.tableLook !== void 0) opts.tableLook = base.tableLook;
4761
+ if (base.cellSpacing !== void 0) opts.cellSpacing = base.cellSpacing;
4762
+ const tblPrExChange = findChild(el, "w:tblPrExChange");
4763
+ if (tblPrExChange) {
4764
+ const change = parseTablePropertyExChange(tblPrExChange);
4765
+ if (change) opts.tblPrExChange = change;
4766
+ }
4767
+ return opts;
4768
+ }
4769
+ /** Parse a w:tblPrExChange (CT_TblPrExChange) — track-change wrapper around the previous tblPrEx. */
4770
+ function parseTablePropertyExChange(el) {
4771
+ const change = {};
4772
+ const id = attrNum(el, "w:id");
4773
+ if (id !== void 0) change.id = id;
4774
+ const author = attr(el, "w:author");
4775
+ if (author) change.author = author;
4776
+ const date = attr(el, "w:date");
4777
+ if (date) change.date = date;
4778
+ const innerTblPrEx = findChild(el, "w:tblPrEx");
4779
+ if (innerTblPrEx) {
4780
+ const inner = parseTablePropertyExceptions(innerTblPrEx);
4781
+ if (inner.width !== void 0) change.width = inner.width;
4782
+ if (inner.indent !== void 0) change.indent = inner.indent;
4783
+ if (inner.layout !== void 0) change.layout = inner.layout;
4784
+ if (inner.borders !== void 0) change.borders = inner.borders;
4785
+ if (inner.shading !== void 0) change.shading = inner.shading;
4786
+ if (inner.alignment !== void 0) change.alignment = inner.alignment;
4787
+ if (inner.cellMargin !== void 0) change.cellMargin = inner.cellMargin;
4788
+ if (inner.tableLook !== void 0) change.tableLook = inner.tableLook;
4789
+ if (inner.cellSpacing !== void 0) change.cellSpacing = inner.cellSpacing;
4790
+ }
4791
+ if (change.id === void 0 || change.author === void 0) return void 0;
4792
+ return change;
4793
+ }
3582
4794
  const tableDesc = {
3583
4795
  kind: "custom",
3584
4796
  stringify(opts, ctx) {
@@ -3594,6 +4806,7 @@ const tableDesc = {
3594
4806
  indent: opts.indent,
3595
4807
  layout: opts.layout,
3596
4808
  revision: opts.revision,
4809
+ shading: opts.shading,
3597
4810
  style: opts.style,
3598
4811
  styleColBandSize: opts.styleColBandSize,
3599
4812
  styleRowBandSize: opts.styleRowBandSize,
@@ -3603,13 +4816,23 @@ const tableDesc = {
3603
4816
  includeIfEmpty: true
3604
4817
  };
3605
4818
  parts.push(stringifyTableProperties(tblPrOpts));
3606
- const columnWidths = opts.columnWidths ?? Array(Math.max(...opts.rows.map((r) => r.cells.length))).fill(100);
4819
+ const columnWidths = opts.columnWidths ?? Array(Math.max(1, ...opts.rows.map((r) => isPlainRow(r) ? r.cells.length : 0))).fill(100);
3607
4820
  parts.push(buildTableGridXml(columnWidths, opts.columnWidthsRevision));
3608
4821
  const extraCells = computeVerticalMergeCells(opts.rows);
3609
4822
  for (let ri = 0; ri < opts.rows.length; ri++) {
3610
- const row = opts.rows[ri];
3611
- const extras = extraCells.get(ri);
3612
- parts.push(stringifyTableRow(row, ctx, extras));
4823
+ const r = opts.rows[ri];
4824
+ if ("sdt" in r) {
4825
+ const sdt = r.sdt;
4826
+ const contentXml = (sdt.rows ?? []).map((rr) => stringifyTableRow(rr, ctx)).join("");
4827
+ parts.push(stringifySdtShell(sdt.properties, sdt.endProperties, contentXml));
4828
+ } else if ("customXml" in r) {
4829
+ const cx = r.customXml;
4830
+ const contentXml = (cx.children ?? []).map((rr) => stringifyTableRow(rr, ctx)).join("");
4831
+ parts.push(stringifyCustomXmlShell(cx, contentXml));
4832
+ } else {
4833
+ const extras = extraCells.get(ri);
4834
+ parts.push(stringifyTableRow(r, ctx, extras));
4835
+ }
3613
4836
  }
3614
4837
  return `<w:tbl>${parts.join("")}</w:tbl>`;
3615
4838
  },
@@ -3689,35 +4912,13 @@ function parseTablePropertiesEl(el) {
3689
4912
  }
3690
4913
  const tblCellMar = findChild(el, "w:tblCellMar");
3691
4914
  if (tblCellMar) {
3692
- const margins = {};
3693
- for (const side of [
3694
- "top",
3695
- "bottom",
3696
- "left",
3697
- "right"
3698
- ]) {
3699
- const sideEl = findChild(tblCellMar, `w:${side}`);
3700
- if (sideEl) {
3701
- const size = attrNum(sideEl, "w:w");
3702
- const type = attr(sideEl, "w:type");
3703
- if (size !== void 0) margins[side] = {
3704
- size,
3705
- type: type ?? "dxa"
3706
- };
3707
- }
3708
- }
3709
- if (Object.keys(margins).length > 0) opts.margins = margins;
4915
+ const margins = parseCellMargins(tblCellMar);
4916
+ if (margins) opts.margins = margins;
3710
4917
  }
3711
4918
  const shd = findChild(el, "w:shd");
3712
4919
  if (shd) {
3713
- const shading = {};
3714
- const fill = attr(shd, "w:fill");
3715
- if (fill) shading.fill = fill;
3716
- const color = attr(shd, "w:color");
3717
- if (color) shading.color = color;
3718
- const val = attr(shd, "w:val");
3719
- if (val) shading.type = val;
3720
- if (Object.keys(shading).length > 0) opts.shading = shading;
4920
+ const shading = parseShading(shd);
4921
+ if (shading) opts.shading = shading;
3721
4922
  }
3722
4923
  const tblDesc = findChild(el, "w:tblDescription");
3723
4924
  if (tblDesc) {
@@ -3725,28 +4926,35 @@ function parseTablePropertiesEl(el) {
3725
4926
  if (val) opts.description = val;
3726
4927
  }
3727
4928
  const tblpPr = findChild(el, "w:tblpPr");
3728
- if (tblpPr) {
4929
+ const tblOverlap = findChild(el, "w:tblOverlap");
4930
+ if (tblpPr || tblOverlap) {
3729
4931
  const floatOpts = {};
3730
- const horzAnchor = attr(tblpPr, "w:horzAnchor");
3731
- if (horzAnchor) floatOpts.horizontalAnchor = horzAnchor;
3732
- const vertAnchor = attr(tblpPr, "w:vertAnchor");
3733
- if (vertAnchor) floatOpts.verticalAnchor = vertAnchor;
3734
- const tblpX = attrNum(tblpPr, "w:tblpX");
3735
- if (tblpX !== void 0) floatOpts.absoluteHorizontalPosition = tblpX;
3736
- const tblpXSpec = attr(tblpPr, "w:tblpXSpec");
3737
- if (tblpXSpec) floatOpts.relativeHorizontalPosition = tblpXSpec;
3738
- const tblpY = attrNum(tblpPr, "w:tblpY");
3739
- if (tblpY !== void 0) floatOpts.absoluteVerticalPosition = tblpY;
3740
- const tblpYSpec = attr(tblpPr, "w:tblpYSpec");
3741
- if (tblpYSpec) floatOpts.relativeVerticalPosition = tblpYSpec;
3742
- const bottomFromText = attrNum(tblpPr, "w:bottomFromText");
3743
- if (bottomFromText !== void 0) floatOpts.bottomFromText = bottomFromText;
3744
- const topFromText = attrNum(tblpPr, "w:topFromText");
3745
- if (topFromText !== void 0) floatOpts.topFromText = topFromText;
3746
- const leftFromText = attrNum(tblpPr, "w:leftFromText");
3747
- if (leftFromText !== void 0) floatOpts.leftFromText = leftFromText;
3748
- const rightFromText = attrNum(tblpPr, "w:rightFromText");
3749
- if (rightFromText !== void 0) floatOpts.rightFromText = rightFromText;
4932
+ if (tblpPr) {
4933
+ const horzAnchor = attr(tblpPr, "w:horzAnchor");
4934
+ if (horzAnchor) floatOpts.horizontalAnchor = horzAnchor;
4935
+ const vertAnchor = attr(tblpPr, "w:vertAnchor");
4936
+ if (vertAnchor) floatOpts.verticalAnchor = vertAnchor;
4937
+ const tblpX = attrNum(tblpPr, "w:tblpX");
4938
+ if (tblpX !== void 0) floatOpts.absoluteHorizontalPosition = tblpX;
4939
+ const tblpXSpec = attr(tblpPr, "w:tblpXSpec");
4940
+ if (tblpXSpec) floatOpts.relativeHorizontalPosition = tblpXSpec;
4941
+ const tblpY = attrNum(tblpPr, "w:tblpY");
4942
+ if (tblpY !== void 0) floatOpts.absoluteVerticalPosition = tblpY;
4943
+ const tblpYSpec = attr(tblpPr, "w:tblpYSpec");
4944
+ if (tblpYSpec) floatOpts.relativeVerticalPosition = tblpYSpec;
4945
+ const bottomFromText = attrNum(tblpPr, "w:bottomFromText");
4946
+ if (bottomFromText !== void 0) floatOpts.bottomFromText = bottomFromText;
4947
+ const topFromText = attrNum(tblpPr, "w:topFromText");
4948
+ if (topFromText !== void 0) floatOpts.topFromText = topFromText;
4949
+ const leftFromText = attrNum(tblpPr, "w:leftFromText");
4950
+ if (leftFromText !== void 0) floatOpts.leftFromText = leftFromText;
4951
+ const rightFromText = attrNum(tblpPr, "w:rightFromText");
4952
+ if (rightFromText !== void 0) floatOpts.rightFromText = rightFromText;
4953
+ }
4954
+ if (tblOverlap) {
4955
+ const overlap = attr(tblOverlap, "w:val");
4956
+ if (overlap) floatOpts.overlap = overlap;
4957
+ }
3750
4958
  if (Object.keys(floatOpts).length > 0) opts.float = floatOpts;
3751
4959
  }
3752
4960
  const tblInd = findChild(el, "w:tblInd");
@@ -3780,7 +4988,7 @@ function parseTablePropertiesEl(el) {
3780
4988
  const type = attr(tblCellSpacing, "w:type");
3781
4989
  const w = attrNum(tblCellSpacing, "w:w");
3782
4990
  if (w !== void 0) opts.cellSpacing = {
3783
- value: w,
4991
+ size: w,
3784
4992
  ...type ? { type } : {}
3785
4993
  };
3786
4994
  }
@@ -3797,17 +5005,51 @@ function parseTablePropertiesEl(el) {
3797
5005
  if (innerTblPr) Object.assign(rev, parseTablePropertiesEl(innerTblPr));
3798
5006
  if (Object.keys(rev).length > 0) opts.revision = rev;
3799
5007
  }
5008
+ const tblLook = findChild(el, "w:tblLook");
5009
+ if (tblLook) {
5010
+ const look = {};
5011
+ const firstRow = attrBool(tblLook, "w:firstRow");
5012
+ if (firstRow !== void 0) look.firstRow = firstRow;
5013
+ const lastRow = attrBool(tblLook, "w:lastRow");
5014
+ if (lastRow !== void 0) look.lastRow = lastRow;
5015
+ const firstColumn = attrBool(tblLook, "w:firstColumn");
5016
+ if (firstColumn !== void 0) look.firstColumn = firstColumn;
5017
+ const lastColumn = attrBool(tblLook, "w:lastColumn");
5018
+ if (lastColumn !== void 0) look.lastColumn = lastColumn;
5019
+ const noHBand = attrBool(tblLook, "w:noHBand");
5020
+ if (noHBand !== void 0) look.noHBand = noHBand;
5021
+ const noVBand = attrBool(tblLook, "w:noVBand");
5022
+ if (noVBand !== void 0) look.noVBand = noVBand;
5023
+ if (Object.keys(look).length > 0) opts.tableLook = look;
5024
+ }
3800
5025
  return opts;
3801
5026
  }
3802
5027
  function parseColumnWidthsEl(el) {
3803
- const cols = [];
5028
+ const widths = [];
3804
5029
  const tblGrid = findChild(el, "w:tblGrid");
3805
- if (!tblGrid) return cols;
5030
+ if (!tblGrid) return { widths };
3806
5031
  for (const col of children(tblGrid, "w:gridCol")) {
3807
5032
  const w = attrNum(col, "w:w");
3808
- cols.push(w ?? 100);
5033
+ widths.push(w ?? 100);
5034
+ }
5035
+ const tblGridChange = findChild(tblGrid, "w:tblGridChange");
5036
+ if (tblGridChange) {
5037
+ const id = attrNum(tblGridChange, "w:id");
5038
+ const innerGrid = findChild(tblGridChange, "w:tblGrid");
5039
+ const revWidths = [];
5040
+ if (innerGrid) for (const col of children(innerGrid, "w:gridCol")) {
5041
+ const w = attrNum(col, "w:w");
5042
+ revWidths.push(w ?? 100);
5043
+ }
5044
+ if (id !== void 0) return {
5045
+ widths,
5046
+ revision: {
5047
+ id,
5048
+ columnWidths: revWidths
5049
+ }
5050
+ };
3809
5051
  }
3810
- return cols;
5052
+ return { widths };
3811
5053
  }
3812
5054
  function parseTableRowPropertiesEl(el) {
3813
5055
  const opts = {};
@@ -3817,18 +5059,13 @@ function parseTableRowPropertiesEl(el) {
3817
5059
  const rule = attr(trHeight, "w:hRule");
3818
5060
  if (val !== void 0) opts.height = {
3819
5061
  value: val,
3820
- rule: rule ?? "atLeast"
5062
+ ...rule ? { rule } : {}
3821
5063
  };
3822
5064
  }
3823
5065
  const cnfStyle = findChild(el, "w:cnfStyle");
3824
5066
  if (cnfStyle) {
3825
- const val = attr(cnfStyle, "w:val");
3826
- if (val) {
3827
- const cnf = { val };
3828
- const changed = attrBool(cnfStyle, "w:changed");
3829
- if (changed !== void 0) cnf.changed = changed;
3830
- opts.cnfStyle = cnf;
3831
- }
5067
+ const cnf = parseCnfStyle(cnfStyle);
5068
+ if (cnf) opts.cnfStyle = cnf;
3832
5069
  }
3833
5070
  const divId = findChild(el, "w:divId");
3834
5071
  if (divId) {
@@ -3877,7 +5114,7 @@ function parseTableRowPropertiesEl(el) {
3877
5114
  const type = attr(tblCellSpacing, "w:type");
3878
5115
  const w = attrNum(tblCellSpacing, "w:w");
3879
5116
  if (w !== void 0) opts.cellSpacing = {
3880
- value: w,
5117
+ size: w,
3881
5118
  ...type ? { type } : {}
3882
5119
  };
3883
5120
  }
@@ -3902,34 +5139,22 @@ function parseTableRowPropertiesEl(el) {
3902
5139
  if (tblHeader) opts.tableHeader = attrBool(tblHeader, "w:val") ?? true;
3903
5140
  const cantSplit = findChild(el, "w:cantSplit");
3904
5141
  if (cantSplit) opts.cantSplit = attrBool(cantSplit, "w:val") ?? true;
3905
- const tblLook = findChild(el, "w:tblLook");
3906
- if (tblLook) {
3907
- const look = {};
3908
- const firstRow = attrBool(tblLook, "w:firstRow");
3909
- if (firstRow !== void 0) look.firstRow = firstRow;
3910
- const lastRow = attrBool(tblLook, "w:lastRow");
3911
- if (lastRow !== void 0) look.lastRow = lastRow;
3912
- const firstColumn = attrBool(tblLook, "w:firstColumn");
3913
- if (firstColumn !== void 0) look.firstColumn = firstColumn;
3914
- const lastColumn = attrBool(tblLook, "w:lastColumn");
3915
- if (lastColumn !== void 0) look.lastColumn = lastColumn;
3916
- const noHBand = attrBool(tblLook, "w:noHBand");
3917
- if (noHBand !== void 0) look.noHBand = noHBand;
3918
- const noVBand = attrBool(tblLook, "w:noVBand");
3919
- if (noVBand !== void 0) look.noVBand = noVBand;
3920
- if (Object.keys(look).length > 0) opts.tableLook = look;
3921
- }
3922
5142
  return opts;
3923
5143
  }
3924
5144
  function parseTableCellPropertiesEl(el) {
3925
5145
  const opts = {};
5146
+ const cnfStyle = findChild(el, "w:cnfStyle");
5147
+ if (cnfStyle) {
5148
+ const cnf = parseCnfStyle(cnfStyle);
5149
+ if (cnf) opts.cnfStyle = cnf;
5150
+ }
3926
5151
  const tcW = findChild(el, "w:tcW");
3927
5152
  if (tcW) {
3928
5153
  const size = attrNum(tcW, "w:w");
3929
5154
  const type = attr(tcW, "w:type");
3930
5155
  if (size !== void 0) opts.width = {
3931
5156
  size,
3932
- type: type ?? "dxa"
5157
+ ...type ? { type } : {}
3933
5158
  };
3934
5159
  }
3935
5160
  const gridSpan = findChild(el, "w:gridSpan");
@@ -3946,39 +5171,47 @@ function parseTableCellPropertiesEl(el) {
3946
5171
  }
3947
5172
  const shd = findChild(el, "w:shd");
3948
5173
  if (shd) {
3949
- const shading = {};
3950
- const fill = attr(shd, "w:fill");
3951
- if (fill) shading.fill = fill;
3952
- const color = attr(shd, "w:color");
3953
- if (color) shading.color = color;
3954
- const val = attr(shd, "w:val");
3955
- if (val) shading.type = val;
3956
- if (Object.keys(shading).length > 0) opts.shading = shading;
5174
+ const shading = parseShading(shd);
5175
+ if (shading) opts.shading = shading;
3957
5176
  }
3958
5177
  const tcBorders = findChild(el, "w:tcBorders");
3959
5178
  if (tcBorders) {
3960
- const borders = {};
3961
- for (const [xmlSide, key] of [
5179
+ const SIDE_KEYS = [
3962
5180
  ["top", "top"],
3963
5181
  ["start", "start"],
3964
5182
  ["left", "left"],
3965
5183
  ["bottom", "bottom"],
3966
5184
  ["end", "end"],
3967
5185
  ["right", "right"],
5186
+ ["insideH", "insideHorizontal"],
5187
+ ["insideV", "insideVertical"],
3968
5188
  ["tl2br", "topLeftToBottomRight"],
3969
5189
  ["tr2bl", "topRightToBottomLeft"]
3970
- ]) {
5190
+ ];
5191
+ const borders = {};
5192
+ for (const [xmlSide, key] of SIDE_KEYS) {
3971
5193
  const sideEl = findChild(tcBorders, `w:${xmlSide}`);
3972
- if (sideEl) {
3973
- const b = {};
3974
- const val = attr(sideEl, "w:val");
3975
- if (val) b.style = val;
3976
- const color = attr(sideEl, "w:color");
3977
- if (color) b.color = color;
3978
- const sz = attrNum(sideEl, "w:sz");
3979
- if (sz !== void 0) b.size = sz;
3980
- borders[key] = b;
3981
- }
5194
+ if (!sideEl) continue;
5195
+ const style = attr(sideEl, "w:val");
5196
+ if (!style || !BORDER_STYLES.includes(style)) continue;
5197
+ const sideOpts = { style };
5198
+ const color = attr(sideEl, "w:color");
5199
+ if (color) sideOpts.color = color;
5200
+ const size = attrNum(sideEl, "w:sz");
5201
+ if (size !== void 0) sideOpts.size = size;
5202
+ const space = attrNum(sideEl, "w:space");
5203
+ if (space !== void 0) sideOpts.space = space;
5204
+ const themeColor = attr(sideEl, "w:themeColor");
5205
+ if (themeColor && THEME_COLORS.includes(themeColor)) sideOpts.themeColor = themeColor;
5206
+ const themeTint = attr(sideEl, "w:themeTint");
5207
+ if (themeTint) sideOpts.themeTint = themeTint;
5208
+ const themeShade = attr(sideEl, "w:themeShade");
5209
+ if (themeShade) sideOpts.themeShade = themeShade;
5210
+ const shadow = attrBool(sideEl, "w:shadow");
5211
+ if (shadow !== void 0) sideOpts.shadow = shadow;
5212
+ const frame = attrBool(sideEl, "w:frame");
5213
+ if (frame !== void 0) sideOpts.frame = frame;
5214
+ borders[key] = sideOpts;
3982
5215
  }
3983
5216
  if (Object.keys(borders).length > 0) opts.borders = borders;
3984
5217
  }
@@ -3986,26 +5219,8 @@ function parseTableCellPropertiesEl(el) {
3986
5219
  if (noWrap) opts.noWrap = attrBool(noWrap, "w:val") ?? true;
3987
5220
  const tcMar = findChild(el, "w:tcMar");
3988
5221
  if (tcMar) {
3989
- const margins = {};
3990
- let marginUnitType;
3991
- for (const side of [
3992
- "top",
3993
- "bottom",
3994
- "left",
3995
- "right"
3996
- ]) {
3997
- const sideEl = findChild(tcMar, `w:${side}`);
3998
- if (sideEl) {
3999
- const size = attrNum(sideEl, "w:w");
4000
- const type = attr(sideEl, "w:type");
4001
- if (size !== void 0) {
4002
- margins[side] = size;
4003
- if (type && !marginUnitType) marginUnitType = type;
4004
- }
4005
- }
4006
- }
4007
- if (marginUnitType) margins.marginUnitType = marginUnitType;
4008
- if (Object.keys(margins).length > 0) opts.margins = margins;
5222
+ const margins = parseCellMargins(tcMar);
5223
+ if (margins) opts.margins = margins;
4009
5224
  }
4010
5225
  const textDirection = findChild(el, "w:textDirection");
4011
5226
  if (textDirection) {
@@ -4076,6 +5291,11 @@ function parseTableRowEl(el, ctx) {
4076
5291
  const opts = {};
4077
5292
  const trPr = findChild(el, "w:trPr");
4078
5293
  if (trPr) Object.assign(opts, parseTableRowPropertiesEl(trPr));
5294
+ const tblPrEx = findChild(el, "w:tblPrEx");
5295
+ if (tblPrEx) {
5296
+ const exceptions = parseTablePropertyExceptions(tblPrEx);
5297
+ if (Object.keys(exceptions).length > 0) opts.propertyExceptions = exceptions;
5298
+ }
4079
5299
  for (const [attrName, optKey] of [
4080
5300
  ["w:rsidRPr", "rsidRPr"],
4081
5301
  ["w:rsidR", "rsidR"],
@@ -4087,6 +5307,34 @@ function parseTableRowEl(el, ctx) {
4087
5307
  }
4088
5308
  const childCells = [];
4089
5309
  for (const child of el.elements ?? []) if (child.name === "w:tc") childCells.push(parseTableCellEl(child, ctx));
5310
+ else if (child.name === "w:sdt") {
5311
+ const sdtPr = findChild(child, "w:sdtPr");
5312
+ const properties = sdtPr ? parseSdtProperties(sdtPr) : {};
5313
+ const sdtEndPr = findChild(child, "w:sdtEndPr");
5314
+ const endProperties = sdtEndPr ? parseRunProperties(sdtEndPr) : void 0;
5315
+ const sdtContent = findChild(child, "w:sdtContent");
5316
+ const sdtCells = [];
5317
+ if (sdtContent) {
5318
+ for (const sub of sdtContent.elements ?? []) if (sub.name === "w:tc") sdtCells.push(parseTableCellEl(sub, ctx));
5319
+ }
5320
+ const sdt = { properties };
5321
+ if (sdtCells.length > 0) sdt.cells = sdtCells;
5322
+ if (endProperties) sdt.endProperties = endProperties;
5323
+ childCells.push({ sdt });
5324
+ } else if (child.name === "w:customXml") {
5325
+ const cx = { element: attr(child, "w:element") ?? "" };
5326
+ const cxUri = attr(child, "w:uri");
5327
+ if (cxUri) cx.uri = cxUri;
5328
+ const xmlPr = findChild(child, "w:customXmlPr");
5329
+ if (xmlPr) {
5330
+ const parsed = parseCustomXmlPr(xmlPr);
5331
+ if (parsed.placeholder !== void 0 || parsed.attributes !== void 0) cx.customXmlPr = parsed;
5332
+ }
5333
+ const cxCells = [];
5334
+ for (const sub of child.elements ?? []) if (sub.name === "w:tc") cxCells.push(parseTableCellEl(sub, ctx));
5335
+ if (cxCells.length > 0) cx.children = cxCells;
5336
+ childCells.push({ customXml: cx });
5337
+ }
4090
5338
  opts.cells = childCells;
4091
5339
  return opts;
4092
5340
  }
@@ -4094,10 +5342,39 @@ function parseTableEl(el, ctx) {
4094
5342
  const opts = {};
4095
5343
  const tblPr = findChild(el, "w:tblPr");
4096
5344
  if (tblPr) Object.assign(opts, parseTablePropertiesEl(tblPr));
4097
- const colWidths = parseColumnWidthsEl(el);
4098
- if (colWidths.length > 0) opts.columnWidths = colWidths;
5345
+ const grid = parseColumnWidthsEl(el);
5346
+ if (grid.widths.length > 0) opts.columnWidths = grid.widths;
5347
+ if (grid.revision) opts.columnWidthsRevision = grid.revision;
4099
5348
  const rows = [];
4100
5349
  for (const child of el.elements ?? []) if (child.name === "w:tr") rows.push(parseTableRowEl(child, ctx));
5350
+ else if (child.name === "w:sdt") {
5351
+ const sdtPr = findChild(child, "w:sdtPr");
5352
+ const properties = sdtPr ? parseSdtProperties(sdtPr) : {};
5353
+ const sdtEndPr = findChild(child, "w:sdtEndPr");
5354
+ const endProperties = sdtEndPr ? parseRunProperties(sdtEndPr) : void 0;
5355
+ const sdtContent = findChild(child, "w:sdtContent");
5356
+ const sdtRows = [];
5357
+ if (sdtContent) {
5358
+ for (const sub of sdtContent.elements ?? []) if (sub.name === "w:tr") sdtRows.push(parseTableRowEl(sub, ctx));
5359
+ }
5360
+ const sdt = { properties };
5361
+ if (sdtRows.length > 0) sdt.rows = sdtRows;
5362
+ if (endProperties) sdt.endProperties = endProperties;
5363
+ rows.push({ sdt });
5364
+ } else if (child.name === "w:customXml") {
5365
+ const cx = { element: attr(child, "w:element") ?? "" };
5366
+ const cxUri = attr(child, "w:uri");
5367
+ if (cxUri) cx.uri = cxUri;
5368
+ const xmlPr = findChild(child, "w:customXmlPr");
5369
+ if (xmlPr) {
5370
+ const parsed = parseCustomXmlPr(xmlPr);
5371
+ if (parsed.placeholder !== void 0 || parsed.attributes !== void 0) cx.customXmlPr = parsed;
5372
+ }
5373
+ const cxRows = [];
5374
+ for (const sub of child.elements ?? []) if (sub.name === "w:tr") cxRows.push(parseTableRowEl(sub, ctx));
5375
+ if (cxRows.length > 0) cx.children = cxRows;
5376
+ rows.push({ customXml: cx });
5377
+ }
4101
5378
  opts.rows = rows;
4102
5379
  return opts;
4103
5380
  }
@@ -5081,6 +6358,6 @@ function parseNotePropertiesEl(el) {
5081
6358
  return opts;
5082
6359
  }
5083
6360
  //#endregion
5084
- export { FormFieldTextType as $, stringifyParagraphProperties as A, VerticalPositionRelativeFrom as B, createPageSize as C, stringifyChildDispatch as D, tableDesc as E, resetDrawingIdGen as F, createWrapThrough as G, NumberFormat as H, parseDrawingRun as I, TextWrappingType as J, createWrapTight as K, createVerticalPosition as L, stringifyRunPropertiesInner as M, parseMathChildren as N, stringifyParagraphInline as O, drawingDesc as P, VerticalMergeType as Q, createHorizontalPosition as R, PageOrientation as S, setTableParseChild as T, SpaceType as U, HorizontalPositionAlign as V, VerticalPositionAlign as W, BorderStyle as X, WidthType as Y, TextDirection as Z, DocumentGridType as _, HeaderFooterType as a, TextVerticalType as at, sectionPageSizeDefaults as b, createSectionType as c, WORKAROUND2 as ct, createPageMargin as d, createFormFieldData as et, PageBorderDisplay as f, createPageNumberType as g, PageNumberSeparator as h, HeaderFooterReferenceType as i, TextVertOverflowType as it, stringifyRunProperties as j, stringifyRunInline as k, LineNumberRestartFormat as l, Media as lt, PageBorderZOrder as m, sectionPropertiesDesc as n, TextBodyWrappingType as nt, createHeaderFooterReference as o, VerticalAnchor as ot, PageBorderOffsetFrom as p, TextWrappingSide as q, stringifySectionPropertiesXml as r, TextHorzOverflowType as rt, SectionType as s, createBodyProperties as st, parseSectionPropertiesEl as t, parseFormFieldData as tt, createLineNumberType as u, createTransformation as ut, createDocumentGrid as v, DocumentAttributeNamespaces as w, PageTextDirectionType as x, sectionMarginDefaults as y, HorizontalPositionRelativeFrom as z };
6361
+ export { setBodyParseChild as $, stringifyParagraphInline as A, HorizontalPositionAlign as B, createPageSize as C, Media as Ct, parseSdtBlock as D, tableDesc as E, parseDrawingRun as F, createWrapTight as G, SpaceType as H, createVerticalPosition as I, altChunkDesc as J, TextWrappingSide as K, createHorizontalPosition as L, parseMathChildren as M, drawingDesc as N, parseSdtProperties as O, resetDrawingIdGen as P, sdtBlockDesc as Q, HorizontalPositionRelativeFrom as R, PageOrientation as S, WORKAROUND2 as St, setTableParseChild as T, VerticalPositionAlign as U, NumberFormat as V, createWrapThrough as W, customXmlBlockDesc as X, checkboxSymbolRunInner as Y, parseCustomXmlPr as Z, DocumentGridType as _, TextHorzOverflowType as _t, HeaderFooterType as a, stringifyRunProperties as at, sectionPageSizeDefaults as b, VerticalAnchor as bt, createSectionType as c, parsedRunToOptions as ct, createPageMargin as d, TextDirection as dt, stringifyCustomXmlShell as et, PageBorderDisplay as f, VerticalMergeType as ft, createPageNumberType as g, TextBodyWrappingType as gt, PageNumberSeparator as h, parseFormFieldData as ht, HeaderFooterReferenceType as i, stringifyParagraphProperties as it, stringifyRunInline as j, stringifyChildDispatch as k, LineNumberRestartFormat as l, WidthType as lt, PageBorderZOrder as m, createFormFieldData as mt, sectionPropertiesDesc as n, stringifySdtShell as nt, createHeaderFooterReference as o, parseRun as ot, PageBorderOffsetFrom as p, FormFieldTextType as pt, TextWrappingType as q, stringifySectionPropertiesXml as r, subDocDesc as rt, SectionType as s, parseRunProperties as st, parseSectionPropertiesEl as t, stringifySdtPr as tt, createLineNumberType as u, BorderStyle as ut, createDocumentGrid as v, TextVertOverflowType as vt, DocumentAttributeNamespaces as w, createTransformation as wt, PageTextDirectionType as x, createBodyProperties as xt, sectionMarginDefaults as y, TextVerticalType as yt, VerticalPositionRelativeFrom as z };
5085
6362
 
5086
- //# sourceMappingURL=document-B_uvEH8b.mjs.map
6363
+ //# sourceMappingURL=document-D2OTVcbX.mjs.map