@office-open/docx 0.9.3 → 0.9.5

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
- import { TargetModeType, 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";
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, 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,213 +632,1495 @@ 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(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(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.
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.
749
1025
  *
750
- * Reference: http://officeopenxml.com/drwPicFloating-textWrap.php
751
- *
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",
836
- OUTSIDE: "outside",
837
- TOP: "top"
838
- };
839
- /**
840
- * Number format types for page numbers and list numbering.
841
- *
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(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(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",
2118
+ OUTSIDE: "outside",
2119
+ TOP: "top"
2120
+ };
2121
+ /**
2122
+ * Number format types for page numbers and list numbering.
2123
+ *
842
2124
  * Reference: http://officeopenxml.com/WPnumbering-numFmt.php
843
2125
  *
844
2126
  * @publicApi
@@ -1259,40 +2541,76 @@ function parseImageRun(el, ctx) {
1259
2541
  }
1260
2542
  if (anchor && !inline) {
1261
2543
  const floating = {};
2544
+ const margins = {};
2545
+ const distT = attrNum(anchor, "distT");
2546
+ if (distT !== void 0) margins.top = distT;
2547
+ const distB = attrNum(anchor, "distB");
2548
+ if (distB !== void 0) margins.bottom = distB;
2549
+ const distL = attrNum(anchor, "distL");
2550
+ if (distL !== void 0) margins.left = distL;
2551
+ const distR = attrNum(anchor, "distR");
2552
+ if (distR !== void 0) margins.right = distR;
2553
+ if (Object.keys(margins).length > 0) floating.margins = margins;
1262
2554
  const posH = findChild(anchor, "wp:positionH");
1263
2555
  if (posH) {
1264
- const align = findChild(posH, "wp:align");
1265
- const posOffset = findChild(posH, "wp:posOffset");
1266
- if (align) floating.horizontalPosition = { align: textOf(align) };
1267
- else if (posOffset) {
1268
- const val = Number(textOf(posOffset));
1269
- if (!isNaN(val)) floating.horizontalPosition = { offset: val };
1270
- }
2556
+ const hp = readPosition(posH);
2557
+ if (hp) floating.horizontalPosition = hp;
1271
2558
  }
1272
2559
  const posV = findChild(anchor, "wp:positionV");
1273
2560
  if (posV) {
1274
- const align = findChild(posV, "wp:align");
1275
- const posOffset = findChild(posV, "wp:posOffset");
1276
- if (align) floating.verticalPosition = { align: textOf(align) };
1277
- else if (posOffset) {
1278
- const val = Number(textOf(posOffset));
1279
- if (!isNaN(val)) floating.verticalPosition = { offset: val };
1280
- }
1281
- }
1282
- for (const wrapType of [
1283
- "wrapSquare",
1284
- "wrapTight",
1285
- "wrapTopAndBottom",
1286
- "wrapNone"
1287
- ]) if (findChild(anchor, `wp:${wrapType}`)) {
1288
- floating.wrap = wrapType;
1289
- break;
2561
+ const vp = readPosition(posV);
2562
+ if (vp) floating.verticalPosition = vp;
1290
2563
  }
1291
- if (attrBool(anchor, "behindDoc")) floating.behindDocument = true;
2564
+ const wrap = readWrap(anchor);
2565
+ if (wrap) floating.wrap = wrap;
2566
+ const allowOverlap = attrBool(anchor, "allowOverlap");
2567
+ if (allowOverlap !== void 0) floating.allowOverlap = allowOverlap;
2568
+ const behindDoc = attrBool(anchor, "behindDoc");
2569
+ if (behindDoc !== void 0) floating.behindDocument = behindDoc;
2570
+ const locked = attrBool(anchor, "locked");
2571
+ if (locked !== void 0) floating.lockAnchor = locked;
2572
+ const layoutInCell = attrBool(anchor, "layoutInCell");
2573
+ if (layoutInCell !== void 0) floating.layoutInCell = layoutInCell;
2574
+ const relativeHeight = attrNum(anchor, "relativeHeight");
2575
+ if (relativeHeight !== void 0) floating.zIndex = relativeHeight;
1292
2576
  if (Object.keys(floating).length > 0) imageOpts.floating = floating;
1293
2577
  }
1294
2578
  return { image: imageOpts };
1295
2579
  }
2580
+ /** Map wp:positionH/V children + relativeFrom into a position-options object. */
2581
+ function readPosition(posEl) {
2582
+ const relative = attr(posEl, "relativeFrom");
2583
+ const alignEl = findChild(posEl, "wp:align");
2584
+ const posOffset = findChild(posEl, "wp:posOffset");
2585
+ const result = {};
2586
+ if (relative) result.relative = relative;
2587
+ if (alignEl) {
2588
+ const a = textOf(alignEl);
2589
+ if (a) result.align = a;
2590
+ } else if (posOffset) {
2591
+ const val = Number(textOf(posOffset));
2592
+ if (!isNaN(val)) result.offset = val;
2593
+ }
2594
+ return Object.keys(result).length > 0 ? result : void 0;
2595
+ }
2596
+ /** Map the wp:anchor wrap child element into a TextWrapping ({ type, side? }). */
2597
+ function readWrap(anchor) {
2598
+ const WRAP_TYPE = [
2599
+ ["wrapNone", TextWrappingType.NONE],
2600
+ ["wrapSquare", TextWrappingType.SQUARE],
2601
+ ["wrapTight", TextWrappingType.TIGHT],
2602
+ ["wrapTopAndBottom", TextWrappingType.TOP_AND_BOTTOM],
2603
+ ["wrapThrough", TextWrappingType.THROUGH]
2604
+ ];
2605
+ for (const [name, type] of WRAP_TYPE) {
2606
+ const el = findChild(anchor, `wp:${name}`);
2607
+ if (!el) continue;
2608
+ const wrap = { type };
2609
+ const side = attr(el, "wrapText");
2610
+ if (side) wrap.side = side;
2611
+ return wrap;
2612
+ }
2613
+ }
1296
2614
  function getDrawingExtent(el) {
1297
2615
  const inline = findDeep(el, "wp:inline")[0];
1298
2616
  const anchor = inline ? void 0 : findDeep(el, "wp:anchor")[0];
@@ -2149,577 +3467,200 @@ function parseMathFraction(el) {
2149
3467
  denominator: parseMathArg(el, "m:den")
2150
3468
  } };
2151
3469
  }
2152
- function parseMathRadical(el) {
2153
- const degree = parseMathArg(el, "m:deg");
2154
- return { radical: {
2155
- children: parseMathArg(el, "m:e"),
2156
- ...degree.length > 0 ? { degree } : {}
2157
- } };
2158
- }
2159
- function parseMathSuperScript(el) {
2160
- return { superScript: {
2161
- children: parseMathArg(el, "m:e"),
2162
- superScript: parseMathArg(el, "m:sup")
2163
- } };
2164
- }
2165
- function parseMathSubScript(el) {
2166
- return { subScript: {
2167
- children: parseMathArg(el, "m:e"),
2168
- subScript: parseMathArg(el, "m:sub")
2169
- } };
2170
- }
2171
- function parseMathSubSuperScript(el) {
2172
- return { subSuperScript: {
2173
- children: parseMathArg(el, "m:e"),
2174
- subScript: parseMathArg(el, "m:sub"),
2175
- superScript: parseMathArg(el, "m:sup")
2176
- } };
2177
- }
2178
- function parseMathNAry(el) {
2179
- const naryPr = findChild(el, "m:naryPr");
2180
- const chrEl = naryPr ? findChild(naryPr, "m:chr") : void 0;
2181
- const chrVal = chrEl ? attr(chrEl, "m:val") : void 0;
2182
- const baseChildren = parseMathArg(el, "m:e");
2183
- const sub = parseMathArg(el, "m:sub");
2184
- const sup = parseMathArg(el, "m:sup");
2185
- const common = {
2186
- children: baseChildren,
2187
- ...sub.length > 0 ? { subScript: sub } : {},
2188
- ...sup.length > 0 ? { superScript: sup } : {}
2189
- };
2190
- if (chrVal === "∑") return { sum: common };
2191
- return { integral: common };
2192
- }
2193
- function parseMathFunction(el) {
2194
- return { function: {
2195
- name: parseMathArg(el, "m:fName"),
2196
- children: parseMathArg(el, "m:e")
2197
- } };
2198
- }
2199
- function parseMathDelimiter(el) {
2200
- const dPr = findChild(el, "m:dPr");
2201
- const begChrEl = dPr ? findChild(dPr, "m:begChr") : void 0;
2202
- const begChr = begChrEl ? attr(begChrEl, "m:val") : "(";
2203
- const mathChildren = parseMathArg(el, "m:e");
2204
- switch (begChr) {
2205
- case "[": return { squareBrackets: mathChildren };
2206
- case "{": return { curlyBrackets: mathChildren };
2207
- case "<":
2208
- case "⟨": return { angledBrackets: mathChildren };
2209
- default: return { roundBrackets: mathChildren };
2210
- }
2211
- }
2212
- function parseMathMatrix(el) {
2213
- const rows = [];
2214
- for (const mr of children(el, "m:mr")) rows.push(parseMathArg(mr, "m:e"));
2215
- return { matrix: { rows } };
2216
- }
2217
- function parseMathAccent(el) {
2218
- const accPr = findChild(el, "m:accPr");
2219
- const chrEl = accPr ? findChild(accPr, "m:chr") : void 0;
2220
- const accentChar = chrEl ? attr(chrEl, "m:val") : void 0;
2221
- return { accent: {
3470
+ function parseMathRadical(el) {
3471
+ const degree = parseMathArg(el, "m:deg");
3472
+ return { radical: {
2222
3473
  children: parseMathArg(el, "m:e"),
2223
- ...accentChar ? { accentCharacter: accentChar } : {}
3474
+ ...degree.length > 0 ? { degree } : {}
2224
3475
  } };
2225
3476
  }
2226
- function parseMathBar(el) {
2227
- const barPr = findChild(el, "m:barPr");
2228
- const posEl = barPr ? findChild(barPr, "m:pos") : void 0;
2229
- const pos = posEl ? attr(posEl, "m:val") : "top";
2230
- return { bar: {
3477
+ function parseMathSuperScript(el) {
3478
+ return { superScript: {
2231
3479
  children: parseMathArg(el, "m:e"),
2232
- type: pos ?? "top"
3480
+ superScript: parseMathArg(el, "m:sup")
2233
3481
  } };
2234
3482
  }
2235
- function parseMathEqArr(el) {
2236
- const rows = [];
2237
- for (const e of children(el, "m:e")) rows.push(parseMathChildren(e));
2238
- return { eqArr: { rows } };
2239
- }
2240
- function parseMathLimitLower(el) {
2241
- return { limitLower: {
3483
+ function parseMathSubScript(el) {
3484
+ return { subScript: {
2242
3485
  children: parseMathArg(el, "m:e"),
2243
- limit: parseMathArg(el, "m:lim")
3486
+ subScript: parseMathArg(el, "m:sub")
2244
3487
  } };
2245
3488
  }
2246
- function parseMathLimitUpper(el) {
2247
- return { limitUpper: {
3489
+ function parseMathSubSuperScript(el) {
3490
+ return { subSuperScript: {
2248
3491
  children: parseMathArg(el, "m:e"),
2249
- limit: parseMathArg(el, "m:lim")
3492
+ subScript: parseMathArg(el, "m:sub"),
3493
+ superScript: parseMathArg(el, "m:sup")
2250
3494
  } };
2251
3495
  }
2252
- function parseMathArg(parent, childName) {
2253
- const container = findChild(parent, childName);
2254
- if (!container) return [];
2255
- return parseMathChildren(container);
2256
- }
2257
- //#endregion
2258
- //#region src/parts/paragraph/run/field.ts
2259
- /**
2260
- * Field module for WordprocessingML documents.
2261
- *
2262
- * This module provides support for complex fields, which are regions of text
2263
- * that can contain dynamic content such as page numbers, dates, or mail merge fields.
2264
- * Fields are delimited by field character elements (begin, separate, end).
2265
- *
2266
- * Reference: http://officeopenxml.com/WPrun.php
2267
- *
2268
- * @module
2269
- */
2270
- /**
2271
- * Field character types that delimit field regions.
2272
- *
2273
- * @internal
2274
- */
2275
- const FieldCharacterType = {
2276
- BEGIN: "begin",
2277
- END: "end",
2278
- SEPARATE: "separate"
2279
- };
2280
- /**
2281
- * Creates a field character element.
2282
- *
2283
- * ## XSD Schema
2284
- * ```xml
2285
- * <xsd:complexType name="CT_FldChar">
2286
- * <xsd:sequence>
2287
- * <xsd:element name="fldData" type="CT_Text" minOccurs="0"/>
2288
- * <xsd:element name="ffData" type="CT_FFData" minOccurs="0"/>
2289
- * <xsd:element name="numberingChange" type="CT_TrackChangeNumbering" minOccurs="0"/>
2290
- * </xsd:sequence>
2291
- * <xsd:attribute name="fldCharType" type="ST_FldCharType" use="required"/>
2292
- * <xsd:attribute name="fldLock" type="s:ST_OnOff"/>
2293
- * <xsd:attribute name="dirty" type="s:ST_OnOff"/>
2294
- * </xsd:complexType>
2295
- * ```
2296
- * @internal
2297
- */
2298
- const createFieldChar = (type, dirty, ffData, fldData, fieldLock) => {
2299
- const children = [];
2300
- if (fldData !== void 0) children.push(element("w:fldData", { "xml:space": "preserve" }, [fldData]));
2301
- if (ffData) children.push(ffData);
2302
- return element("w:fldChar", {
2303
- "w:dirty": dirty,
2304
- "w:fldLock": fieldLock,
2305
- "w:fldCharType": type
2306
- }, children.length > 0 ? children : void 0);
2307
- };
2308
- /**
2309
- * Creates the beginning of a complex field.
2310
- *
2311
- * The Begin element marks the start of a field. A field consists of a begin character,
2312
- * field instructions, an optional separate character, field result, and an end character.
2313
- *
2314
- * For form fields, pass `formField` to embed `w:ffData` within the begin `w:fldChar`.
2315
- *
2316
- * @param dirty - Whether the field should be recalculated
2317
- * @param formField - Optional form field data to embed in the begin character
2318
- *
2319
- * @example
2320
- * ```typescript
2321
- * // Simple field begin
2322
- * createBegin();
2323
- *
2324
- * // Form field (checkbox)
2325
- * createBegin(false, {
2326
- * name: "Check1",
2327
- * checkBox: { checked: true, sizeAuto: true },
2328
- * });
2329
- * ```
2330
- */
2331
- const createBegin = (dirty, formField, fieldLock) => createFieldChar(FieldCharacterType.BEGIN, dirty, formField ? createFormFieldData(formField) : void 0, void 0, fieldLock);
2332
- /**
2333
- * Creates the separator between field code and field result in a complex field.
2334
- *
2335
- * The Separate element divides the field code (instructions) from the field result
2336
- * (the computed value).
2337
- */
2338
- const createSeparate = (dirty) => createFieldChar(FieldCharacterType.SEPARATE, dirty);
2339
- /**
2340
- * Creates the end of a complex field.
2341
- *
2342
- * The End element marks the end of a field. Every field that begins with a Begin
2343
- * element must be terminated with an End element.
2344
- */
2345
- const createEnd = (dirty) => createFieldChar(FieldCharacterType.END, dirty);
2346
- //#endregion
2347
- //#region src/parts/paragraph/stringify.ts
2348
- /**
2349
- * Direct XML string builders for paragraph and run properties.
2350
- *
2351
- * Replaces `buildParagraphProperties() + xml()` and `buildRunProperties() + xml()`
2352
- * with direct string concatenation — zero intermediate IXmlableObject allocation,
2353
- * zero recursive xml() traversal. Follows PPTX/XLSX pattern.
2354
- *
2355
- * @module
2356
- */
2357
- /** On/off: `<w:name/>` for true, `<w:name w:val="0"/>` for false */
2358
- function onOff(name, val) {
2359
- return val ? `<${name}/>` : `<${name} w:val="0"/>`;
2360
- }
2361
- /** Build attrs string from key-value pairs, skipping undefined */
2362
- function attrParts(attrs) {
2363
- const parts = [];
2364
- for (const [key, val] of Object.entries(attrs)) if (val !== void 0) parts.push(`${key}="${val}"`);
2365
- return parts.join(" ");
2366
- }
2367
- function borderStr(name, opts) {
2368
- return `<${name} ${attrParts({
2369
- "w:val": opts.style,
2370
- "w:color": opts.color !== void 0 ? hexColorValue(opts.color) : void 0,
2371
- "w:sz": opts.size !== void 0 ? eighthPointMeasureValue(opts.size) : void 0,
2372
- "w:space": opts.space !== void 0 ? pointMeasureValue(opts.space) : void 0,
2373
- "w:themeColor": opts.themeColor,
2374
- "w:themeTint": opts.themeTint !== void 0 ? uCharHexNumber(opts.themeTint) : void 0,
2375
- "w:themeShade": opts.themeShade !== void 0 ? uCharHexNumber(opts.themeShade) : void 0,
2376
- "w:shadow": opts.shadow !== void 0 ? opts.shadow ? 1 : 0 : void 0,
2377
- "w:frame": opts.frame !== void 0 ? opts.frame ? 1 : 0 : void 0
2378
- })}/>`;
2379
- }
2380
- function shadingStr(opts) {
2381
- return `<w:shd ${attrParts({
2382
- "w:val": opts.type ?? "clear",
2383
- "w:color": opts.color !== void 0 ? hexColorValue(opts.color) : void 0,
2384
- "w:fill": opts.fill !== void 0 ? hexColorValue(opts.fill) : void 0,
2385
- "w:themeColor": opts.themeColor,
2386
- "w:themeTint": opts.themeTint !== void 0 ? uCharHexNumber(opts.themeTint) : void 0,
2387
- "w:themeShade": opts.themeShade !== void 0 ? uCharHexNumber(opts.themeShade) : void 0,
2388
- "w:themeFill": opts.themeFill,
2389
- "w:themeFillTint": opts.themeFillTint !== void 0 ? uCharHexNumber(opts.themeFillTint) : void 0,
2390
- "w:themeFillShade": opts.themeFillShade !== void 0 ? uCharHexNumber(opts.themeFillShade) : void 0
2391
- })}/>`;
2392
- }
2393
- function spacingStr(opts) {
2394
- return `<w:spacing ${attrParts({
2395
- "w:after": opts.after,
2396
- "w:afterAutospacing": opts.afterAutoSpacing !== void 0 ? opts.afterAutoSpacing ? 1 : 0 : void 0,
2397
- "w:afterLines": opts.afterLines !== void 0 ? decimalNumber(opts.afterLines) : void 0,
2398
- "w:before": opts.before,
2399
- "w:beforeAutospacing": opts.beforeAutoSpacing !== void 0 ? opts.beforeAutoSpacing ? 1 : 0 : void 0,
2400
- "w:beforeLines": opts.beforeLines !== void 0 ? decimalNumber(opts.beforeLines) : void 0,
2401
- "w:line": opts.line,
2402
- "w:lineRule": opts.lineRule
2403
- })}/>`;
2404
- }
2405
- function indentStr(opts) {
2406
- return `<w:ind ${attrParts({
2407
- "w:start": opts.start !== void 0 ? signedTwipsMeasureValue(opts.start) : void 0,
2408
- "w:startChars": opts.startChars !== void 0 ? decimalNumber(opts.startChars) : void 0,
2409
- "w:end": opts.end !== void 0 ? signedTwipsMeasureValue(opts.end) : void 0,
2410
- "w:endChars": opts.endChars !== void 0 ? decimalNumber(opts.endChars) : void 0,
2411
- "w:left": opts.left !== void 0 ? signedTwipsMeasureValue(opts.left) : void 0,
2412
- "w:leftChars": opts.leftChars !== void 0 ? decimalNumber(opts.leftChars) : void 0,
2413
- "w:right": opts.right !== void 0 ? signedTwipsMeasureValue(opts.right) : void 0,
2414
- "w:rightChars": opts.rightChars !== void 0 ? decimalNumber(opts.rightChars) : void 0,
2415
- "w:hanging": opts.hanging !== void 0 ? twipsMeasureValue(opts.hanging) : void 0,
2416
- "w:hangingChars": opts.hangingChars !== void 0 ? decimalNumber(opts.hangingChars) : void 0,
2417
- "w:firstLine": opts.firstLine !== void 0 ? twipsMeasureValue(opts.firstLine) : void 0,
2418
- "w:firstLineChars": opts.firstLineChars !== void 0 ? decimalNumber(opts.firstLineChars) : void 0
2419
- })}/>`;
2420
- }
2421
- function tabStopsStr(defs) {
2422
- return `<w:tabs>${defs.map(({ type, position, leader }) => {
2423
- return `<w:tab ${attrParts({
2424
- "w:val": type,
2425
- "w:pos": position,
2426
- "w:leader": leader
2427
- })}/>`;
2428
- }).join("")}</w:tabs>`;
2429
- }
2430
- function cnfStyleStr(opts) {
2431
- return `<w:cnfStyle ${attrParts({
2432
- "w:firstRow": opts.firstRow ? "1" : "0",
2433
- "w:lastRow": opts.lastRow ? "1" : "0",
2434
- "w:firstColumn": opts.firstColumn ? "1" : "0",
2435
- "w:lastColumn": opts.lastColumn ? "1" : "0",
2436
- "w:oddVBand": opts.oddVBand ? "1" : "0",
2437
- "w:evenVBand": opts.evenVBand ? "1" : "0",
2438
- "w:oddHBand": opts.oddHBand ? "1" : "0",
2439
- "w:evenHBand": opts.evenHBand ? "1" : "0",
2440
- "w:firstRowFirstColumn": opts.firstRowFirstColumn ? "1" : "0",
2441
- "w:firstRowLastColumn": opts.firstRowLastColumn ? "1" : "0",
2442
- "w:lastRowFirstColumn": opts.lastRowFirstColumn ? "1" : "0",
2443
- "w:lastRowLastColumn": opts.lastRowLastColumn ? "1" : "0"
2444
- })}/>`;
2445
- }
2446
- function framePrStr(opts) {
2447
- const alignment = opts.alignment;
2448
- const position = opts.position;
2449
- return `<w:framePr ${attrParts({
2450
- "w:xAlign": alignment?.x,
2451
- "w:yAlign": alignment?.y,
2452
- "w:hAnchor": opts.anchor?.horizontal,
2453
- "w:anchorLock": opts.anchorLock,
2454
- "w:vAnchor": opts.anchor?.vertical,
2455
- "w:dropCap": opts.dropCap,
2456
- "w:h": opts.height,
2457
- "w:lines": opts.lines,
2458
- "w:hRule": opts.rule,
2459
- "w:hSpace": opts.space?.horizontal,
2460
- "w:vSpace": opts.space?.vertical,
2461
- "w:w": opts.width,
2462
- "w:wrap": opts.wrap,
2463
- "w:x": position?.x,
2464
- "w:y": position?.y
2465
- })}/>`;
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 };
2466
3510
  }
2467
- function numPrStr(numberId, indentLevel, numberingChange) {
2468
- const idVal = typeof numberId === "string" ? `{${numberId}}` : numberId;
2469
- const parts = [`<w:ilvl w:val="${Math.min(indentLevel, 9)}"/>`, `<w:numId w:val="${idVal}"/>`];
2470
- if (numberingChange) {
2471
- const a = attrParts({
2472
- "w:original": numberingChange.original,
2473
- "w:id": numberingChange.id,
2474
- "w:author": numberingChange.author,
2475
- "w:date": numberingChange.date
2476
- });
2477
- parts.push(`<w:numberingChange ${a}/>`);
3511
+ function parseMathFunction(el) {
3512
+ return { function: {
3513
+ name: parseMathArg(el, "m:fName"),
3514
+ children: parseMathArg(el, "m:e")
3515
+ } };
3516
+ }
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 };
2478
3528
  }
2479
- return `<w:numPr>${parts.join("")}</w:numPr>`;
2480
3529
  }
2481
- function colorStr(colorOrOptions) {
2482
- if (typeof colorOrOptions === "string") return `<w:color w:val="${hexColorValue(colorOrOptions)}"/>`;
2483
- const opts = colorOrOptions;
2484
- return `<w:color ${attrParts({
2485
- "w:val": opts.val !== void 0 ? hexColorValue(opts.val) : void 0,
2486
- "w:themeColor": opts.themeColor,
2487
- "w:themeTint": opts.themeTint !== void 0 ? uCharHexNumber(opts.themeTint) : void 0,
2488
- "w:themeShade": opts.themeShade !== void 0 ? uCharHexNumber(opts.themeShade) : void 0
2489
- })}/>`;
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 } };
2490
3534
  }
2491
- function runFontsStr(nameOrAttrs, hint) {
2492
- if (typeof nameOrAttrs === "string") return `<w:rFonts ${attrParts({
2493
- "w:ascii": nameOrAttrs,
2494
- "w:cs": nameOrAttrs,
2495
- "w:eastAsia": nameOrAttrs,
2496
- "w:hAnsi": nameOrAttrs,
2497
- "w:hint": hint
2498
- })}/>`;
2499
- const attrs = nameOrAttrs;
2500
- return `<w:rFonts ${attrParts({
2501
- "w:ascii": attrs.ascii,
2502
- "w:asciiTheme": attrs.asciiTheme,
2503
- "w:cs": attrs.cs,
2504
- "w:cstheme": attrs.cstheme,
2505
- "w:eastAsia": attrs.eastAsia,
2506
- "w:eastAsiaTheme": attrs.eastAsiaTheme,
2507
- "w:hAnsi": attrs.hAnsi,
2508
- "w:hAnsiTheme": attrs.hAnsiTheme,
2509
- "w:hint": attrs.hint
2510
- })}/>`;
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
+ } };
2511
3543
  }
2512
- function underlineStr(type, color) {
2513
- return `<w:u ${attrParts({
2514
- "w:val": type ?? "single",
2515
- "w:color": color !== void 0 ? hexColorValue(color) : void 0
2516
- })}/>`;
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
+ } };
2517
3552
  }
2518
- function eastAsianLayoutStr(opts) {
2519
- return `<w:eastAsianLayout ${attrParts({
2520
- "w:id": opts.id !== void 0 ? decimalNumber(opts.id) : void 0,
2521
- "w:combine": opts.combine !== void 0 ? opts.combine ? 1 : 0 : void 0,
2522
- "w:combineBrackets": opts.combineBrackets,
2523
- "w:vert": opts.vert !== void 0 ? opts.vert ? 1 : 0 : void 0,
2524
- "w:vertCompress": opts.vertCompress !== void 0 ? opts.vertCompress ? 1 : 0 : void 0
2525
- })}/>`;
3553
+ function parseMathEqArr(el) {
3554
+ const rows = [];
3555
+ for (const e of children(el, "m:e")) rows.push(parseMathChildren(e));
3556
+ return { eqArr: { rows } };
2526
3557
  }
2527
- function languageStr(opts) {
2528
- return `<w:lang ${attrParts({
2529
- "w:val": opts.value,
2530
- "w:eastAsia": opts.eastAsia,
2531
- "w:bidi": opts.bidirectional
2532
- })}/>`;
3558
+ function parseMathLimitLower(el) {
3559
+ return { limitLower: {
3560
+ children: parseMathArg(el, "m:e"),
3561
+ limit: parseMathArg(el, "m:lim")
3562
+ } };
3563
+ }
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);
2533
3574
  }
3575
+ //#endregion
3576
+ //#region src/parts/paragraph/run/field.ts
2534
3577
  /**
2535
- * Build `<w:pPr>` XML string directly from options — zero IXmlableObject allocation.
3578
+ * Field module for WordprocessingML documents.
2536
3579
  *
2537
- * 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
2538
3587
  */
2539
- function stringifyParagraphProperties(options) {
2540
- const numberingReferences = [];
2541
- if (!options) return {
2542
- xml: void 0,
2543
- numberingReferences
2544
- };
2545
- const parts = [];
2546
- if (options.heading) parts.push(`<w:pStyle w:val="${escapeXml(options.heading)}"/>`);
2547
- if (options.bullet) parts.push("<w:pStyle w:val=\"ListParagraph\"/>");
2548
- if (options.numbering) {
2549
- if (!options.style && !options.heading) {
2550
- if (!options.numbering.custom) parts.push("<w:pStyle w:val=\"ListParagraph\"/>");
2551
- }
2552
- }
2553
- if (options.style) parts.push(`<w:pStyle w:val="${escapeXml(options.style)}"/>`);
2554
- if (options.keepNext !== void 0) parts.push(onOff("w:keepNext", options.keepNext));
2555
- if (options.keepLines !== void 0) parts.push(onOff("w:keepLines", options.keepLines));
2556
- if (options.pageBreakBefore) parts.push("<w:pageBreakBefore/>");
2557
- if (options.frame) parts.push(framePrStr(options.frame));
2558
- if (options.widowControl !== void 0) parts.push(onOff("w:widowControl", options.widowControl));
2559
- if (options.bullet) parts.push(`<w:numPr><w:ilvl w:val="${Math.min(options.bullet.level, 9)}"/><w:numId w:val="1"/></w:numPr>`);
2560
- if (options.numbering) {
2561
- numberingReferences.push({
2562
- instance: options.numbering.instance ?? 0,
2563
- reference: options.numbering.reference
2564
- });
2565
- const numId = `${options.numbering.reference}-${options.numbering.instance ?? 0}`;
2566
- parts.push(numPrStr(numId, options.numbering.level, options.numbering.numberingChange));
2567
- } else if (options.numbering === false) parts.push(numPrStr(0, 0));
2568
- if (options.border) {
2569
- const bParts = [];
2570
- if (options.border.top) bParts.push(borderStr("w:top", options.border.top));
2571
- if (options.border.left) bParts.push(borderStr("w:left", options.border.left));
2572
- if (options.border.bottom) bParts.push(borderStr("w:bottom", options.border.bottom));
2573
- if (options.border.right) bParts.push(borderStr("w:right", options.border.right));
2574
- if (options.border.between) bParts.push(borderStr("w:between", options.border.between));
2575
- if (options.border.bar) bParts.push(borderStr("w:bar", options.border.bar));
2576
- if (bParts.length) parts.push(`<w:pBdr>${bParts.join("")}</w:pBdr>`);
2577
- }
2578
- if (options.thematicBreak) parts.push(`<w:pBdr>${borderStr("w:bottom", {
2579
- color: "auto",
2580
- size: 6,
2581
- space: 1,
2582
- style: BorderStyle.SINGLE
2583
- })}</w:pBdr>`);
2584
- if (options.shading) parts.push(shadingStr(options.shading));
2585
- if (options.wordWrap) parts.push("<w:wordWrap w:val=\"0\"/>");
2586
- if (options.overflowPunctuation) parts.push(onOff("w:overflowPunct", options.overflowPunctuation));
2587
- const tabDefs = [
2588
- ...options.rightTabStop !== void 0 ? [{
2589
- position: options.rightTabStop,
2590
- type: "right"
2591
- }] : [],
2592
- ...options.tabStops ? options.tabStops : [],
2593
- ...options.leftTabStop !== void 0 ? [{
2594
- position: options.leftTabStop,
2595
- type: "left"
2596
- }] : []
2597
- ];
2598
- if (tabDefs.length > 0) parts.push(tabStopsStr(tabDefs));
2599
- if (options.bidirectional !== void 0) parts.push(onOff("w:bidi", options.bidirectional));
2600
- if (options.spacing) parts.push(spacingStr(options.spacing));
2601
- if (options.indent) parts.push(indentStr(options.indent));
2602
- if (options.contextualSpacing !== void 0) parts.push(onOff("w:contextualSpacing", options.contextualSpacing));
2603
- if (options.alignment) parts.push(`<w:jc w:val="${options.alignment}"/>`);
2604
- if (options.outlineLevel !== void 0) parts.push(`<w:outlineLvl w:val="${options.outlineLevel}"/>`);
2605
- if (options.divId !== void 0) parts.push(`<w:divId w:val="${options.divId}"/>`);
2606
- if (options.cnfStyle) parts.push(cnfStyleStr(options.cnfStyle));
2607
- if (options.suppressLineNumbers !== void 0) parts.push(onOff("w:suppressLineNumbers", options.suppressLineNumbers));
2608
- if (options.autoSpaceEastAsianText !== void 0) parts.push(onOff("w:autoSpaceDN", options.autoSpaceEastAsianText));
2609
- if (options.suppressAutoHyphens !== void 0) parts.push(onOff("w:suppressAutoHyphens", options.suppressAutoHyphens));
2610
- if (options.adjustRightInd !== void 0) parts.push(onOff("w:adjustRightInd", options.adjustRightInd));
2611
- if (options.snapToGrid !== void 0) parts.push(onOff("w:snapToGrid", options.snapToGrid));
2612
- if (options.mirrorIndents !== void 0) parts.push(onOff("w:mirrorIndents", options.mirrorIndents));
2613
- if (options.kinsoku !== void 0) parts.push(onOff("w:kinsoku", options.kinsoku));
2614
- if (options.topLinePunct !== void 0) parts.push(onOff("w:topLinePunct", options.topLinePunct));
2615
- if (options.autoSpaceDE !== void 0) parts.push(onOff("w:autoSpaceDE", options.autoSpaceDE));
2616
- if (options.textAlignment !== void 0) parts.push(`<w:textAlignment w:val="${options.textAlignment}"/>`);
2617
- if (options.textboxTightWrap !== void 0) parts.push(`<w:textboxTightWrap w:val="${options.textboxTightWrap}"/>`);
2618
- if (options.textDirection !== void 0) parts.push(`<w:textDirection w:val="${options.textDirection}"/>`);
2619
- if (options.suppressOverlap !== void 0) parts.push(onOff("w:suppressOverlap", options.suppressOverlap));
2620
- if (options.run) {
2621
- const inner = stringifyRunPropertiesInner(options.run);
2622
- if (inner !== void 0) {
2623
- const extra = [];
2624
- const runOpts = options.run;
2625
- if (runOpts.insertion) {
2626
- const { id, author, date } = runOpts.insertion;
2627
- extra.push(`<w:ins w:id="${id}" w:author="${escapeXml(author)}" w:date="${date}"/>`);
2628
- }
2629
- if (runOpts.deletion) {
2630
- const { id, author, date } = runOpts.deletion;
2631
- extra.push(`<w:del w:id="${id}" w:author="${escapeXml(author)}" w:date="${date}"/>`);
2632
- }
2633
- const body = inner + extra.join("");
2634
- parts.push(`<w:rPr>${body}</w:rPr>`);
2635
- }
2636
- }
2637
- if (options.revision) {
2638
- const rev = options.revision;
2639
- const { author: _a, date: _d, id: _i, ...originalProps } = rev;
2640
- const inner = stringifyParagraphProperties({
2641
- ...originalProps,
2642
- includeIfEmpty: true
2643
- });
2644
- parts.push(`<w:pPrChange w:author="${escapeXml(rev.author)}" w:date="${rev.date}" w:id="${rev.id}">${inner.xml ?? "<w:pPr/>"}</w:pPrChange>`);
2645
- }
2646
- const body = parts.join("");
2647
- return {
2648
- xml: options.includeIfEmpty || body.length > 0 ? `<w:pPr>${body}</w:pPr>` : void 0,
2649
- numberingReferences
2650
- };
2651
- }
2652
3588
  /**
2653
- * Build the inner content of `<w:rPr>` as a string.
2654
- * Returns undefined if no properties are set.
3589
+ * Field character types that delimit field regions.
3590
+ *
3591
+ * @internal
3592
+ */
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
2655
3615
  */
2656
- function stringifyRunPropertiesInner(opts) {
2657
- if (!opts) return void 0;
2658
- const parts = [];
2659
- if (opts.style) parts.push(`<w:rStyle w:val="${escapeXml(opts.style)}"/>`);
2660
- if (opts.font) if (typeof opts.font === "string") parts.push(runFontsStr(opts.font));
2661
- else if ("name" in opts.font) parts.push(runFontsStr(opts.font.name, opts.font.hint));
2662
- else parts.push(runFontsStr(opts.font));
2663
- if (opts.bold !== void 0) parts.push(onOff("w:b", opts.bold));
2664
- if ((opts.boldComplexScript === void 0 && opts.bold !== void 0 || opts.boldComplexScript) !== void 0) parts.push(onOff("w:bCs", opts.boldComplexScript ?? opts.bold));
2665
- if (opts.italic !== void 0) parts.push(onOff("w:i", opts.italic));
2666
- if ((opts.italicComplexScript === void 0 && opts.italic !== void 0 || opts.italicComplexScript) !== void 0) parts.push(onOff("w:iCs", opts.italicComplexScript ?? opts.italic));
2667
- if (opts.smallCaps !== void 0) parts.push(onOff("w:smallCaps", opts.smallCaps));
2668
- else if (opts.allCaps !== void 0) parts.push(onOff("w:caps", opts.allCaps));
2669
- if (opts.strike !== void 0) parts.push(onOff("w:strike", opts.strike));
2670
- if (opts.doubleStrike !== void 0) parts.push(onOff("w:dstrike", opts.doubleStrike));
2671
- if (opts.emboss !== void 0) parts.push(onOff("w:emboss", opts.emboss));
2672
- if (opts.imprint !== void 0) parts.push(onOff("w:imprint", opts.imprint));
2673
- if (opts.outline !== void 0) parts.push(onOff("w:outline", opts.outline));
2674
- if (opts.shadow !== void 0) parts.push(onOff("w:shadow", opts.shadow));
2675
- if (opts.webHidden !== void 0) parts.push(onOff("w:webHidden", opts.webHidden));
2676
- if (opts.noProof !== void 0) parts.push(onOff("w:noProof", opts.noProof));
2677
- if (opts.snapToGrid !== void 0) parts.push(onOff("w:snapToGrid", opts.snapToGrid));
2678
- if (opts.vanish) parts.push(onOff("w:vanish", opts.vanish));
2679
- if (opts.color) parts.push(colorStr(opts.color));
2680
- if (opts.characterSpacing) parts.push(`<w:spacing w:val="${signedTwipsMeasureValue(opts.characterSpacing)}"/>`);
2681
- if (opts.scale !== void 0) parts.push(`<w:w w:val="${opts.scale}"/>`);
2682
- if (opts.kern) parts.push(`<w:kern w:val="${hpsMeasureValue(opts.kern)}"/>`);
2683
- if (opts.position) parts.push(`<w:position w:val="${opts.position}"/>`);
2684
- if (opts.size !== void 0) parts.push(`<w:sz w:val="${hpsMeasureValue(opts.size * 2)}"/>`);
2685
- const szCs = opts.sizeComplexScript === void 0 || opts.sizeComplexScript === true ? opts.size : opts.sizeComplexScript;
2686
- if (szCs) parts.push(`<w:szCs w:val="${hpsMeasureValue(szCs * 2)}"/>`);
2687
- if (opts.highlight) parts.push(`<w:highlight w:val="${opts.highlight}"/>`);
2688
- if (opts.highlightComplexScript === true) {
2689
- if (opts.highlight) parts.push(`<w:highlightCs w:val="${opts.highlight}"/>`);
2690
- } else if (opts.highlightComplexScript !== void 0 && opts.highlightComplexScript !== false) parts.push(`<w:highlightCs w:val="${opts.highlightComplexScript}"/>`);
2691
- if (opts.underline) parts.push(underlineStr(opts.underline.type, opts.underline.color));
2692
- if (opts.effect) parts.push(`<w:effect w:val="${opts.effect}"/>`);
2693
- if (opts.border) parts.push(borderStr("w:bdr", opts.border));
2694
- if (opts.shading) parts.push(shadingStr(opts.shading));
2695
- if (opts.subScript) parts.push("<w:vertAlign w:val=\"subscript\"/>");
2696
- if (opts.superScript) parts.push("<w:vertAlign w:val=\"superscript\"/>");
2697
- if (opts.rightToLeft !== void 0) parts.push(onOff("w:rtl", opts.rightToLeft));
2698
- if (opts.emphasisMark) parts.push(`<w:em w:val="${opts.emphasisMark.type ?? "dot"}"/>`);
2699
- if (opts.language) parts.push(languageStr(opts.language));
2700
- if (opts.specVanish) parts.push("<w:specVanish/>");
2701
- if (opts.math) parts.push(onOff("w:oMath", opts.math));
2702
- if (opts.fitText !== void 0) parts.push(`<w:fitText w:val="${opts.fitText}"/>`);
2703
- if (opts.complexScript !== void 0) parts.push(onOff("w:cs", opts.complexScript));
2704
- if (opts.eastAsianLayout) parts.push(eastAsianLayoutStr(opts.eastAsianLayout));
2705
- if (opts.contentPartRId) parts.push(`<w:contentPart r:id="${opts.contentPartRId}"/>`);
2706
- if (opts.revision) {
2707
- const rev = opts.revision;
2708
- const { author: _a, date: _d, id: _i, ...originalProps } = rev;
2709
- const inner = stringifyRunPropertiesInner(originalProps);
2710
- parts.push(`<w:rPrChange w:author="${escapeXml(rev.author)}" w:date="${rev.date}" w:id="${rev.id}"><w:rPr>${inner ?? ""}</w:rPr></w:rPrChange>`);
2711
- }
2712
- return parts.length > 0 ? parts.join("") : void 0;
2713
- }
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
+ };
2714
3626
  /**
2715
- * Build `<w:rPr>` XML string directly from options — zero IXmlableObject allocation.
3627
+ * Creates the beginning of a complex field.
2716
3628
  *
2717
- * 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
+ * ```
2718
3648
  */
2719
- function stringifyRunProperties(opts) {
2720
- const inner = stringifyRunPropertiesInner(opts);
2721
- return inner ? `<w:rPr>${inner}</w:rPr>` : void 0;
2722
- }
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);
2723
3664
  //#endregion
2724
3665
  //#region src/parts/inline.ts
2725
3666
  /**
@@ -3032,6 +3973,11 @@ function stringifyChildDispatch(child, ctx) {
3032
3973
  if (sf.cachedValue !== void 0) return `<w:fldSimple w:instr="${escapeXml(sf.instruction)}"><w:r><w:t>${escapeXml(sf.cachedValue)}</w:t></w:r></w:fldSimple>`;
3033
3974
  return `<w:fldSimple w:instr="${escapeXml(sf.instruction)}"/>`;
3034
3975
  }
3976
+ if ("complexField" in child) {
3977
+ const cf = child.complexField;
3978
+ const resultXml = cf.result !== void 0 ? `<w:r><w:fldChar w:fldCharType="separate"/></w:r><w:r><w:t xml:space="preserve">${escapeXml(cf.result)}</w:t></w:r>` : "";
3979
+ return `<w:r><w:fldChar w:fldCharType="begin"/></w:r><w:r><w:instrText xml:space="preserve">${escapeXml(cf.instruction)}</w:instrText></w:r>` + resultXml + "<w:r><w:fldChar w:fldCharType=\"end\"/></w:r>";
3980
+ }
3035
3981
  if ("seqIdentifier" in child) {
3036
3982
  const id = child.seqIdentifier;
3037
3983
  return `<w:r><w:fldChar w:fldCharType="begin"/><w:instrText xml:space="preserve"> SEQ ${escapeXml(id)} </w:instrText><w:fldChar w:fldCharType="separate"/><w:fldChar w:fldCharType="end"/></w:r>`;
@@ -3074,26 +4020,30 @@ function stringifyChildDispatch(child, ctx) {
3074
4020
  }
3075
4021
  if ("customXml" in child) {
3076
4022
  const cx = child.customXml;
3077
- const attrs = [`w:element="${escapeXml(cx.element)}"`];
3078
- if (cx.uri) attrs.push(`w:uri="${escapeXml(cx.uri)}"`);
3079
- const parts = [];
3080
- if (cx.customXmlPr) {
3081
- const prParts = [];
3082
- if (cx.customXmlPr.placeholder) prParts.push(`<w:placeholder w:val="${escapeXml(cx.customXmlPr.placeholder)}"/>`);
3083
- if (cx.customXmlPr.attrs?.length) for (const a of cx.customXmlPr.attrs) {
3084
- const aa = [`w:name="${escapeXml(a.name)}"`, `w:val="${escapeXml(a.val)}"`];
3085
- if (a.uri) aa.push(`w:uri="${escapeXml(a.uri)}"`);
3086
- prParts.push(`<w:attr ${aa.join(" ")}/>`);
3087
- }
3088
- if (prParts.length) parts.push(`<w:customXmlPr>${prParts.join("")}</w:customXmlPr>`);
3089
- }
3090
- 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));
3091
4025
  else {
3092
4026
  const jr = stringifyChildDispatch(c, ctx);
3093
- if (jr !== void 0) parts.push(Array.isArray(jr) ? jr.join("") : jr);
3094
- 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("");
3095
4045
  }
3096
- return `<w:customXml ${attrs.join(" ")}>${parts.join("")}</w:customXml>`;
4046
+ return stringifySdtShell(s.properties, s.endProperties, contentXml);
3097
4047
  }
3098
4048
  }
3099
4049
  /** Serialize children of Dir/Bdo containers. */
@@ -3134,6 +4084,151 @@ function stringifyParagraphInline(opts, ctx) {
3134
4084
  return body ? `<w:p>${body}</w:p>` : "<w:p/>";
3135
4085
  }
3136
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
3137
4232
  //#region src/parts/table/stringify.ts
3138
4233
  /**
3139
4234
  * Direct XML string builders for table properties.
@@ -3178,20 +4273,15 @@ function cellMarginStr(tag, opts) {
3178
4273
  const inner = cellMarginChildrenStr(opts);
3179
4274
  return inner ? `<${tag}>${inner}</${tag}>` : void 0;
3180
4275
  }
3181
- const DEFAULT_BORDER = {
3182
- color: "auto",
3183
- size: 4,
3184
- style: BorderStyle.SINGLE
3185
- };
3186
4276
  function tableBordersStr(opts) {
3187
4277
  const parts = [];
3188
- parts.push(borderStr("w:top", opts.top ?? DEFAULT_BORDER));
3189
- parts.push(borderStr("w:left", opts.left ?? DEFAULT_BORDER));
3190
- parts.push(borderStr("w:bottom", opts.bottom ?? DEFAULT_BORDER));
3191
- parts.push(borderStr("w:right", opts.right ?? DEFAULT_BORDER));
3192
- parts.push(borderStr("w:insideH", opts.insideHorizontal ?? DEFAULT_BORDER));
3193
- parts.push(borderStr("w:insideV", opts.insideVertical ?? DEFAULT_BORDER));
3194
- return `<w:tblBorders>${parts.join("")}</w:tblBorders>`;
4278
+ if (opts.top) parts.push(borderStr("w:top", opts.top));
4279
+ if (opts.left) parts.push(borderStr("w:left", opts.left));
4280
+ if (opts.bottom) parts.push(borderStr("w:bottom", opts.bottom));
4281
+ if (opts.right) parts.push(borderStr("w:right", opts.right));
4282
+ if (opts.insideHorizontal) parts.push(borderStr("w:insideH", opts.insideHorizontal));
4283
+ if (opts.insideVertical) parts.push(borderStr("w:insideV", opts.insideVertical));
4284
+ return parts.length > 0 ? `<w:tblBorders>${parts.join("")}</w:tblBorders>` : void 0;
3195
4285
  }
3196
4286
  function cellBordersStr(opts) {
3197
4287
  const parts = [];
@@ -3276,7 +4366,10 @@ function stringifyTablePropertiesInner(options) {
3276
4366
  if (options.width) parts.push(tableWidthStr("w:tblW", options.width));
3277
4367
  if (options.alignment) parts.push(`<w:jc w:val="${options.alignment}"/>`);
3278
4368
  if (options.indent) parts.push(tableWidthStr("w:tblInd", options.indent));
3279
- if (options.borders) parts.push(tableBordersStr(options.borders));
4369
+ if (options.borders) {
4370
+ const bs = tableBordersStr(options.borders);
4371
+ if (bs) parts.push(bs);
4372
+ }
3280
4373
  if (options.shading) parts.push(shadingStr(options.shading));
3281
4374
  if (options.layout) parts.push(`<w:tblLayout w:type="${options.layout}"/>`);
3282
4375
  if (options.cellMargin) {
@@ -3400,7 +4493,10 @@ function stringifyTablePropertyExceptions(options) {
3400
4493
  if (options.alignment) parts.push(`<w:jc w:val="${options.alignment}"/>`);
3401
4494
  if (options.cellSpacing) parts.push(cellSpacingStr(options.cellSpacing));
3402
4495
  if (options.indent) parts.push(tableWidthStr("w:tblInd", options.indent));
3403
- if (options.borders) parts.push(tableBordersStr(options.borders));
4496
+ if (options.borders) {
4497
+ const bs = tableBordersStr(options.borders);
4498
+ if (bs) parts.push(bs);
4499
+ }
3404
4500
  if (options.shading) parts.push(shadingStr(options.shading));
3405
4501
  if (options.layout) parts.push(`<w:tblLayout w:type="${options.layout}"/>`);
3406
4502
  if (options.cellMargin) {
@@ -3422,6 +4518,27 @@ function stringifyTablePropertyExceptions(options) {
3422
4518
  }
3423
4519
  //#endregion
3424
4520
  //#region src/parts/table/descriptor.ts
4521
+ /**
4522
+ * Table (w:tbl) descriptor for DOCX.
4523
+ *
4524
+ * Stringifies pure JSON TableOptions into XML using direct string
4525
+ * concatenation — zero IXmlableObject, zero xml(), zero BaseXmlComponent.
4526
+ *
4527
+ * @module
4528
+ */
4529
+ const BORDER_STYLES = Object.values(BorderStyle);
4530
+ const THEME_COLORS = Object.values(ThemeColor);
4531
+ /** Parse track-change attributes (id/author/date) from w:ins/w:del/w:cellIns/w:cellDel. */
4532
+ function parseChangeAttrs(el) {
4533
+ const change = {};
4534
+ const id = attrNum(el, "w:id");
4535
+ if (id !== void 0) change.id = id;
4536
+ const author = attr(el, "w:author");
4537
+ if (author) change.author = author;
4538
+ const date = attr(el, "w:date");
4539
+ if (date) change.date = date;
4540
+ return change;
4541
+ }
3425
4542
  function buildTableGridXml(widths, revision) {
3426
4543
  const cols = widths.map((w) => `<w:gridCol w:w="${w}"/>`).join("");
3427
4544
  if (revision) {
@@ -3463,7 +4580,15 @@ function stringifyTableRow(row, ctx, extraCells) {
3463
4580
  const trPr = stringifyTableRowProperties(row);
3464
4581
  if (trPr) parts.push(trPr);
3465
4582
  const prefixCount = parts.length;
3466
- for (const cell of row.cells) parts.push(stringifyTableCell(cell, ctx));
4583
+ for (const cell of row.cells) if ("sdt" in cell) {
4584
+ const s = cell.sdt;
4585
+ const contentXml = (s.cells ?? []).map((c) => stringifyTableCell(c, ctx)).join("");
4586
+ parts.push(stringifySdtShell(s.properties, s.endProperties, contentXml));
4587
+ } else if ("customXml" in cell) {
4588
+ const cx = cell.customXml;
4589
+ const contentXml = (cx.children ?? []).map((c) => stringifyTableCell(c, ctx)).join("");
4590
+ parts.push(stringifyCustomXmlShell(cx, contentXml));
4591
+ } else parts.push(stringifyTableCell(cell, ctx));
3467
4592
  if (extraCells && extraCells.length > 0) for (const { cell, columnIndex } of extraCells) {
3468
4593
  const insertIdx = findInsertIndex(row.cells, columnIndex, prefixCount);
3469
4594
  parts.splice(insertIdx, 0, stringifyTableCell(cell, ctx));
@@ -3477,10 +4602,20 @@ function stringifyTableRow(row, ctx, extraCells) {
3477
4602
  const body = parts.join("");
3478
4603
  return body ? `<w:tr${attr}>${body}</w:tr>` : attr ? `<w:tr${attr}/>` : "<w:tr/>";
3479
4604
  }
4605
+ /** Type guard: a plain row (not SDT/customXml-wrapped). */
4606
+ function isPlainRow(r) {
4607
+ return !("sdt" in r) && !("customXml" in r);
4608
+ }
4609
+ /** Type guard: a plain cell (not SDT/customXml-wrapped). */
4610
+ function isPlainCell(c) {
4611
+ return !("sdt" in c) && !("customXml" in c);
4612
+ }
3480
4613
  function findInsertIndex(cells, columnIndex, prefixCount) {
3481
4614
  let colIdx = 0;
3482
4615
  for (let i = 0; i < cells.length; i++) {
3483
- const { columnSpan } = getCellSpans(cells[i]);
4616
+ const c = cells[i];
4617
+ if (!isPlainCell(c)) continue;
4618
+ const { columnSpan } = getCellSpans(c);
3484
4619
  colIdx += columnSpan;
3485
4620
  if (colIdx > columnIndex) return i + prefixCount;
3486
4621
  }
@@ -3492,9 +4627,12 @@ function findInsertIndex(cells, columnIndex, prefixCount) {
3492
4627
  function computeVerticalMergeCells(rows) {
3493
4628
  const extraMap = /* @__PURE__ */ new Map();
3494
4629
  for (let ri = 0; ri < rows.length - 1; ri++) {
3495
- const cells = rows[ri].cells;
4630
+ const row = rows[ri];
4631
+ if (!isPlainRow(row)) continue;
4632
+ const cells = row.cells;
3496
4633
  let colIdx = 0;
3497
4634
  for (const cell of cells) {
4635
+ if (!isPlainCell(cell)) continue;
3498
4636
  const typedCell = cell;
3499
4637
  const { columnSpan, rowSpan } = getCellSpans(typedCell);
3500
4638
  if (rowSpan > 1) {
@@ -3520,9 +4658,9 @@ const tableDesc = {
3520
4658
  kind: "custom",
3521
4659
  stringify(opts, ctx) {
3522
4660
  const parts = [];
3523
- const tblPr = stringifyTableProperties({
4661
+ const tblPrOpts = {
3524
4662
  alignment: opts.alignment,
3525
- borders: opts.borders ?? {},
4663
+ borders: opts.borders,
3526
4664
  caption: opts.caption,
3527
4665
  cellMargin: opts.margins,
3528
4666
  cellSpacing: opts.cellSpacing,
@@ -3536,16 +4674,27 @@ const tableDesc = {
3536
4674
  styleRowBandSize: opts.styleRowBandSize,
3537
4675
  tableLook: opts.tableLook,
3538
4676
  visuallyRightToLeft: opts.visuallyRightToLeft,
3539
- width: opts.width ?? { size: 100 }
3540
- });
3541
- if (tblPr) parts.push(tblPr);
3542
- const columnWidths = opts.columnWidths ?? Array(Math.max(...opts.rows.map((r) => r.cells.length))).fill(100);
4677
+ width: opts.width,
4678
+ includeIfEmpty: true
4679
+ };
4680
+ parts.push(stringifyTableProperties(tblPrOpts));
4681
+ const columnWidths = opts.columnWidths ?? Array(Math.max(1, ...opts.rows.map((r) => isPlainRow(r) ? r.cells.length : 0))).fill(100);
3543
4682
  parts.push(buildTableGridXml(columnWidths, opts.columnWidthsRevision));
3544
4683
  const extraCells = computeVerticalMergeCells(opts.rows);
3545
4684
  for (let ri = 0; ri < opts.rows.length; ri++) {
3546
- const row = opts.rows[ri];
3547
- const extras = extraCells.get(ri);
3548
- parts.push(stringifyTableRow(row, ctx, extras));
4685
+ const r = opts.rows[ri];
4686
+ if ("sdt" in r) {
4687
+ const sdt = r.sdt;
4688
+ const contentXml = (sdt.rows ?? []).map((rr) => stringifyTableRow(rr, ctx)).join("");
4689
+ parts.push(stringifySdtShell(sdt.properties, sdt.endProperties, contentXml));
4690
+ } else if ("customXml" in r) {
4691
+ const cx = r.customXml;
4692
+ const contentXml = (cx.children ?? []).map((rr) => stringifyTableRow(rr, ctx)).join("");
4693
+ parts.push(stringifyCustomXmlShell(cx, contentXml));
4694
+ } else {
4695
+ const extras = extraCells.get(ri);
4696
+ parts.push(stringifyTableRow(r, ctx, extras));
4697
+ }
3549
4698
  }
3550
4699
  return `<w:tbl>${parts.join("")}</w:tbl>`;
3551
4700
  },
@@ -3588,28 +4737,38 @@ function parseTablePropertiesEl(el) {
3588
4737
  }
3589
4738
  const tblBorders = findChild(el, "w:tblBorders");
3590
4739
  if (tblBorders) {
4740
+ const SIDE_KEYS = [
4741
+ ["top", "top"],
4742
+ ["left", "left"],
4743
+ ["bottom", "bottom"],
4744
+ ["right", "right"],
4745
+ ["insideH", "insideHorizontal"],
4746
+ ["insideV", "insideVertical"]
4747
+ ];
3591
4748
  const borders = {};
3592
- for (const side of [
3593
- "top",
3594
- "bottom",
3595
- "left",
3596
- "right",
3597
- "insideH",
3598
- "insideV"
3599
- ]) {
3600
- const sideEl = findChild(tblBorders, `w:${side}`);
3601
- if (sideEl) {
3602
- const b = {};
3603
- const val = attr(sideEl, "w:val");
3604
- if (val) b.style = val;
3605
- const color = attr(sideEl, "w:color");
3606
- if (color) b.color = color;
3607
- const sz = attrNum(sideEl, "w:sz");
3608
- if (sz !== void 0) b.size = sz;
3609
- const space = attrNum(sideEl, "w:space");
3610
- if (space !== void 0) b.space = space;
3611
- borders[side] = b;
3612
- }
4749
+ for (const [xmlSide, key] of SIDE_KEYS) {
4750
+ const sideEl = findChild(tblBorders, `w:${xmlSide}`);
4751
+ if (!sideEl) continue;
4752
+ const style = attr(sideEl, "w:val");
4753
+ if (!style || !BORDER_STYLES.includes(style)) continue;
4754
+ const sideOpts = { style };
4755
+ const color = attr(sideEl, "w:color");
4756
+ if (color) sideOpts.color = color;
4757
+ const size = attrNum(sideEl, "w:sz");
4758
+ if (size !== void 0) sideOpts.size = size;
4759
+ const space = attrNum(sideEl, "w:space");
4760
+ if (space !== void 0) sideOpts.space = space;
4761
+ const themeColor = attr(sideEl, "w:themeColor");
4762
+ if (themeColor && THEME_COLORS.includes(themeColor)) sideOpts.themeColor = themeColor;
4763
+ const themeTint = attr(sideEl, "w:themeTint");
4764
+ if (themeTint) sideOpts.themeTint = themeTint;
4765
+ const themeShade = attr(sideEl, "w:themeShade");
4766
+ if (themeShade) sideOpts.themeShade = themeShade;
4767
+ const shadow = attrBool(sideEl, "w:shadow");
4768
+ if (shadow !== void 0) sideOpts.shadow = shadow;
4769
+ const frame = attrBool(sideEl, "w:frame");
4770
+ if (frame !== void 0) sideOpts.frame = frame;
4771
+ borders[key] = sideOpts;
3613
4772
  }
3614
4773
  if (Object.keys(borders).length > 0) opts.borders = borders;
3615
4774
  }
@@ -3684,6 +4843,45 @@ function parseTablePropertiesEl(el) {
3684
4843
  ...type ? { type } : {}
3685
4844
  };
3686
4845
  }
4846
+ const bidiVisual = findChild(el, "w:bidiVisual");
4847
+ if (bidiVisual) opts.visuallyRightToLeft = attrBool(bidiVisual, "w:val") ?? true;
4848
+ const tblStyleRowBandSize = findChild(el, "w:tblStyleRowBandSize");
4849
+ if (tblStyleRowBandSize) {
4850
+ const val = attrNum(tblStyleRowBandSize, "w:val");
4851
+ if (val !== void 0) opts.styleRowBandSize = val;
4852
+ }
4853
+ const tblStyleColBandSize = findChild(el, "w:tblStyleColBandSize");
4854
+ if (tblStyleColBandSize) {
4855
+ const val = attrNum(tblStyleColBandSize, "w:val");
4856
+ if (val !== void 0) opts.styleColBandSize = val;
4857
+ }
4858
+ const tblCaption = findChild(el, "w:tblCaption");
4859
+ if (tblCaption) {
4860
+ const val = attr(tblCaption, "w:val");
4861
+ if (val) opts.caption = val;
4862
+ }
4863
+ const tblCellSpacing = findChild(el, "w:tblCellSpacing");
4864
+ if (tblCellSpacing) {
4865
+ const type = attr(tblCellSpacing, "w:type");
4866
+ const w = attrNum(tblCellSpacing, "w:w");
4867
+ if (w !== void 0) opts.cellSpacing = {
4868
+ value: w,
4869
+ ...type ? { type } : {}
4870
+ };
4871
+ }
4872
+ const tblPrChange = findChild(el, "w:tblPrChange");
4873
+ if (tblPrChange) {
4874
+ const rev = {};
4875
+ const author = attr(tblPrChange, "w:author");
4876
+ if (author) rev.author = author;
4877
+ const date = attr(tblPrChange, "w:date");
4878
+ if (date) rev.date = date;
4879
+ const id = attrNum(tblPrChange, "w:id");
4880
+ if (id !== void 0) rev.id = id;
4881
+ const innerTblPr = findChild(tblPrChange, "w:tblPr");
4882
+ if (innerTblPr) Object.assign(rev, parseTablePropertiesEl(innerTblPr));
4883
+ if (Object.keys(rev).length > 0) opts.revision = rev;
4884
+ }
3687
4885
  return opts;
3688
4886
  }
3689
4887
  function parseColumnWidthsEl(el) {
@@ -3707,10 +4905,105 @@ function parseTableRowPropertiesEl(el) {
3707
4905
  rule: rule ?? "atLeast"
3708
4906
  };
3709
4907
  }
4908
+ const cnfStyle = findChild(el, "w:cnfStyle");
4909
+ if (cnfStyle) {
4910
+ const val = attr(cnfStyle, "w:val");
4911
+ if (val) {
4912
+ const cnf = { val };
4913
+ const changed = attrBool(cnfStyle, "w:changed");
4914
+ if (changed !== void 0) cnf.changed = changed;
4915
+ opts.cnfStyle = cnf;
4916
+ }
4917
+ }
4918
+ const divId = findChild(el, "w:divId");
4919
+ if (divId) {
4920
+ const val = attrNum(divId, "w:val");
4921
+ if (val !== void 0) opts.divId = val;
4922
+ }
4923
+ const gridBefore = findChild(el, "w:gridBefore");
4924
+ if (gridBefore) {
4925
+ const val = attrNum(gridBefore, "w:val");
4926
+ if (val !== void 0) opts.gridBefore = val;
4927
+ }
4928
+ const gridAfter = findChild(el, "w:gridAfter");
4929
+ if (gridAfter) {
4930
+ const val = attrNum(gridAfter, "w:val");
4931
+ if (val !== void 0) opts.gridAfter = val;
4932
+ }
4933
+ const wBefore = findChild(el, "w:wBefore");
4934
+ if (wBefore) {
4935
+ const rawSize = attr(wBefore, "w:w");
4936
+ const type = attr(wBefore, "w:type");
4937
+ const size = type === "pct" ? rawSize : attrNum(wBefore, "w:w");
4938
+ if (size !== void 0) opts.widthBefore = {
4939
+ size,
4940
+ ...type ? { type } : {}
4941
+ };
4942
+ }
4943
+ const wAfter = findChild(el, "w:wAfter");
4944
+ if (wAfter) {
4945
+ const rawSize = attr(wAfter, "w:w");
4946
+ const type = attr(wAfter, "w:type");
4947
+ const size = type === "pct" ? rawSize : attrNum(wAfter, "w:w");
4948
+ if (size !== void 0) opts.widthAfter = {
4949
+ size,
4950
+ ...type ? { type } : {}
4951
+ };
4952
+ }
4953
+ const jc = findChild(el, "w:jc");
4954
+ if (jc) {
4955
+ const val = attr(jc, "w:val");
4956
+ if (val) opts.rowAlignment = val;
4957
+ }
4958
+ const hidden = findChild(el, "w:hidden");
4959
+ if (hidden) opts.hidden = attrBool(hidden, "w:val") ?? true;
4960
+ const tblCellSpacing = findChild(el, "w:tblCellSpacing");
4961
+ if (tblCellSpacing) {
4962
+ const type = attr(tblCellSpacing, "w:type");
4963
+ const w = attrNum(tblCellSpacing, "w:w");
4964
+ if (w !== void 0) opts.cellSpacing = {
4965
+ value: w,
4966
+ ...type ? { type } : {}
4967
+ };
4968
+ }
4969
+ const ins = findChild(el, "w:ins");
4970
+ if (ins) opts.insertion = parseChangeAttrs(ins);
4971
+ const del = findChild(el, "w:del");
4972
+ if (del) opts.deletion = parseChangeAttrs(del);
4973
+ const trPrChange = findChild(el, "w:trPrChange");
4974
+ if (trPrChange) {
4975
+ const rev = {};
4976
+ const author = attr(trPrChange, "w:author");
4977
+ if (author) rev.author = author;
4978
+ const date = attr(trPrChange, "w:date");
4979
+ if (date) rev.date = date;
4980
+ const id = attrNum(trPrChange, "w:id");
4981
+ if (id !== void 0) rev.id = id;
4982
+ const innerTrPr = findChild(trPrChange, "w:trPr");
4983
+ if (innerTrPr) Object.assign(rev, parseTableRowPropertiesEl(innerTrPr));
4984
+ if (Object.keys(rev).length > 0) opts.revision = rev;
4985
+ }
3710
4986
  const tblHeader = findChild(el, "w:tblHeader");
3711
4987
  if (tblHeader) opts.tableHeader = attrBool(tblHeader, "w:val") ?? true;
3712
4988
  const cantSplit = findChild(el, "w:cantSplit");
3713
4989
  if (cantSplit) opts.cantSplit = attrBool(cantSplit, "w:val") ?? true;
4990
+ const tblLook = findChild(el, "w:tblLook");
4991
+ if (tblLook) {
4992
+ const look = {};
4993
+ const firstRow = attrBool(tblLook, "w:firstRow");
4994
+ if (firstRow !== void 0) look.firstRow = firstRow;
4995
+ const lastRow = attrBool(tblLook, "w:lastRow");
4996
+ if (lastRow !== void 0) look.lastRow = lastRow;
4997
+ const firstColumn = attrBool(tblLook, "w:firstColumn");
4998
+ if (firstColumn !== void 0) look.firstColumn = firstColumn;
4999
+ const lastColumn = attrBool(tblLook, "w:lastColumn");
5000
+ if (lastColumn !== void 0) look.lastColumn = lastColumn;
5001
+ const noHBand = attrBool(tblLook, "w:noHBand");
5002
+ if (noHBand !== void 0) look.noHBand = noHBand;
5003
+ const noVBand = attrBool(tblLook, "w:noVBand");
5004
+ if (noVBand !== void 0) look.noVBand = noVBand;
5005
+ if (Object.keys(look).length > 0) opts.tableLook = look;
5006
+ }
3714
5007
  return opts;
3715
5008
  }
3716
5009
  function parseTableCellPropertiesEl(el) {
@@ -3750,13 +5043,17 @@ function parseTableCellPropertiesEl(el) {
3750
5043
  const tcBorders = findChild(el, "w:tcBorders");
3751
5044
  if (tcBorders) {
3752
5045
  const borders = {};
3753
- for (const side of [
3754
- "top",
3755
- "bottom",
3756
- "left",
3757
- "right"
5046
+ for (const [xmlSide, key] of [
5047
+ ["top", "top"],
5048
+ ["start", "start"],
5049
+ ["left", "left"],
5050
+ ["bottom", "bottom"],
5051
+ ["end", "end"],
5052
+ ["right", "right"],
5053
+ ["tl2br", "topLeftToBottomRight"],
5054
+ ["tr2bl", "topRightToBottomLeft"]
3758
5055
  ]) {
3759
- const sideEl = findChild(tcBorders, `w:${side}`);
5056
+ const sideEl = findChild(tcBorders, `w:${xmlSide}`);
3760
5057
  if (sideEl) {
3761
5058
  const b = {};
3762
5059
  const val = attr(sideEl, "w:val");
@@ -3765,12 +5062,13 @@ function parseTableCellPropertiesEl(el) {
3765
5062
  if (color) b.color = color;
3766
5063
  const sz = attrNum(sideEl, "w:sz");
3767
5064
  if (sz !== void 0) b.size = sz;
3768
- borders[side] = b;
5065
+ borders[key] = b;
3769
5066
  }
3770
5067
  }
3771
5068
  if (Object.keys(borders).length > 0) opts.borders = borders;
3772
5069
  }
3773
- if (findChild(el, "w:noWrap")) opts.noWrap = true;
5070
+ const noWrap = findChild(el, "w:noWrap");
5071
+ if (noWrap) opts.noWrap = attrBool(noWrap, "w:val") ?? true;
3774
5072
  const tcMar = findChild(el, "w:tcMar");
3775
5073
  if (tcMar) {
3776
5074
  const margins = {};
@@ -3799,6 +5097,48 @@ function parseTableCellPropertiesEl(el) {
3799
5097
  const val = attr(textDirection, "w:val");
3800
5098
  if (val) opts.textDirection = val;
3801
5099
  }
5100
+ const hMerge = findChild(el, "w:hMerge");
5101
+ if (hMerge) opts.horizontalMerge = attr(hMerge, "w:val") === "restart" ? "restart" : "continue";
5102
+ const tcFitText = findChild(el, "w:tcFitText");
5103
+ if (tcFitText) opts.fitText = attrBool(tcFitText, "w:val") ?? true;
5104
+ const hideMark = findChild(el, "w:hideMark");
5105
+ if (hideMark) opts.hideMark = attrBool(hideMark, "w:val") ?? true;
5106
+ const headersEl = findChild(el, "w:headers");
5107
+ if (headersEl) {
5108
+ const headerVals = [];
5109
+ for (const h of headersEl.elements ?? []) {
5110
+ if (h.name !== "w:header") continue;
5111
+ const val = attr(h, "w:val");
5112
+ if (val) headerVals.push(val);
5113
+ }
5114
+ if (headerVals.length > 0) opts.headers = headerVals;
5115
+ }
5116
+ const cellIns = findChild(el, "w:cellIns");
5117
+ if (cellIns) opts.insertion = parseChangeAttrs(cellIns);
5118
+ const cellDel = findChild(el, "w:cellDel");
5119
+ if (cellDel) opts.deletion = parseChangeAttrs(cellDel);
5120
+ const tcPrChange = findChild(el, "w:tcPrChange");
5121
+ if (tcPrChange) {
5122
+ const rev = {};
5123
+ const author = attr(tcPrChange, "w:author");
5124
+ if (author) rev.author = author;
5125
+ const date = attr(tcPrChange, "w:date");
5126
+ if (date) rev.date = date;
5127
+ const id = attrNum(tcPrChange, "w:id");
5128
+ if (id !== void 0) rev.id = id;
5129
+ const innerTcPr = findChild(tcPrChange, "w:tcPr");
5130
+ if (innerTcPr) Object.assign(rev, parseTableCellPropertiesEl(innerTcPr));
5131
+ if (Object.keys(rev).length > 0) opts.revision = rev;
5132
+ }
5133
+ const cellMerge = findChild(el, "w:cellMerge");
5134
+ if (cellMerge) {
5135
+ const cm = parseChangeAttrs(cellMerge);
5136
+ const vMerge = attr(cellMerge, "w:vMerge");
5137
+ if (vMerge) cm.verticalMerge = xsdVerticalMergeRev.from(vMerge);
5138
+ const vMergeOrig = attr(cellMerge, "w:vMergeOrig");
5139
+ if (vMergeOrig) cm.verticalMergeOriginal = xsdVerticalMergeRev.from(vMergeOrig);
5140
+ if (Object.keys(cm).length > 0) opts.cellMerge = cm;
5141
+ }
3802
5142
  return opts;
3803
5143
  }
3804
5144
  function parseTableCellEl(el, ctx) {
@@ -3821,8 +5161,45 @@ function parseTableRowEl(el, ctx) {
3821
5161
  const opts = {};
3822
5162
  const trPr = findChild(el, "w:trPr");
3823
5163
  if (trPr) Object.assign(opts, parseTableRowPropertiesEl(trPr));
5164
+ for (const [attrName, optKey] of [
5165
+ ["w:rsidRPr", "rsidRPr"],
5166
+ ["w:rsidR", "rsidR"],
5167
+ ["w:rsidDel", "rsidDel"],
5168
+ ["w:rsidTr", "rsidTr"]
5169
+ ]) {
5170
+ const val = attr(el, attrName);
5171
+ if (val) opts[optKey] = val;
5172
+ }
3824
5173
  const childCells = [];
3825
5174
  for (const child of el.elements ?? []) if (child.name === "w:tc") childCells.push(parseTableCellEl(child, ctx));
5175
+ else if (child.name === "w:sdt") {
5176
+ const sdtPr = findChild(child, "w:sdtPr");
5177
+ const properties = sdtPr ? parseSdtProperties(sdtPr) : {};
5178
+ const sdtEndPr = findChild(child, "w:sdtEndPr");
5179
+ const endProperties = sdtEndPr ? parseRunProperties(sdtEndPr) : void 0;
5180
+ const sdtContent = findChild(child, "w:sdtContent");
5181
+ const sdtCells = [];
5182
+ if (sdtContent) {
5183
+ for (const sub of sdtContent.elements ?? []) if (sub.name === "w:tc") sdtCells.push(parseTableCellEl(sub, ctx));
5184
+ }
5185
+ const sdt = { properties };
5186
+ if (sdtCells.length > 0) sdt.cells = sdtCells;
5187
+ if (endProperties) sdt.endProperties = endProperties;
5188
+ childCells.push({ sdt });
5189
+ } else if (child.name === "w:customXml") {
5190
+ const cx = { element: attr(child, "w:element") ?? "" };
5191
+ const cxUri = attr(child, "w:uri");
5192
+ if (cxUri) cx.uri = cxUri;
5193
+ const xmlPr = findChild(child, "w:customXmlPr");
5194
+ if (xmlPr) {
5195
+ const parsed = parseCustomXmlPr(xmlPr);
5196
+ if (parsed.placeholder !== void 0 || parsed.attributes !== void 0) cx.customXmlPr = parsed;
5197
+ }
5198
+ const cxCells = [];
5199
+ for (const sub of child.elements ?? []) if (sub.name === "w:tc") cxCells.push(parseTableCellEl(sub, ctx));
5200
+ if (cxCells.length > 0) cx.children = cxCells;
5201
+ childCells.push({ customXml: cx });
5202
+ }
3826
5203
  opts.cells = childCells;
3827
5204
  return opts;
3828
5205
  }
@@ -3834,6 +5211,34 @@ function parseTableEl(el, ctx) {
3834
5211
  if (colWidths.length > 0) opts.columnWidths = colWidths;
3835
5212
  const rows = [];
3836
5213
  for (const child of el.elements ?? []) if (child.name === "w:tr") rows.push(parseTableRowEl(child, ctx));
5214
+ else if (child.name === "w:sdt") {
5215
+ const sdtPr = findChild(child, "w:sdtPr");
5216
+ const properties = sdtPr ? parseSdtProperties(sdtPr) : {};
5217
+ const sdtEndPr = findChild(child, "w:sdtEndPr");
5218
+ const endProperties = sdtEndPr ? parseRunProperties(sdtEndPr) : void 0;
5219
+ const sdtContent = findChild(child, "w:sdtContent");
5220
+ const sdtRows = [];
5221
+ if (sdtContent) {
5222
+ for (const sub of sdtContent.elements ?? []) if (sub.name === "w:tr") sdtRows.push(parseTableRowEl(sub, ctx));
5223
+ }
5224
+ const sdt = { properties };
5225
+ if (sdtRows.length > 0) sdt.rows = sdtRows;
5226
+ if (endProperties) sdt.endProperties = endProperties;
5227
+ rows.push({ sdt });
5228
+ } else if (child.name === "w:customXml") {
5229
+ const cx = { element: attr(child, "w:element") ?? "" };
5230
+ const cxUri = attr(child, "w:uri");
5231
+ if (cxUri) cx.uri = cxUri;
5232
+ const xmlPr = findChild(child, "w:customXmlPr");
5233
+ if (xmlPr) {
5234
+ const parsed = parseCustomXmlPr(xmlPr);
5235
+ if (parsed.placeholder !== void 0 || parsed.attributes !== void 0) cx.customXmlPr = parsed;
5236
+ }
5237
+ const cxRows = [];
5238
+ for (const sub of child.elements ?? []) if (sub.name === "w:tr") cxRows.push(parseTableRowEl(sub, ctx));
5239
+ if (cxRows.length > 0) cx.children = cxRows;
5240
+ rows.push({ customXml: cx });
5241
+ }
3837
5242
  opts.rows = rows;
3838
5243
  return opts;
3839
5244
  }
@@ -4402,6 +5807,10 @@ const createHeaderFooterReference = (type, options) => `<${type} r:id="rId${opti
4402
5807
  *
4403
5808
  * @module
4404
5809
  */
5810
+ /** Valid page-number @w:fmt values (ST_NumberFormat). */
5811
+ const PAGE_NUMBER_FORMATS = Object.values(NumberFormat);
5812
+ /** Valid page-number @w:chapSep values (ST_ChapterSep). */
5813
+ const PAGE_NUMBER_SEPARATORS = Object.values(PageNumberSeparator);
4405
5814
  function stringifyBorderXml(tag, opts) {
4406
5815
  const attrs = [];
4407
5816
  if (opts.style !== void 0) attrs.push(`w:val="${opts.style}"`);
@@ -4516,14 +5925,14 @@ function stringifySectionPropertiesInner(opts) {
4516
5925
  const parts = [];
4517
5926
  appendHeaderFooterRefs(parts, "w:headerReference", opts.headerWrapperGroup);
4518
5927
  appendHeaderFooterRefs(parts, "w:footerReference", opts.footerWrapperGroup);
4519
- const { size: { width = sectionPageSizeDefaults.WIDTH, height = sectionPageSizeDefaults.HEIGHT, orientation = sectionPageSizeDefaults.ORIENTATION } = {}, margin: { top = sectionMarginDefaults.TOP, right = sectionMarginDefaults.RIGHT, bottom = sectionMarginDefaults.BOTTOM, left = sectionMarginDefaults.LEFT, header = sectionMarginDefaults.HEADER, footer = sectionMarginDefaults.FOOTER, gutter = sectionMarginDefaults.GUTTER } = {}, pageNumbers = {}, borders, textDirection } = opts.page ?? {};
5928
+ const { size: { width = sectionPageSizeDefaults.WIDTH, height = sectionPageSizeDefaults.HEIGHT, orientation = sectionPageSizeDefaults.ORIENTATION, code } = {}, margin: { top = sectionMarginDefaults.TOP, right = sectionMarginDefaults.RIGHT, bottom = sectionMarginDefaults.BOTTOM, left = sectionMarginDefaults.LEFT, header = sectionMarginDefaults.HEADER, footer = sectionMarginDefaults.FOOTER, gutter = sectionMarginDefaults.GUTTER } = {}, pageNumbers = {}, borders, textDirection } = opts.page ?? {};
4520
5929
  const { linePitch = 312, charSpace = 0, type: gridType = "lines" } = opts.grid ?? {};
4521
5930
  if (opts.footnotePr) parts.push(footnotePrXml("w:footnotePr", opts.footnotePr));
4522
5931
  if (opts.endnotePr) parts.push(footnotePrXml("w:endnotePr", opts.endnotePr));
4523
5932
  if (opts.type) parts.push(sectionTypeXml(opts.type));
4524
5933
  const pgW = orientation === "landscape" ? convertToTwip(height) : width;
4525
5934
  const pgH = orientation === "landscape" ? convertToTwip(width) : height;
4526
- parts.push(pageSizeXml(pgW, pgH, orientation));
5935
+ parts.push(pageSizeXml(pgW, pgH, orientation, code));
4527
5936
  parts.push(pageMarginXml(top, right, bottom, left, header, footer, gutter));
4528
5937
  if (borders) parts.push(pageBordersXml(borders));
4529
5938
  if (opts.lineNumbers) parts.push(lineNumberXml(opts.lineNumbers));
@@ -4605,6 +6014,8 @@ function parseSectionPropertiesEl(el) {
4605
6014
  if (h !== void 0) size.height = h;
4606
6015
  }
4607
6016
  if (orient) size.orientation = orient;
6017
+ const code = attrNum(pgSz, "w:code");
6018
+ if (code !== void 0) size.code = code;
4608
6019
  if (Object.keys(size).length > 0) page.size = size;
4609
6020
  const pgMar = findChild(el, "w:pgMar");
4610
6021
  if (pgMar) {
@@ -4629,7 +6040,11 @@ function parseSectionPropertiesEl(el) {
4629
6040
  const start = attrNum(pgNumType, "w:start");
4630
6041
  if (start !== void 0) pageNumbers.start = start;
4631
6042
  const fmt = attr(pgNumType, "w:fmt");
4632
- if (fmt) pageNumbers.formatType = fmt;
6043
+ if (fmt && PAGE_NUMBER_FORMATS.includes(fmt)) pageNumbers.formatType = fmt;
6044
+ const chapSep = attr(pgNumType, "w:chapSep");
6045
+ if (chapSep && PAGE_NUMBER_SEPARATORS.includes(chapSep)) pageNumbers.separator = chapSep;
6046
+ const chapStyle = attrNum(pgNumType, "w:chapStyle");
6047
+ if (chapStyle !== void 0) pageNumbers.chapStyle = chapStyle;
4633
6048
  if (Object.keys(pageNumbers).length > 0) page.pageNumbers = pageNumbers;
4634
6049
  }
4635
6050
  if (Object.keys(page).length > 0) opts.page = page;
@@ -4641,7 +6056,21 @@ function parseSectionPropertiesEl(el) {
4641
6056
  if (count !== void 0) column.count = count;
4642
6057
  const space = attrNum(cols, "w:space");
4643
6058
  if (space !== void 0) column.space = space;
4644
- if (attrBool(cols, "w:sep")) column.separator = true;
6059
+ const separate = attrBool(cols, "w:sep");
6060
+ if (separate !== void 0) column.separate = separate;
6061
+ const equalWidth = attrBool(cols, "w:equalWidth");
6062
+ if (equalWidth !== void 0) column.equalWidth = equalWidth;
6063
+ const colChildren = [];
6064
+ for (const colEl of cols.elements ?? []) {
6065
+ if (colEl.name !== "w:col") continue;
6066
+ const width = attrNum(colEl, "w:w");
6067
+ if (width === void 0) continue;
6068
+ const colAttr = { width };
6069
+ const colSpace = attrNum(colEl, "w:space");
6070
+ if (colSpace !== void 0) colAttr.space = colSpace;
6071
+ colChildren.push(colAttr);
6072
+ }
6073
+ if (colChildren.length > 0) column.children = colChildren;
4645
6074
  if (Object.keys(column).length > 0) opts.column = column;
4646
6075
  }
4647
6076
  const type = findChild(el, "w:type");
@@ -4793,6 +6222,6 @@ function parseNotePropertiesEl(el) {
4793
6222
  return opts;
4794
6223
  }
4795
6224
  //#endregion
4796
- export { createFormFieldData as $, stringifyParagraphProperties as A, HorizontalPositionAlign as B, createPageSize as C, stringifyChildDispatch as D, tableDesc as E, parseDrawingRun as F, createWrapTight as G, SpaceType as H, createVerticalPosition as I, WidthType as J, TextWrappingSide as K, createHorizontalPosition as L, parseMathChildren as M, drawingDesc as N, stringifyParagraphInline as O, resetDrawingIdGen as P, FormFieldTextType as Q, HorizontalPositionRelativeFrom as R, PageOrientation as S, setTableParseChild as T, VerticalPositionAlign as U, NumberFormat as V, createWrapThrough as W, TextDirection as X, BorderStyle as Y, VerticalMergeType as Z, DocumentGridType as _, HeaderFooterType as a, VerticalAnchor as at, sectionPageSizeDefaults as b, createSectionType as c, Media as ct, createPageMargin as d, parseFormFieldData as et, PageBorderDisplay as f, createPageNumberType as g, PageNumberSeparator as h, HeaderFooterReferenceType as i, TextVerticalType as it, stringifyRunProperties as j, stringifyRunInline as k, LineNumberRestartFormat as l, createTransformation as lt, PageBorderZOrder as m, sectionPropertiesDesc as n, TextHorzOverflowType as nt, createHeaderFooterReference as o, createBodyProperties as ot, PageBorderOffsetFrom as p, TextWrappingType as q, stringifySectionPropertiesXml as r, TextVertOverflowType as rt, SectionType as s, WORKAROUND2 as st, parseSectionPropertiesEl as t, TextBodyWrappingType as tt, createLineNumberType as u, createDocumentGrid as v, DocumentAttributeNamespaces as w, PageTextDirectionType as x, sectionMarginDefaults as y, VerticalPositionRelativeFrom as z };
6225
+ 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 };
4797
6226
 
4798
- //# sourceMappingURL=document-D8HDkCHs.mjs.map
6227
+ //# sourceMappingURL=document-DYH7tGnw.mjs.map