documonster 0.18.1 → 0.18.2

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,7 +1,8 @@
1
1
  import { VALID_SUBTOTALS, METRIC_DISPLAY_NAMES } from "../../../core/pivot-table-types.js";
2
2
  import { colCache } from "../../../utils/col-cache.js";
3
3
  import { BaseXform } from "../base-xform.js";
4
- import { RawXmlCollector, serializeAttributes } from "./raw-xml-collector.js";
4
+ import { RawXmlCollector } from "./raw-xml-collector.js";
5
+ import { parseXsdBoolean } from "../xsd-values.js";
5
6
  import { StdDocAttributes } from "../../../../xml/writer.js";
6
7
  /** OOXML sentinel field index meaning "data values" pseudo-field (used in pivotArea references) */
7
8
  const FIELD_INDEX_DATA_VALUES = 4294967294; // 0xFFFFFFFE
@@ -15,15 +16,75 @@ const DEFAULT_PIVOT_STYLE = "PivotStyleLight16";
15
16
  /** Valid OOXML axis values for pivot fields */
16
17
  const VALID_PIVOT_AXES = new Set(["axisRow", "axisCol", "axisPage", "axisValues"]);
17
18
  /** PivotFieldItem attribute keys — used to build the attrs object for rendering */
18
- const PIVOT_FIELD_ITEM_KEYS = ["x", "t", "h", "sd", "f", "m", "c", "d"];
19
+ const PIVOT_FIELD_ITEM_KEYS = ["n", "t", "h", "s", "sd", "f", "m", "c", "x", "d", "e"];
19
20
  /** Factory for default ParserState values */
20
21
  function createDefaultParserState() {
21
22
  return {
22
- currentSection: null,
23
- inPivotArea: false,
24
- inAutoSortScope: false
23
+ currentSection: null
25
24
  };
26
25
  }
26
+ /**
27
+ * Root `<pivotTableDefinition>` attributes that are parsed individually. Every
28
+ * other attribute is kept in `extraRootAttrs`: CT_pivotTableDefinition defines
29
+ * some seventy, and a whitelist that only re-emits the ones it names silently
30
+ * drops the rest (issue #238 lost rowHeaderCaption/colHeaderCaption that way).
31
+ */
32
+ const KNOWN_ROOT_KEYS = new Set([
33
+ // xmlns:xr is deliberately absent: an original mc:Ignorable="xr" needs the
34
+ // prefix declared even when the table carries no xr:uid.
35
+ "xmlns",
36
+ "xr:uid",
37
+ "name",
38
+ "cacheId",
39
+ "applyNumberFormats",
40
+ "applyBorderFormats",
41
+ "applyFontFormats",
42
+ "applyPatternFormats",
43
+ "applyAlignmentFormats",
44
+ "applyWidthHeightFormats",
45
+ "dataCaption",
46
+ "updatedVersion",
47
+ "minRefreshableVersion",
48
+ "createdVersion",
49
+ "useAutoFormatting",
50
+ "itemPrintTitles",
51
+ "indent",
52
+ "compact",
53
+ "compactData",
54
+ "multipleFieldFilters",
55
+ "outline",
56
+ "outlineData",
57
+ "chartFormat",
58
+ "colGrandTotals",
59
+ "rowGrandTotals",
60
+ "showError",
61
+ "errorCaption",
62
+ "showMissing",
63
+ "missingCaption",
64
+ "grandTotalCaption",
65
+ "rowHeaderCaption",
66
+ "colHeaderCaption",
67
+ "tag"
68
+ ]);
69
+ /** dataField attributes that are parsed individually */
70
+ const KNOWN_DATA_FIELD_KEYS = new Set([
71
+ "name",
72
+ "fld",
73
+ "subtotal",
74
+ "baseField",
75
+ "baseItem",
76
+ "numFmtId"
77
+ ]);
78
+ /** Collect the attributes not named in `known`, or undefined when there are none. */
79
+ function collectExtraAttrs(attributes, known) {
80
+ let extra;
81
+ for (const [k, v] of Object.entries(attributes)) {
82
+ if (!known.has(k) && v != null) {
83
+ (extra ?? (extra = {}))[k] = String(v);
84
+ }
85
+ }
86
+ return extra;
87
+ }
27
88
  /** Known pivotField attributes that we parse individually (hoisted to module scope) */
28
89
  const KNOWN_PIVOT_FIELD_KEYS = new Set([
29
90
  "axis",
@@ -48,12 +109,17 @@ class PivotTableXform extends BaseXform {
48
109
  this.currentRowItem = null;
49
110
  this.currentColItem = null;
50
111
  this.currentChartFormat = null;
51
- // Buffer for collecting pivotArea XML
52
- this.pivotAreaXmlBuffer = [];
53
- // Buffer for collecting autoSortScope XML
54
- this.autoSortScopeXmlBuffer = [];
112
+ this.currentDataField = null;
113
+ this.currentPageField = null;
55
114
  // Raw XML collectors (replacing manual in/depth/buffer triples)
56
115
  this.extLstCollector = new RawXmlCollector("extLst");
116
+ /**
117
+ * Verbatim capture of a subtree that belongs to the element being parsed —
118
+ * a field's own `<extLst>`, a pivotField's `<autoSortScope>`, a chartFormat's
119
+ * `<pivotArea>`. `commit` receives the XML once the subtree closes.
120
+ */
121
+ this.nestedCollector = new RawXmlCollector("");
122
+ this.nestedCommit = null;
57
123
  this.formatsCollector = new RawXmlCollector("formats");
58
124
  this.conditionalFormatsCollector = new RawXmlCollector("conditionalFormats");
59
125
  this.filtersCollector = new RawXmlCollector("filters");
@@ -75,8 +141,10 @@ class PivotTableXform extends BaseXform {
75
141
  this.currentRowItem = null;
76
142
  this.currentColItem = null;
77
143
  this.currentChartFormat = null;
78
- this.pivotAreaXmlBuffer = [];
79
- this.autoSortScopeXmlBuffer = [];
144
+ this.currentDataField = null;
145
+ this.currentPageField = null;
146
+ this.nestedCollector.reset();
147
+ this.nestedCommit = null;
80
148
  this.extLstCollector.reset();
81
149
  this.formatsCollector.reset();
82
150
  this.conditionalFormatsCollector.reset();
@@ -355,7 +423,17 @@ class PivotTableXform extends BaseXform {
355
423
  if (pf.name !== undefined) {
356
424
  pfAttrs.name = pf.name;
357
425
  }
358
- xmlStream.leafNode("pageField", pfAttrs);
426
+ if (pf.cap !== undefined) {
427
+ pfAttrs.cap = pf.cap;
428
+ }
429
+ if (pf.extLstXml) {
430
+ xmlStream.openNode("pageField", pfAttrs);
431
+ xmlStream.writeRaw(pf.extLstXml);
432
+ xmlStream.closeNode();
433
+ }
434
+ else {
435
+ xmlStream.leafNode("pageField", pfAttrs);
436
+ }
359
437
  }
360
438
  xmlStream.closeNode();
361
439
  }
@@ -379,7 +457,19 @@ class PivotTableXform extends BaseXform {
379
457
  if (dataField.numFmtId !== undefined) {
380
458
  dfAttrs.numFmtId = dataField.numFmtId;
381
459
  }
382
- xmlStream.leafNode("dataField", dfAttrs);
460
+ if (dataField.extraAttrs) {
461
+ for (const [k, v] of Object.entries(dataField.extraAttrs)) {
462
+ dfAttrs[k] ?? (dfAttrs[k] = v);
463
+ }
464
+ }
465
+ if (dataField.extLstXml) {
466
+ xmlStream.openNode("dataField", dfAttrs);
467
+ xmlStream.writeRaw(dataField.extLstXml);
468
+ xmlStream.closeNode();
469
+ }
470
+ else {
471
+ xmlStream.leafNode("dataField", dfAttrs);
472
+ }
383
473
  }
384
474
  xmlStream.closeNode();
385
475
  }
@@ -494,6 +584,15 @@ class PivotTableXform extends BaseXform {
494
584
  if (model.grandTotalCaption !== undefined) {
495
585
  attrs.grandTotalCaption = model.grandTotalCaption;
496
586
  }
587
+ if (model.rowHeaderCaption !== undefined) {
588
+ attrs.rowHeaderCaption = model.rowHeaderCaption;
589
+ }
590
+ if (model.colHeaderCaption !== undefined) {
591
+ attrs.colHeaderCaption = model.colHeaderCaption;
592
+ }
593
+ if (model.tag !== undefined) {
594
+ attrs.tag = model.tag;
595
+ }
497
596
  // Only write compact/compactData when false (non-default).
498
597
  // OOXML spec: absent = true (default). So if the original file had compact="0",
499
598
  // we must preserve it; omitting it would change semantics from false to true.
@@ -503,6 +602,11 @@ class PivotTableXform extends BaseXform {
503
602
  if (model.compactData === false) {
504
603
  attrs.compactData = "0";
505
604
  }
605
+ if (model.extraRootAttrs) {
606
+ for (const [k, v] of Object.entries(model.extraRootAttrs)) {
607
+ attrs[k] ?? (attrs[k] = v);
608
+ }
609
+ }
506
610
  return attrs;
507
611
  }
508
612
  /**
@@ -605,7 +709,9 @@ class PivotTableXform extends BaseXform {
605
709
  attrs[k] = v;
606
710
  }
607
711
  }
608
- const hasChildren = (field.items !== undefined && field.items.length > 0) || field.autoSortScopeXml !== undefined;
712
+ const hasChildren = (field.items !== undefined && field.items.length > 0) ||
713
+ field.autoSortScopeXml !== undefined ||
714
+ field.extLstXml !== undefined;
609
715
  if (hasChildren) {
610
716
  xmlStream.openNode("pivotField", attrs);
611
717
  if (field.items !== undefined && field.items.length > 0) {
@@ -624,6 +730,9 @@ class PivotTableXform extends BaseXform {
624
730
  if (field.autoSortScopeXml) {
625
731
  xmlStream.writeRaw(field.autoSortScopeXml);
626
732
  }
733
+ if (field.extLstXml) {
734
+ xmlStream.writeRaw(field.extLstXml);
735
+ }
627
736
  xmlStream.closeNode(); // pivotField
628
737
  }
629
738
  else {
@@ -635,7 +744,18 @@ class PivotTableXform extends BaseXform {
635
744
  // approach, while verbose, handles all OOXML edge cases correctly.
636
745
  parseOpen(node) {
637
746
  const { name, attributes } = node;
638
- // Collect raw XML verbatim for roundtrip preservation (5 collectors)
747
+ // A new root always starts a fresh table, even if a previous parse on this
748
+ // instance was abandoned mid-capture. The unprefixed main-namespace root
749
+ // cannot legally occur inside a captured subtree (extension content is
750
+ // namespaced, e.g. x14:pivotTableDefinition), so this cannot cut one short.
751
+ if (name === this.tag) {
752
+ this.reset();
753
+ }
754
+ // Collect raw XML verbatim for roundtrip preservation
755
+ if (this.nestedCollector.active) {
756
+ this.nestedCollector.feedOpen(name, attributes);
757
+ return true;
758
+ }
639
759
  if (this.extLstCollector.active) {
640
760
  this.extLstCollector.feedOpen(name, attributes);
641
761
  return true;
@@ -658,8 +778,7 @@ class PivotTableXform extends BaseXform {
658
778
  }
659
779
  switch (name) {
660
780
  case this.tag:
661
- // pivotTableDefinition root element
662
- this.reset();
781
+ // pivotTableDefinition root element (state was reset above)
663
782
  this.model = {
664
783
  name: attributes.name,
665
784
  cacheId: parseInt(attributes.cacheId ?? "0", 10),
@@ -682,11 +801,11 @@ class PivotTableXform extends BaseXform {
682
801
  useAutoFormatting: attributes.useAutoFormatting,
683
802
  itemPrintTitles: attributes.itemPrintTitles,
684
803
  indent: attributes.indent !== undefined ? parseInt(attributes.indent, 10) : undefined,
685
- compact: attributes.compact !== "0",
686
- compactData: attributes.compactData !== "0",
804
+ compact: parseXsdBoolean(attributes.compact) ?? true,
805
+ compactData: parseXsdBoolean(attributes.compactData) ?? true,
687
806
  multipleFieldFilters: attributes.multipleFieldFilters,
688
- outline: attributes.outline === "1",
689
- outlineData: attributes.outlineData === "1",
807
+ outline: parseXsdBoolean(attributes.outline) ?? false,
808
+ outlineData: parseXsdBoolean(attributes.outlineData) ?? false,
690
809
  chartFormat: attributes.chartFormat !== undefined ? parseInt(attributes.chartFormat, 10) : undefined,
691
810
  colGrandTotals: attributes.colGrandTotals,
692
811
  rowGrandTotals: attributes.rowGrandTotals,
@@ -695,6 +814,10 @@ class PivotTableXform extends BaseXform {
695
814
  showMissing: attributes.showMissing,
696
815
  missingCaption: attributes.missingCaption,
697
816
  grandTotalCaption: attributes.grandTotalCaption,
817
+ rowHeaderCaption: attributes.rowHeaderCaption,
818
+ colHeaderCaption: attributes.colHeaderCaption,
819
+ tag: attributes.tag,
820
+ extraRootAttrs: collectExtraAttrs(attributes, KNOWN_ROOT_KEYS),
698
821
  rowItems: [],
699
822
  colItems: [],
700
823
  chartFormats: [],
@@ -728,29 +851,23 @@ class PivotTableXform extends BaseXform {
728
851
  break;
729
852
  case "pivotField":
730
853
  if (this.state.currentSection === "pivotFields") {
731
- // Collect unknown attributes into extraAttrs bag for roundtrip preservation
732
- const extraAttrs = {};
733
- for (const [k, v] of Object.entries(attributes)) {
734
- if (!KNOWN_PIVOT_FIELD_KEYS.has(k)) {
735
- extraAttrs[k] = String(v);
736
- }
737
- }
738
854
  this.currentPivotField = {
739
855
  axis: VALID_PIVOT_AXES.has(attributes.axis)
740
856
  ? attributes.axis
741
857
  : undefined,
742
- dataField: attributes.dataField === "1",
858
+ dataField: parseXsdBoolean(attributes.dataField) ?? false,
743
859
  items: [],
744
- compact: attributes.compact !== "0",
745
- outline: attributes.outline !== "0",
746
- showAll: attributes.showAll !== "0",
747
- defaultSubtotal: attributes.defaultSubtotal !== "0",
860
+ compact: parseXsdBoolean(attributes.compact) ?? true,
861
+ outline: parseXsdBoolean(attributes.outline) ?? true,
862
+ showAll: parseXsdBoolean(attributes.showAll) ?? true,
863
+ defaultSubtotal: parseXsdBoolean(attributes.defaultSubtotal) ?? true,
748
864
  numFmtId: attributes.numFmtId !== undefined ? parseInt(attributes.numFmtId, 10) : undefined,
749
865
  sortType: attributes.sortType,
750
- subtotalTop: attributes.subtotalTop !== undefined ? attributes.subtotalTop === "1" : undefined,
751
- insertBlankRow: attributes.insertBlankRow === "1" ? true : undefined,
752
- multipleItemSelectionAllowed: attributes.multipleItemSelectionAllowed === "1" ? true : undefined,
753
- extraAttrs: Object.keys(extraAttrs).length > 0 ? extraAttrs : undefined
866
+ subtotalTop: parseXsdBoolean(attributes.subtotalTop),
867
+ insertBlankRow: parseXsdBoolean(attributes.insertBlankRow) === true ? true : undefined,
868
+ multipleItemSelectionAllowed: parseXsdBoolean(attributes.multipleItemSelectionAllowed) === true ? true : undefined,
869
+ // Unknown attributes kept verbatim for roundtrip preservation
870
+ extraAttrs: collectExtraAttrs(attributes, KNOWN_PIVOT_FIELD_KEYS)
754
871
  };
755
872
  }
756
873
  break;
@@ -776,8 +893,10 @@ class PivotTableXform extends BaseXform {
776
893
  case "autoSortScope":
777
894
  // Start collecting autoSortScope XML for the current pivotField
778
895
  if (this.currentPivotField) {
779
- this.state.inAutoSortScope = true;
780
- this.autoSortScopeXmlBuffer = ["<autoSortScope>"];
896
+ const field = this.currentPivotField;
897
+ this.startNested(name, attributes, xml => {
898
+ field.autoSortScopeXml = xml;
899
+ });
781
900
  }
782
901
  break;
783
902
  case "rowFields":
@@ -798,12 +917,14 @@ class PivotTableXform extends BaseXform {
798
917
  break;
799
918
  case "pageField":
800
919
  if (this.state.currentSection === "pageFields" && this.model) {
801
- this.model.pageFields.push({
920
+ this.currentPageField = {
802
921
  fld: parseInt(attributes.fld ?? "0", 10),
803
922
  item: attributes.item !== undefined ? parseInt(attributes.item, 10) : undefined,
804
923
  hier: attributes.hier !== undefined ? parseInt(attributes.hier, 10) : undefined,
805
- name: attributes.name
806
- });
924
+ name: attributes.name,
925
+ cap: attributes.cap
926
+ };
927
+ this.model.pageFields.push(this.currentPageField);
807
928
  }
808
929
  break;
809
930
  case "rowItems":
@@ -833,18 +954,8 @@ class PivotTableXform extends BaseXform {
833
954
  }
834
955
  break;
835
956
  case "x":
836
- // Handle x element inside row/col items or pivotArea
837
- if (this.state.inPivotArea) {
838
- // Collect x element for pivotArea XML (re-encode attribute values for XML safety)
839
- const xAttrs = serializeAttributes(attributes);
840
- if (this.state.inAutoSortScope) {
841
- this.autoSortScopeXmlBuffer.push(xAttrs ? `<x ${xAttrs}/>` : "<x/>");
842
- }
843
- else {
844
- this.pivotAreaXmlBuffer.push(xAttrs ? `<x ${xAttrs}/>` : "<x/>");
845
- }
846
- }
847
- else if (this.currentRowItem) {
957
+ // x element inside row/col items (pivotArea x elements are captured verbatim)
958
+ if (this.currentRowItem) {
848
959
  this.currentRowItem.x.push({ v: parseInt(attributes.v ?? "0", 10) });
849
960
  }
850
961
  else if (this.currentColItem) {
@@ -859,34 +970,17 @@ class PivotTableXform extends BaseXform {
859
970
  this.currentChartFormat = {
860
971
  chart: parseInt(attributes.chart ?? "0", 10),
861
972
  format: parseInt(attributes.format ?? "0", 10),
862
- series: attributes.series !== undefined ? attributes.series === "1" : undefined
973
+ series: parseXsdBoolean(attributes.series)
863
974
  };
864
975
  }
865
976
  break;
866
977
  case "pivotArea":
867
- // Start collecting pivotArea XML for chartFormat or autoSortScope
978
+ // A chartFormat's pivotArea is kept verbatim for roundtrip
868
979
  if (this.currentChartFormat) {
869
- this.state.inPivotArea = true;
870
- const attrsStr = serializeAttributes(attributes);
871
- this.pivotAreaXmlBuffer = [attrsStr ? `<pivotArea ${attrsStr}>` : "<pivotArea>"];
872
- }
873
- else if (this.state.inAutoSortScope) {
874
- this.state.inPivotArea = true;
875
- const attrsStr = serializeAttributes(attributes);
876
- this.autoSortScopeXmlBuffer.push(attrsStr ? `<pivotArea ${attrsStr}>` : "<pivotArea>");
877
- }
878
- break;
879
- case "references":
880
- case "reference":
881
- // Collect nested elements in pivotArea
882
- if (this.state.inPivotArea) {
883
- const attrsStr = serializeAttributes(attributes);
884
- if (this.state.inAutoSortScope) {
885
- this.autoSortScopeXmlBuffer.push(`<${name}${attrsStr ? " " + attrsStr : ""}>`);
886
- }
887
- else {
888
- this.pivotAreaXmlBuffer.push(`<${name}${attrsStr ? " " + attrsStr : ""}>`);
889
- }
980
+ const chartFormat = this.currentChartFormat;
981
+ this.startNested(name, attributes, xml => {
982
+ chartFormat.pivotAreaXml = xml;
983
+ });
890
984
  }
891
985
  break;
892
986
  case "field":
@@ -903,7 +997,7 @@ class PivotTableXform extends BaseXform {
903
997
  break;
904
998
  case "dataField":
905
999
  if (this.state.currentSection === "dataFields" && this.model) {
906
- this.model.dataFields.push({
1000
+ this.currentDataField = {
907
1001
  name: attributes.name ?? "",
908
1002
  fld: parseInt(attributes.fld ?? "0", 10),
909
1003
  baseField: attributes.baseField !== undefined ? parseInt(attributes.baseField, 10) : undefined,
@@ -911,8 +1005,10 @@ class PivotTableXform extends BaseXform {
911
1005
  subtotal: VALID_SUBTOTALS.has(attributes.subtotal)
912
1006
  ? attributes.subtotal
913
1007
  : undefined,
914
- numFmtId: attributes.numFmtId !== undefined ? parseInt(attributes.numFmtId, 10) : undefined
915
- });
1008
+ numFmtId: attributes.numFmtId !== undefined ? parseInt(attributes.numFmtId, 10) : undefined,
1009
+ extraAttrs: collectExtraAttrs(attributes, KNOWN_DATA_FIELD_KEYS)
1010
+ };
1011
+ this.model.dataFields.push(this.currentDataField);
916
1012
  }
917
1013
  break;
918
1014
  case "pivotTableStyleInfo":
@@ -929,9 +1025,20 @@ class PivotTableXform extends BaseXform {
929
1025
  }
930
1026
  break;
931
1027
  case "extLst":
932
- // Start collecting extLst XML for roundtrip preservation
1028
+ // Start collecting extLst XML for roundtrip preservation. A pivotField or
1029
+ // dataField may carry its own extLst; it belongs to that field, not the table.
933
1030
  if (this.model) {
934
- this.extLstCollector.start(attributes);
1031
+ // Per CT_PivotField / CT_DataField / CT_PageField these are the only
1032
+ // modeled elements that carry an extLst besides the table itself.
1033
+ const owner = this.currentPivotField ?? this.currentDataField ?? this.currentPageField;
1034
+ if (owner) {
1035
+ this.startNested(name, attributes, xml => {
1036
+ owner.extLstXml = xml;
1037
+ });
1038
+ }
1039
+ else {
1040
+ this.extLstCollector.start(attributes);
1041
+ }
935
1042
  }
936
1043
  break;
937
1044
  case "formats":
@@ -958,12 +1065,9 @@ class PivotTableXform extends BaseXform {
958
1065
  // This preserves elements like pivotHierarchies, rowHierarchiesUsage,
959
1066
  // colHierarchiesUsage, etc. that we don't individually model.
960
1067
  // R8-B1: Only activate at the top level of pivotTableDefinition — NOT inside
961
- // known sections (pivotFields, rowFields, etc.) or pivotArea/autoSortScope,
1068
+ // known sections (pivotFields, rowFields, etc.),
962
1069
  // otherwise the collector would steal subsequent tags from normal parsing.
963
- if (this.model &&
964
- this.state.currentSection === null &&
965
- !this.state.inPivotArea &&
966
- !this.state.inAutoSortScope) {
1070
+ if (this.model && this.state.currentSection === null) {
967
1071
  this.unknownCollector.startAs(name, attributes);
968
1072
  }
969
1073
  break;
@@ -972,7 +1076,10 @@ class PivotTableXform extends BaseXform {
972
1076
  }
973
1077
  parseText(text) {
974
1078
  // Forward text nodes to whichever raw-XML collector is active (B3 fix)
975
- if (this.extLstCollector.active) {
1079
+ if (this.nestedCollector.active) {
1080
+ this.nestedCollector.feedText(text);
1081
+ }
1082
+ else if (this.extLstCollector.active) {
976
1083
  this.extLstCollector.feedText(text);
977
1084
  }
978
1085
  else if (this.formatsCollector.active) {
@@ -988,6 +1095,11 @@ class PivotTableXform extends BaseXform {
988
1095
  this.unknownCollector.feedText(text);
989
1096
  }
990
1097
  }
1098
+ /** Begin capturing the subtree rooted at `name`; `commit` receives it when it closes. */
1099
+ startNested(name, attributes, commit) {
1100
+ this.nestedCommit = commit;
1101
+ this.nestedCollector.startAs(name, attributes);
1102
+ }
991
1103
  /** Feed a close-tag to a collector; if it completes, store the result on the model. */
992
1104
  tryCloseCollector(collector, name, modelKey) {
993
1105
  if (collector.feedClose(name)) {
@@ -999,6 +1111,14 @@ class PivotTableXform extends BaseXform {
999
1111
  }
1000
1112
  parseClose(name) {
1001
1113
  // Handle raw-XML collectors — close tags
1114
+ if (this.nestedCollector.active) {
1115
+ if (this.nestedCollector.feedClose(name)) {
1116
+ this.nestedCommit?.(this.nestedCollector.result);
1117
+ this.nestedCommit = null;
1118
+ this.nestedCollector.reset();
1119
+ }
1120
+ return true;
1121
+ }
1002
1122
  if (this.extLstCollector.active) {
1003
1123
  this.tryCloseCollector(this.extLstCollector, name, "extLstXml");
1004
1124
  return true;
@@ -1022,34 +1142,6 @@ class PivotTableXform extends BaseXform {
1022
1142
  }
1023
1143
  return true;
1024
1144
  }
1025
- // Handle pivotArea nested elements - close tags
1026
- if (this.state.inPivotArea) {
1027
- if (name === "pivotArea") {
1028
- if (this.state.inAutoSortScope) {
1029
- this.autoSortScopeXmlBuffer.push("</pivotArea>");
1030
- }
1031
- else {
1032
- this.pivotAreaXmlBuffer.push("</pivotArea>");
1033
- if (this.currentChartFormat) {
1034
- this.currentChartFormat.pivotAreaXml = this.pivotAreaXmlBuffer.join("");
1035
- }
1036
- this.pivotAreaXmlBuffer = [];
1037
- }
1038
- this.state.inPivotArea = false;
1039
- return true;
1040
- }
1041
- else if (name === "references" || name === "reference") {
1042
- if (this.state.inAutoSortScope) {
1043
- this.autoSortScopeXmlBuffer.push(`</${name}>`);
1044
- }
1045
- else {
1046
- this.pivotAreaXmlBuffer.push(`</${name}>`);
1047
- }
1048
- return true;
1049
- }
1050
- // x elements are self-closing, no need to handle close
1051
- return true;
1052
- }
1053
1145
  switch (name) {
1054
1146
  case this.tag:
1055
1147
  // End of pivotTableDefinition — store any collected unknown elements
@@ -1073,18 +1165,15 @@ class PivotTableXform extends BaseXform {
1073
1165
  this.currentPivotField = null;
1074
1166
  }
1075
1167
  break;
1168
+ case "dataField":
1169
+ this.currentDataField = null;
1170
+ break;
1171
+ case "pageField":
1172
+ this.currentPageField = null;
1173
+ break;
1076
1174
  case "items":
1077
1175
  // No close handling needed — item parsing guarded by currentPivotField
1078
1176
  break;
1079
- case "autoSortScope":
1080
- // Finish collecting autoSortScope XML
1081
- if (this.state.inAutoSortScope && this.currentPivotField) {
1082
- this.autoSortScopeXmlBuffer.push("</autoSortScope>");
1083
- this.currentPivotField.autoSortScopeXml = this.autoSortScopeXmlBuffer.join("");
1084
- this.autoSortScopeXmlBuffer = [];
1085
- this.state.inAutoSortScope = false;
1086
- }
1087
- break;
1088
1177
  case "i":
1089
1178
  // Finish row/col item
1090
1179
  if (this.currentRowItem && this.model) {
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * documonster v0.18.1
2
+ * documonster v0.18.2
3
3
  * Zero-dependency TypeScript toolkit for Excel, Word, PDF, CSV, Markdown, XML & ZIP — one API across Node.js, Bun & browsers.
4
4
  * (c) 2025 cjnoname
5
5
  * Released under the Apache-2.0 License
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * documonster v0.18.1
2
+ * documonster v0.18.2
3
3
  * Zero-dependency TypeScript toolkit for Excel, Word, PDF, CSV, Markdown, XML & ZIP — one API across Node.js, Bun & browsers.
4
4
  * (c) 2025 cjnoname
5
5
  * Released under the Apache-2.0 License
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * documonster v0.18.1
2
+ * documonster v0.18.2
3
3
  * Zero-dependency TypeScript toolkit for Excel, Word, PDF, CSV, Markdown, XML & ZIP — one API across Node.js, Bun & browsers.
4
4
  * (c) 2025 cjnoname
5
5
  * Released under the Apache-2.0 License