@cj-tech-master/excelts 5.0.6 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/dist/browser/index.browser.d.ts +1 -1
  2. package/dist/browser/index.d.ts +1 -1
  3. package/dist/browser/modules/archive/unzip/stream.base.js +19 -19
  4. package/dist/browser/modules/archive/unzip/stream.browser.js +3 -3
  5. package/dist/browser/modules/csv/csv-core.js +6 -3
  6. package/dist/browser/modules/csv/csv.browser.js +2 -2
  7. package/dist/browser/modules/csv/csv.js +1 -1
  8. package/dist/browser/modules/excel/anchor.js +4 -4
  9. package/dist/browser/modules/excel/cell.js +5 -5
  10. package/dist/browser/modules/excel/column.js +4 -4
  11. package/dist/browser/modules/excel/defined-names.js +1 -1
  12. package/dist/browser/modules/excel/form-control.js +1 -1
  13. package/dist/browser/modules/excel/pivot-table.d.ts +168 -17
  14. package/dist/browser/modules/excel/pivot-table.js +278 -70
  15. package/dist/browser/modules/excel/row.js +4 -4
  16. package/dist/browser/modules/excel/stream/workbook-reader.browser.js +4 -4
  17. package/dist/browser/modules/excel/stream/workbook-writer.browser.js +4 -4
  18. package/dist/browser/modules/excel/stream/worksheet-reader.js +1 -1
  19. package/dist/browser/modules/excel/stream/worksheet-writer.js +4 -4
  20. package/dist/browser/modules/excel/table.js +2 -2
  21. package/dist/browser/modules/excel/types.d.ts +0 -4
  22. package/dist/browser/modules/excel/utils/cell-format.js +3 -3
  23. package/dist/browser/modules/excel/utils/shared-formula.js +1 -1
  24. package/dist/browser/modules/excel/utils/stream-buf.js +2 -2
  25. package/dist/browser/modules/excel/utils/string-buf.js +1 -1
  26. package/dist/browser/modules/excel/workbook.d.ts +0 -2
  27. package/dist/browser/modules/excel/workbook.js +4 -5
  28. package/dist/browser/modules/excel/worksheet.js +9 -9
  29. package/dist/browser/modules/excel/xlsx/xform/base-xform.d.ts +5 -5
  30. package/dist/browser/modules/excel/xlsx/xform/base-xform.js +1 -1
  31. package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.js +2 -2
  32. package/dist/browser/modules/excel/xlsx/xform/book/workbook-view-xform.js +4 -4
  33. package/dist/browser/modules/excel/xlsx/xform/book/workbook-xform.js +16 -4
  34. package/dist/browser/modules/excel/xlsx/xform/comment/comment-xform.d.ts +1 -2
  35. package/dist/browser/modules/excel/xlsx/xform/comment/comments-xform.d.ts +1 -2
  36. package/dist/browser/modules/excel/xlsx/xform/comment/style/vml-position-xform.d.ts +3 -4
  37. package/dist/browser/modules/excel/xlsx/xform/comment/style/vml-position-xform.js +1 -1
  38. package/dist/browser/modules/excel/xlsx/xform/comment/style/vml-protection-xform.js +1 -1
  39. package/dist/browser/modules/excel/xlsx/xform/comment/vml-client-data-xform.d.ts +1 -2
  40. package/dist/browser/modules/excel/xlsx/xform/comment/vml-notes-xform.d.ts +1 -2
  41. package/dist/browser/modules/excel/xlsx/xform/comment/vml-shape-xform.js +1 -1
  42. package/dist/browser/modules/excel/xlsx/xform/comment/vml-textbox-xform.d.ts +1 -2
  43. package/dist/browser/modules/excel/xlsx/xform/comment/vml-textbox-xform.js +1 -1
  44. package/dist/browser/modules/excel/xlsx/xform/composite-xform.d.ts +1 -1
  45. package/dist/browser/modules/excel/xlsx/xform/core/app-xform.js +1 -1
  46. package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +24 -11
  47. package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +1 -1
  48. package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +1 -2
  49. package/dist/browser/modules/excel/xlsx/xform/drawing/cell-position-xform.d.ts +1 -2
  50. package/dist/browser/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +1 -2
  51. package/dist/browser/modules/excel/xlsx/xform/drawing/drawing-xform.d.ts +1 -2
  52. package/dist/browser/modules/excel/xlsx/xform/drawing/ext-xform.d.ts +1 -2
  53. package/dist/browser/modules/excel/xlsx/xform/drawing/ext-xform.js +2 -2
  54. package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +1 -1
  55. package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +1 -1
  56. package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +1 -2
  57. package/dist/browser/modules/excel/xlsx/xform/list-xform.d.ts +1 -2
  58. package/dist/browser/modules/excel/xlsx/xform/list-xform.js +3 -3
  59. package/dist/browser/modules/excel/xlsx/xform/pivot-table/cache-field-xform.d.ts +5 -15
  60. package/dist/browser/modules/excel/xlsx/xform/pivot-table/cache-field-xform.js +134 -52
  61. package/dist/browser/modules/excel/xlsx/xform/pivot-table/cache-field.d.ts +14 -15
  62. package/dist/browser/modules/excel/xlsx/xform/pivot-table/cache-field.js +244 -70
  63. package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-cache-definition-xform.d.ts +13 -29
  64. package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +213 -37
  65. package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-cache-records-xform.d.ts +7 -34
  66. package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-cache-records-xform.js +143 -41
  67. package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +101 -27
  68. package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +793 -408
  69. package/dist/browser/modules/excel/xlsx/xform/pivot-table/raw-xml-collector.d.ts +78 -0
  70. package/dist/browser/modules/excel/xlsx/xform/pivot-table/raw-xml-collector.js +149 -0
  71. package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.js +1 -1
  72. package/dist/browser/modules/excel/xlsx/xform/sheet/cf/cf-rule-xform.js +1 -1
  73. package/dist/browser/modules/excel/xlsx/xform/sheet/cf/conditional-formattings-xform.js +1 -1
  74. package/dist/browser/modules/excel/xlsx/xform/sheet/cf-ext/cf-rule-ext-xform.js +1 -1
  75. package/dist/browser/modules/excel/xlsx/xform/sheet/col-xform.js +3 -3
  76. package/dist/browser/modules/excel/xlsx/xform/sheet/data-validations-xform.js +3 -3
  77. package/dist/browser/modules/excel/xlsx/xform/sheet/header-footer-xform.js +6 -6
  78. package/dist/browser/modules/excel/xlsx/xform/sheet/page-setup-xform.js +11 -11
  79. package/dist/browser/modules/excel/xlsx/xform/sheet/row-xform.d.ts +1 -2
  80. package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +3 -3
  81. package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-view-xform.d.ts +1 -2
  82. package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +10 -10
  83. package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +12 -12
  84. package/dist/browser/modules/excel/xlsx/xform/strings/phonetic-text-xform.js +2 -2
  85. package/dist/browser/modules/excel/xlsx/xform/style/color-xform.js +1 -1
  86. package/dist/browser/modules/excel/xlsx/xform/style/style-xform.js +5 -5
  87. package/dist/browser/modules/excel/xlsx/xform/table/auto-filter-xform.d.ts +1 -2
  88. package/dist/browser/modules/excel/xlsx/xform/table/custom-filter-xform.d.ts +1 -2
  89. package/dist/browser/modules/excel/xlsx/xform/table/filter-column-xform.d.ts +1 -2
  90. package/dist/browser/modules/excel/xlsx/xform/table/filter-xform.d.ts +1 -2
  91. package/dist/browser/modules/excel/xlsx/xform/table/table-column-xform.d.ts +1 -2
  92. package/dist/browser/modules/excel/xlsx/xform/table/table-style-info-xform.d.ts +1 -2
  93. package/dist/browser/modules/excel/xlsx/xform/table/table-xform.d.ts +1 -2
  94. package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +5 -2
  95. package/dist/browser/modules/excel/xlsx/xlsx.browser.js +88 -54
  96. package/dist/browser/utils/env.d.ts +0 -5
  97. package/dist/browser/utils/env.js +0 -7
  98. package/dist/browser/utils/utils.base.d.ts +8 -13
  99. package/dist/browser/utils/utils.base.js +40 -47
  100. package/dist/browser/utils/utils.browser.d.ts +1 -1
  101. package/dist/browser/utils/utils.browser.js +1 -1
  102. package/dist/browser/utils/utils.d.ts +1 -1
  103. package/dist/browser/utils/utils.js +1 -1
  104. package/dist/cjs/modules/archive/unzip/stream.base.js +19 -19
  105. package/dist/cjs/modules/archive/unzip/stream.browser.js +3 -3
  106. package/dist/cjs/modules/csv/csv-core.js +6 -3
  107. package/dist/cjs/modules/csv/csv.browser.js +2 -2
  108. package/dist/cjs/modules/csv/csv.js +1 -1
  109. package/dist/cjs/modules/excel/anchor.js +4 -4
  110. package/dist/cjs/modules/excel/cell.js +5 -5
  111. package/dist/cjs/modules/excel/column.js +4 -4
  112. package/dist/cjs/modules/excel/defined-names.js +1 -1
  113. package/dist/cjs/modules/excel/form-control.js +1 -1
  114. package/dist/cjs/modules/excel/pivot-table.js +280 -70
  115. package/dist/cjs/modules/excel/row.js +4 -4
  116. package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +4 -4
  117. package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +4 -4
  118. package/dist/cjs/modules/excel/stream/worksheet-reader.js +1 -1
  119. package/dist/cjs/modules/excel/stream/worksheet-writer.js +4 -4
  120. package/dist/cjs/modules/excel/table.js +2 -2
  121. package/dist/cjs/modules/excel/utils/cell-format.js +3 -3
  122. package/dist/cjs/modules/excel/utils/shared-formula.js +1 -1
  123. package/dist/cjs/modules/excel/utils/stream-buf.js +2 -2
  124. package/dist/cjs/modules/excel/utils/string-buf.js +1 -1
  125. package/dist/cjs/modules/excel/workbook.js +4 -5
  126. package/dist/cjs/modules/excel/worksheet.js +9 -9
  127. package/dist/cjs/modules/excel/xlsx/xform/base-xform.js +1 -1
  128. package/dist/cjs/modules/excel/xlsx/xform/book/defined-name-xform.js +2 -2
  129. package/dist/cjs/modules/excel/xlsx/xform/book/workbook-view-xform.js +4 -4
  130. package/dist/cjs/modules/excel/xlsx/xform/book/workbook-xform.js +16 -4
  131. package/dist/cjs/modules/excel/xlsx/xform/comment/style/vml-position-xform.js +1 -1
  132. package/dist/cjs/modules/excel/xlsx/xform/comment/style/vml-protection-xform.js +1 -1
  133. package/dist/cjs/modules/excel/xlsx/xform/comment/vml-shape-xform.js +1 -1
  134. package/dist/cjs/modules/excel/xlsx/xform/comment/vml-textbox-xform.js +1 -1
  135. package/dist/cjs/modules/excel/xlsx/xform/core/app-xform.js +1 -1
  136. package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +24 -11
  137. package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +1 -1
  138. package/dist/cjs/modules/excel/xlsx/xform/drawing/ext-xform.js +2 -2
  139. package/dist/cjs/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +1 -1
  140. package/dist/cjs/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +1 -1
  141. package/dist/cjs/modules/excel/xlsx/xform/list-xform.js +3 -3
  142. package/dist/cjs/modules/excel/xlsx/xform/pivot-table/cache-field-xform.js +133 -51
  143. package/dist/cjs/modules/excel/xlsx/xform/pivot-table/cache-field.js +245 -71
  144. package/dist/cjs/modules/excel/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +212 -36
  145. package/dist/cjs/modules/excel/xlsx/xform/pivot-table/pivot-cache-records-xform.js +142 -40
  146. package/dist/cjs/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +793 -408
  147. package/dist/cjs/modules/excel/xlsx/xform/pivot-table/raw-xml-collector.js +153 -0
  148. package/dist/cjs/modules/excel/xlsx/xform/sheet/cell-xform.js +1 -1
  149. package/dist/cjs/modules/excel/xlsx/xform/sheet/cf/cf-rule-xform.js +1 -1
  150. package/dist/cjs/modules/excel/xlsx/xform/sheet/cf/conditional-formattings-xform.js +1 -1
  151. package/dist/cjs/modules/excel/xlsx/xform/sheet/cf-ext/cf-rule-ext-xform.js +1 -1
  152. package/dist/cjs/modules/excel/xlsx/xform/sheet/col-xform.js +3 -3
  153. package/dist/cjs/modules/excel/xlsx/xform/sheet/data-validations-xform.js +3 -3
  154. package/dist/cjs/modules/excel/xlsx/xform/sheet/header-footer-xform.js +6 -6
  155. package/dist/cjs/modules/excel/xlsx/xform/sheet/page-setup-xform.js +11 -11
  156. package/dist/cjs/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +3 -3
  157. package/dist/cjs/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +10 -10
  158. package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +12 -12
  159. package/dist/cjs/modules/excel/xlsx/xform/strings/phonetic-text-xform.js +2 -2
  160. package/dist/cjs/modules/excel/xlsx/xform/style/color-xform.js +1 -1
  161. package/dist/cjs/modules/excel/xlsx/xform/style/style-xform.js +5 -5
  162. package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +88 -54
  163. package/dist/cjs/utils/env.js +0 -8
  164. package/dist/cjs/utils/utils.base.js +41 -54
  165. package/dist/cjs/utils/utils.browser.js +2 -7
  166. package/dist/cjs/utils/utils.js +2 -7
  167. package/dist/esm/modules/archive/unzip/stream.base.js +19 -19
  168. package/dist/esm/modules/archive/unzip/stream.browser.js +3 -3
  169. package/dist/esm/modules/csv/csv-core.js +6 -3
  170. package/dist/esm/modules/csv/csv.browser.js +2 -2
  171. package/dist/esm/modules/csv/csv.js +1 -1
  172. package/dist/esm/modules/excel/anchor.js +4 -4
  173. package/dist/esm/modules/excel/cell.js +5 -5
  174. package/dist/esm/modules/excel/column.js +4 -4
  175. package/dist/esm/modules/excel/defined-names.js +1 -1
  176. package/dist/esm/modules/excel/form-control.js +1 -1
  177. package/dist/esm/modules/excel/pivot-table.js +278 -70
  178. package/dist/esm/modules/excel/row.js +4 -4
  179. package/dist/esm/modules/excel/stream/workbook-reader.browser.js +4 -4
  180. package/dist/esm/modules/excel/stream/workbook-writer.browser.js +4 -4
  181. package/dist/esm/modules/excel/stream/worksheet-reader.js +1 -1
  182. package/dist/esm/modules/excel/stream/worksheet-writer.js +4 -4
  183. package/dist/esm/modules/excel/table.js +2 -2
  184. package/dist/esm/modules/excel/utils/cell-format.js +3 -3
  185. package/dist/esm/modules/excel/utils/shared-formula.js +1 -1
  186. package/dist/esm/modules/excel/utils/stream-buf.js +2 -2
  187. package/dist/esm/modules/excel/utils/string-buf.js +1 -1
  188. package/dist/esm/modules/excel/workbook.js +4 -5
  189. package/dist/esm/modules/excel/worksheet.js +9 -9
  190. package/dist/esm/modules/excel/xlsx/xform/base-xform.js +1 -1
  191. package/dist/esm/modules/excel/xlsx/xform/book/defined-name-xform.js +2 -2
  192. package/dist/esm/modules/excel/xlsx/xform/book/workbook-view-xform.js +4 -4
  193. package/dist/esm/modules/excel/xlsx/xform/book/workbook-xform.js +16 -4
  194. package/dist/esm/modules/excel/xlsx/xform/comment/style/vml-position-xform.js +1 -1
  195. package/dist/esm/modules/excel/xlsx/xform/comment/style/vml-protection-xform.js +1 -1
  196. package/dist/esm/modules/excel/xlsx/xform/comment/vml-shape-xform.js +1 -1
  197. package/dist/esm/modules/excel/xlsx/xform/comment/vml-textbox-xform.js +1 -1
  198. package/dist/esm/modules/excel/xlsx/xform/core/app-xform.js +1 -1
  199. package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +24 -11
  200. package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +1 -1
  201. package/dist/esm/modules/excel/xlsx/xform/drawing/ext-xform.js +2 -2
  202. package/dist/esm/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +1 -1
  203. package/dist/esm/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +1 -1
  204. package/dist/esm/modules/excel/xlsx/xform/list-xform.js +3 -3
  205. package/dist/esm/modules/excel/xlsx/xform/pivot-table/cache-field-xform.js +134 -52
  206. package/dist/esm/modules/excel/xlsx/xform/pivot-table/cache-field.js +244 -70
  207. package/dist/esm/modules/excel/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +213 -37
  208. package/dist/esm/modules/excel/xlsx/xform/pivot-table/pivot-cache-records-xform.js +143 -41
  209. package/dist/esm/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +793 -408
  210. package/dist/esm/modules/excel/xlsx/xform/pivot-table/raw-xml-collector.js +149 -0
  211. package/dist/esm/modules/excel/xlsx/xform/sheet/cell-xform.js +1 -1
  212. package/dist/esm/modules/excel/xlsx/xform/sheet/cf/cf-rule-xform.js +1 -1
  213. package/dist/esm/modules/excel/xlsx/xform/sheet/cf/conditional-formattings-xform.js +1 -1
  214. package/dist/esm/modules/excel/xlsx/xform/sheet/cf-ext/cf-rule-ext-xform.js +1 -1
  215. package/dist/esm/modules/excel/xlsx/xform/sheet/col-xform.js +3 -3
  216. package/dist/esm/modules/excel/xlsx/xform/sheet/data-validations-xform.js +3 -3
  217. package/dist/esm/modules/excel/xlsx/xform/sheet/header-footer-xform.js +6 -6
  218. package/dist/esm/modules/excel/xlsx/xform/sheet/page-setup-xform.js +11 -11
  219. package/dist/esm/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +3 -3
  220. package/dist/esm/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +10 -10
  221. package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +12 -12
  222. package/dist/esm/modules/excel/xlsx/xform/strings/phonetic-text-xform.js +2 -2
  223. package/dist/esm/modules/excel/xlsx/xform/style/color-xform.js +1 -1
  224. package/dist/esm/modules/excel/xlsx/xform/style/style-xform.js +5 -5
  225. package/dist/esm/modules/excel/xlsx/xlsx.browser.js +88 -54
  226. package/dist/esm/utils/env.js +0 -7
  227. package/dist/esm/utils/utils.base.js +40 -47
  228. package/dist/esm/utils/utils.browser.js +1 -1
  229. package/dist/esm/utils/utils.js +1 -1
  230. package/dist/iife/excelts.iife.js +1553 -718
  231. package/dist/iife/excelts.iife.js.map +1 -1
  232. package/dist/iife/excelts.iife.min.js +36 -105
  233. package/dist/types/index.browser.d.ts +1 -1
  234. package/dist/types/index.d.ts +1 -1
  235. package/dist/types/modules/excel/pivot-table.d.ts +168 -17
  236. package/dist/types/modules/excel/types.d.ts +0 -4
  237. package/dist/types/modules/excel/workbook.d.ts +0 -2
  238. package/dist/types/modules/excel/xlsx/xform/base-xform.d.ts +5 -5
  239. package/dist/types/modules/excel/xlsx/xform/comment/comment-xform.d.ts +1 -2
  240. package/dist/types/modules/excel/xlsx/xform/comment/comments-xform.d.ts +1 -2
  241. package/dist/types/modules/excel/xlsx/xform/comment/style/vml-position-xform.d.ts +3 -4
  242. package/dist/types/modules/excel/xlsx/xform/comment/vml-client-data-xform.d.ts +1 -2
  243. package/dist/types/modules/excel/xlsx/xform/comment/vml-notes-xform.d.ts +1 -2
  244. package/dist/types/modules/excel/xlsx/xform/comment/vml-textbox-xform.d.ts +1 -2
  245. package/dist/types/modules/excel/xlsx/xform/composite-xform.d.ts +1 -1
  246. package/dist/types/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +1 -2
  247. package/dist/types/modules/excel/xlsx/xform/drawing/cell-position-xform.d.ts +1 -2
  248. package/dist/types/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +1 -2
  249. package/dist/types/modules/excel/xlsx/xform/drawing/drawing-xform.d.ts +1 -2
  250. package/dist/types/modules/excel/xlsx/xform/drawing/ext-xform.d.ts +1 -2
  251. package/dist/types/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +1 -2
  252. package/dist/types/modules/excel/xlsx/xform/list-xform.d.ts +1 -2
  253. package/dist/types/modules/excel/xlsx/xform/pivot-table/cache-field-xform.d.ts +5 -15
  254. package/dist/types/modules/excel/xlsx/xform/pivot-table/cache-field.d.ts +14 -15
  255. package/dist/types/modules/excel/xlsx/xform/pivot-table/pivot-cache-definition-xform.d.ts +13 -29
  256. package/dist/types/modules/excel/xlsx/xform/pivot-table/pivot-cache-records-xform.d.ts +7 -34
  257. package/dist/types/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +101 -27
  258. package/dist/types/modules/excel/xlsx/xform/pivot-table/raw-xml-collector.d.ts +78 -0
  259. package/dist/types/modules/excel/xlsx/xform/sheet/row-xform.d.ts +1 -2
  260. package/dist/types/modules/excel/xlsx/xform/sheet/sheet-view-xform.d.ts +1 -2
  261. package/dist/types/modules/excel/xlsx/xform/table/auto-filter-xform.d.ts +1 -2
  262. package/dist/types/modules/excel/xlsx/xform/table/custom-filter-xform.d.ts +1 -2
  263. package/dist/types/modules/excel/xlsx/xform/table/filter-column-xform.d.ts +1 -2
  264. package/dist/types/modules/excel/xlsx/xform/table/filter-xform.d.ts +1 -2
  265. package/dist/types/modules/excel/xlsx/xform/table/table-column-xform.d.ts +1 -2
  266. package/dist/types/modules/excel/xlsx/xform/table/table-style-info-xform.d.ts +1 -2
  267. package/dist/types/modules/excel/xlsx/xform/table/table-xform.d.ts +1 -2
  268. package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +5 -2
  269. package/dist/types/utils/env.d.ts +0 -5
  270. package/dist/types/utils/utils.base.d.ts +8 -13
  271. package/dist/types/utils/utils.browser.d.ts +1 -1
  272. package/dist/types/utils/utils.d.ts +1 -1
  273. package/package.json +1 -1
@@ -2,25 +2,50 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PivotTableXform = void 0;
4
4
  const xml_stream_1 = require("../../../utils/xml-stream.js");
5
- const utils_1 = require("../../../../../utils/utils.js");
5
+ const col_cache_1 = require("../../../utils/col-cache.js");
6
6
  const base_xform_1 = require("../base-xform.js");
7
+ const pivot_table_1 = require("../../../pivot-table.js");
8
+ const raw_xml_collector_1 = require("./raw-xml-collector.js");
9
+ /** OOXML sentinel field index meaning "data values" pseudo-field (used in pivotArea references) */
10
+ const FIELD_INDEX_DATA_VALUES = 4294967294; // 0xFFFFFFFE
11
+ /**
12
+ * Signed sentinel for the "Values" pseudo-field in colFields/rowFields.
13
+ * OOXML represents this as x="-2" (signed int32 of 0xFFFFFFFE).
14
+ */
15
+ const VALUES_FIELD_INDEX = -2;
16
+ /** Default pivot table style name */
17
+ const DEFAULT_PIVOT_STYLE = "PivotStyleLight16";
18
+ /** Valid OOXML axis values for pivot fields */
19
+ const VALID_PIVOT_AXES = new Set(["axisRow", "axisCol", "axisPage", "axisValues"]);
20
+ /** PivotFieldItem attribute keys — used to build the attrs object for rendering */
21
+ const PIVOT_FIELD_ITEM_KEYS = ["x", "t", "h", "sd", "f", "m", "c", "d"];
22
+ /** Factory for default ParserState values */
23
+ function createDefaultParserState() {
24
+ return {
25
+ currentSection: null,
26
+ inPivotArea: false,
27
+ inAutoSortScope: false
28
+ };
29
+ }
30
+ /** Known pivotField attributes that we parse individually (hoisted to module scope) */
31
+ const KNOWN_PIVOT_FIELD_KEYS = new Set([
32
+ "axis",
33
+ "dataField",
34
+ "compact",
35
+ "outline",
36
+ "showAll",
37
+ "defaultSubtotal",
38
+ "numFmtId",
39
+ "sortType",
40
+ "subtotalTop",
41
+ "insertBlankRow",
42
+ "multipleItemSelectionAllowed"
43
+ ]);
7
44
  class PivotTableXform extends base_xform_1.BaseXform {
8
45
  constructor() {
9
46
  super();
10
47
  // Parser state consolidated into object for easier reset
11
- this.state = {
12
- inPivotFields: false,
13
- inRowFields: false,
14
- inColFields: false,
15
- inDataFields: false,
16
- inRowItems: false,
17
- inColItems: false,
18
- inLocation: false,
19
- inItems: false,
20
- inPivotTableStyleInfo: false,
21
- inChartFormats: false,
22
- inPivotArea: false
23
- };
48
+ this.state = createDefaultParserState();
24
49
  // Current parsing context
25
50
  this.currentPivotField = null;
26
51
  this.currentRowItem = null;
@@ -28,37 +53,46 @@ class PivotTableXform extends base_xform_1.BaseXform {
28
53
  this.currentChartFormat = null;
29
54
  // Buffer for collecting pivotArea XML
30
55
  this.pivotAreaXmlBuffer = [];
31
- this.pivotAreaDepth = 0;
32
- this.map = {};
56
+ // Buffer for collecting autoSortScope XML
57
+ this.autoSortScopeXmlBuffer = [];
58
+ // Raw XML collectors (replacing manual in/depth/buffer triples)
59
+ this.extLstCollector = new raw_xml_collector_1.RawXmlCollector("extLst");
60
+ this.formatsCollector = new raw_xml_collector_1.RawXmlCollector("formats");
61
+ this.conditionalFormatsCollector = new raw_xml_collector_1.RawXmlCollector("conditionalFormats");
62
+ this.filtersCollector = new raw_xml_collector_1.RawXmlCollector("filters");
63
+ this.unknownCollector = new raw_xml_collector_1.RawXmlCollector("");
64
+ // Accumulated unknown elements XML strings (one per element)
65
+ this.unknownElementsXmlParts = [];
33
66
  this.model = null;
34
67
  }
35
- prepare(_model) {
36
- // No preparation needed
37
- }
38
68
  get tag() {
39
69
  // http://www.datypic.com/sc/ooxml/e-ssml_pivotTableDefinition.html
40
70
  return "pivotTableDefinition";
41
71
  }
42
72
  reset() {
43
73
  this.model = null;
44
- // Reset all parser state flags using object
45
- Object.keys(this.state).forEach(key => {
46
- this.state[key] = false;
47
- });
74
+ // Reset all parser state flags
75
+ this.state = createDefaultParserState();
48
76
  // Reset current context
49
77
  this.currentPivotField = null;
50
78
  this.currentRowItem = null;
51
79
  this.currentColItem = null;
52
80
  this.currentChartFormat = null;
53
81
  this.pivotAreaXmlBuffer = [];
54
- this.pivotAreaDepth = 0;
82
+ this.autoSortScopeXmlBuffer = [];
83
+ this.extLstCollector.reset();
84
+ this.formatsCollector.reset();
85
+ this.conditionalFormatsCollector.reset();
86
+ this.filtersCollector.reset();
87
+ this.unknownCollector.reset();
88
+ this.unknownElementsXmlParts = [];
55
89
  }
56
90
  /**
57
91
  * Render pivot table XML.
58
92
  * Supports both newly created models and loaded models.
59
93
  */
60
94
  render(xmlStream, model) {
61
- const isLoaded = model.isLoaded;
95
+ const isLoaded = "isLoaded" in model && model.isLoaded;
62
96
  if (isLoaded) {
63
97
  this.renderLoaded(xmlStream, model);
64
98
  }
@@ -70,33 +104,26 @@ class PivotTableXform extends base_xform_1.BaseXform {
70
104
  * Render newly created pivot table
71
105
  */
72
106
  renderNew(xmlStream, model) {
73
- const { rows, columns, values, cacheFields, cacheId, applyWidthHeightFormats } = model;
74
- // Build rowItems - need one <i> for each unique value in row fields, plus grand total
75
- const rowItems = buildRowItems(rows, cacheFields);
76
- // Build colItems - need one <i> for each unique value in col fields, plus grand total
77
- const colItems = buildColItems(columns, cacheFields, values.length);
78
- // Calculate pivot table dimensions
79
- const rowFieldItemCount = rows.length > 0 ? cacheFields[rows[0]]?.sharedItems?.length || 0 : 0;
80
- const colFieldItemCount = columns.length > 0 ? cacheFields[columns[0]]?.sharedItems?.length || 0 : 0;
81
- // Location: A3 is where pivot table starts
82
- // - firstHeaderRow: 1 (column headers are in first row of pivot table)
83
- // - firstDataRow: 2 (data starts in second row)
84
- // - firstDataCol: 1 (data starts in second column, after row labels)
85
- // Calculate ref based on actual data size:
86
- // - Start row: 3
87
- // - Header rows: 2 (column label row + subheader row)
88
- // - Data rows: rowFieldItemCount
89
- // - Grand total row: 1
90
- // endRow = 3 + 2 + rowFieldItemCount + 1 - 1 = 5 + rowFieldItemCount
91
- // Or simplified: startRow (3) + 1 (column labels) + rowFieldItemCount (data) + 1 (grand total)
92
- const endRow = 3 + 1 + rowFieldItemCount + 1; // = 5 + rowFieldItemCount
93
- const endCol = 1 + colFieldItemCount + 1; // start col + data cols + grand total
94
- const endColLetter = String.fromCharCode(64 + endCol);
95
- const locationRef = `A3:${endColLetter}${endRow}`;
107
+ const { rows, columns, values, pages = [], cacheFields, cacheId, tableNumber, applyWidthHeightFormats } = model;
108
+ // Multi-value with no explicit columns: the "Values" pseudo-field occupies the column axis
109
+ const isMultiValueNoCol = columns.length === 0 && values.length > 1;
110
+ // Page fields offset: each page field adds 1 row above the pivot table,
111
+ // plus 1 blank separator row when any page fields are present.
112
+ const pageCount = pages.length;
113
+ const pageOffset = pageCount > 0 ? pageCount + 1 : 0;
114
+ // Location ref: firstDataCol = number of row fields (row label columns),
115
+ // endCol = row fields + data columns.
116
+ const firstDataCol = rows.length;
117
+ const startRow = 3 + pageOffset;
118
+ const endRow = startRow + 1; // header + 1 data row placeholder
119
+ const dataColCount = isMultiValueNoCol ? values.length : 1;
120
+ const endCol = firstDataCol + dataColCount;
121
+ const endColLetter = col_cache_1.colCache.n2l(endCol);
122
+ const locationRef = `A${startRow}:${endColLetter}${endRow}`;
96
123
  xmlStream.openXml(xml_stream_1.XmlStream.StdDocAttributes);
97
124
  xmlStream.openNode(this.tag, {
98
125
  ...PivotTableXform.PIVOT_TABLE_ATTRIBUTES,
99
- name: "PivotTable2",
126
+ name: `PivotTable${tableNumber}`,
100
127
  cacheId,
101
128
  applyNumberFormats: "0",
102
129
  applyBorderFormats: "0",
@@ -115,110 +142,121 @@ class PivotTableXform extends base_xform_1.BaseXform {
115
142
  compactData: "0",
116
143
  multipleFieldFilters: "0"
117
144
  });
118
- xmlStream.writeXml(`
119
- <location ref="${locationRef}" firstHeaderRow="1" firstDataRow="2" firstDataCol="1" />
120
- <pivotFields count="${cacheFields.length}">
121
- ${renderPivotFields(model)}
122
- </pivotFields>
123
- <rowFields count="${rows.length}">
124
- ${rows.map(rowIndex => `<field x="${rowIndex}" />`).join("\n ")}
125
- </rowFields>
126
- <rowItems count="${rowItems.count}">
127
- ${rowItems.xml}
128
- </rowItems>
129
- <colFields count="${columns.length === 0 ? 1 : columns.length}">
130
- ${columns.length === 0
131
- ? '<field x="-2" />'
132
- : columns.map(columnIndex => `<field x="${columnIndex}" />`).join("\n ")}
133
- </colFields>
134
- <colItems count="${colItems.count}">
135
- ${colItems.xml}
136
- </colItems>
137
- <dataFields count="${values.length}">
138
- ${buildDataFields(cacheFields, values, model.metric)}
139
- </dataFields>
140
- <pivotTableStyleInfo
141
- name="PivotStyleLight16"
142
- showRowHeaders="1"
143
- showColHeaders="1"
144
- showRowStripes="0"
145
- showColStripes="0"
146
- showLastColumn="1"
147
- />
148
- <extLst>
149
- <ext
150
- uri="{962EF5D1-5CA2-4c93-8EF4-DBF5C05439D2}"
151
- xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
152
- >
153
- <x14:pivotTableDefinition
154
- hideValuesRow="1"
155
- xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main"
156
- />
157
- </ext>
158
- <ext
159
- uri="{747A6164-185A-40DC-8AA5-F01512510D54}"
160
- xmlns:xpdl="http://schemas.microsoft.com/office/spreadsheetml/2016/pivotdefaultlayout"
161
- >
162
- <xpdl:pivotTableDefinition16
163
- EnabledSubtotalsDefault="0"
164
- SubtotalsOnTopDefault="0"
165
- />
166
- </ext>
167
- </extLst>
168
- `);
169
- xmlStream.closeNode();
170
- }
171
- /**
172
- * Render loaded pivot table (preserving original structure)
173
- */
174
- renderLoaded(xmlStream, model) {
175
- const attrs = {
176
- ...PivotTableXform.PIVOT_TABLE_ATTRIBUTES,
177
- name: model.name || "PivotTable1",
178
- cacheId: model.cacheId,
179
- applyNumberFormats: model.applyNumberFormats || "0",
180
- applyBorderFormats: model.applyBorderFormats || "0",
181
- applyFontFormats: model.applyFontFormats || "0",
182
- applyPatternFormats: model.applyPatternFormats || "0",
183
- applyAlignmentFormats: model.applyAlignmentFormats || "0",
184
- // Preserve original value when present; default to Excel's typical "0".
185
- applyWidthHeightFormats: model.applyWidthHeightFormats ?? "0",
186
- dataCaption: model.dataCaption || "Values",
187
- updatedVersion: model.updatedVersion || "8",
188
- minRefreshableVersion: model.minRefreshableVersion || "3",
189
- useAutoFormatting: model.useAutoFormatting ? "1" : "0",
190
- itemPrintTitles: model.itemPrintTitles ? "1" : "0",
191
- createdVersion: model.createdVersion || "8",
192
- indent: model.indent !== undefined ? String(model.indent) : "0",
193
- multipleFieldFilters: model.multipleFieldFilters ? "1" : "0"
145
+ // Location
146
+ const locAttrs = {
147
+ ref: locationRef,
148
+ firstHeaderRow: 1,
149
+ firstDataRow: 1,
150
+ firstDataCol
194
151
  };
195
- // Add outline attributes if present
196
- if (model.outline) {
197
- attrs.outline = "1";
152
+ if (pageCount > 0) {
153
+ locAttrs.rowPageCount = pageCount;
154
+ locAttrs.colPageCount = 1;
198
155
  }
199
- if (model.outlineData) {
200
- attrs.outlineData = "1";
156
+ xmlStream.leafNode("location", locAttrs);
157
+ // Pivot fields
158
+ renderPivotFields(xmlStream, model);
159
+ // Row fields
160
+ xmlStream.openNode("rowFields", { count: rows.length });
161
+ for (const rowIndex of rows) {
162
+ xmlStream.leafNode("field", { x: rowIndex });
201
163
  }
202
- if (model.chartFormat !== undefined) {
203
- attrs.chartFormat = String(model.chartFormat);
164
+ xmlStream.closeNode();
165
+ // Row items: minimal grand total row. refreshOnLoad="1" causes Excel
166
+ // to rebuild the full row expansion on open.
167
+ xmlStream.openNode("rowItems", { count: 1 });
168
+ xmlStream.openNode("i", { t: "grand" });
169
+ xmlStream.leafNode("x");
170
+ xmlStream.closeNode(); // i
171
+ xmlStream.closeNode(); // rowItems
172
+ // colFields: lists the field indices on the column axis.
173
+ // When columns is non-empty, list those field indices.
174
+ // When columns is empty but there are multiple values, emit the synthetic
175
+ // "Values" pseudo-field (field x="-2") so Excel knows where to position
176
+ // the data field labels on the column axis.
177
+ // When columns is empty and there is only one value, omit colFields entirely.
178
+ if (columns.length > 0) {
179
+ const fieldCount = values.length > 1 ? columns.length + 1 : columns.length;
180
+ xmlStream.openNode("colFields", { count: fieldCount });
181
+ for (const colIndex of columns) {
182
+ xmlStream.leafNode("field", { x: colIndex });
183
+ }
184
+ if (values.length > 1) {
185
+ xmlStream.leafNode("field", { x: VALUES_FIELD_INDEX });
186
+ }
187
+ xmlStream.closeNode();
188
+ }
189
+ else if (isMultiValueNoCol) {
190
+ xmlStream.openNode("colFields", { count: 1 });
191
+ xmlStream.leafNode("field", { x: VALUES_FIELD_INDEX });
192
+ xmlStream.closeNode();
193
+ }
194
+ // colItems: for multi-value no-column pivots, one <i> per value field (referencing
195
+ // its index in dataFields via <x v="N"/>) plus a grand total <i>.
196
+ // For single-value or explicit-columns pivots, a single empty <i/>.
197
+ // These are required by Excel — omitting them causes "Repaired Records" errors.
198
+ if (isMultiValueNoCol) {
199
+ xmlStream.openNode("colItems", { count: values.length + 1 });
200
+ for (let idx = 0; idx < values.length; idx++) {
201
+ xmlStream.openNode("i");
202
+ xmlStream.leafNode("x", idx === 0 ? undefined : { v: idx });
203
+ xmlStream.closeNode(); // i
204
+ }
205
+ xmlStream.openNode("i", { t: "grand" });
206
+ xmlStream.leafNode("x");
207
+ xmlStream.closeNode(); // i
208
+ xmlStream.closeNode(); // colItems
204
209
  }
205
- // Only add compact/compactData if they are true (some files don't have them)
206
- if (model.compact) {
207
- attrs.compact = "1";
210
+ else {
211
+ xmlStream.openNode("colItems", { count: 1 });
212
+ xmlStream.leafNode("i");
213
+ xmlStream.closeNode();
208
214
  }
209
- if (model.compactData) {
210
- attrs.compactData = "1";
215
+ // Page fields (between colItems and dataFields per OOXML spec)
216
+ if (pageCount > 0) {
217
+ xmlStream.openNode("pageFields", { count: pageCount });
218
+ for (const fld of pages) {
219
+ xmlStream.leafNode("pageField", { fld, hier: -1 });
220
+ }
221
+ xmlStream.closeNode();
211
222
  }
223
+ // Data fields
224
+ renderDataFields(xmlStream, cacheFields, values, model.valueMetrics);
225
+ // Pivot table style info
226
+ xmlStream.leafNode("pivotTableStyleInfo", {
227
+ name: DEFAULT_PIVOT_STYLE,
228
+ showRowHeaders: "1",
229
+ showColHeaders: "1",
230
+ showRowStripes: "0",
231
+ showColStripes: "0",
232
+ showLastColumn: "1"
233
+ });
234
+ // Extensions
235
+ xmlStream.writeXml(PivotTableXform.EXTLST_XML);
236
+ xmlStream.closeNode();
237
+ }
238
+ /**
239
+ * Render loaded pivot table (preserving original structure)
240
+ */
241
+ renderLoaded(xmlStream, model) {
242
+ const attrs = this.buildLoadedRootAttributes(model);
212
243
  xmlStream.openXml(xml_stream_1.XmlStream.StdDocAttributes);
213
244
  xmlStream.openNode(this.tag, attrs);
214
245
  // Location
215
246
  if (model.location) {
216
- xmlStream.leafNode("location", {
247
+ const locAttrs = {
217
248
  ref: model.location.ref,
218
249
  firstHeaderRow: model.location.firstHeaderRow,
219
250
  firstDataRow: model.location.firstDataRow,
220
251
  firstDataCol: model.location.firstDataCol
221
- });
252
+ };
253
+ if (model.location.rowPageCount !== undefined) {
254
+ locAttrs.rowPageCount = model.location.rowPageCount;
255
+ }
256
+ if (model.location.colPageCount !== undefined) {
257
+ locAttrs.colPageCount = model.location.colPageCount;
258
+ }
259
+ xmlStream.leafNode("location", locAttrs);
222
260
  }
223
261
  // Pivot fields
224
262
  if (model.pivotFields.length > 0) {
@@ -244,24 +282,30 @@ class PivotTableXform extends base_xform_1.BaseXform {
244
282
  }
245
283
  xmlStream.closeNode();
246
284
  }
247
- else {
285
+ else if (model.hasRowItems) {
248
286
  xmlStream.writeXml('<rowItems count="1"><i t="grand"><x/></i></rowItems>');
249
287
  }
250
288
  // Col fields
251
289
  // Only render colFields if it was present in the original file or if there are actual column fields
252
290
  // Some pivot tables don't have colFields element at all
253
291
  if (model.hasColFields || model.colFields.length > 0) {
254
- const colFieldCount = model.colFields.length === 0 ? 1 : model.colFields.length;
255
- xmlStream.openNode("colFields", { count: colFieldCount });
256
- if (model.colFields.length === 0) {
257
- xmlStream.leafNode("field", { x: -2 });
292
+ if (model.colFields.length === 0 && model.dataFields.length <= 1) {
293
+ // Empty colFields with no multi-value need — preserve as empty element
294
+ xmlStream.leafNode("colFields", { count: 0 });
258
295
  }
259
296
  else {
260
- for (const fieldIndex of model.colFields) {
261
- xmlStream.leafNode("field", { x: fieldIndex });
297
+ const colFieldCount = model.colFields.length === 0 ? 1 : model.colFields.length;
298
+ xmlStream.openNode("colFields", { count: colFieldCount });
299
+ if (model.colFields.length === 0) {
300
+ xmlStream.leafNode("field", { x: VALUES_FIELD_INDEX });
262
301
  }
302
+ else {
303
+ for (const fieldIndex of model.colFields) {
304
+ xmlStream.leafNode("field", { x: fieldIndex });
305
+ }
306
+ }
307
+ xmlStream.closeNode();
263
308
  }
264
- xmlStream.closeNode();
265
309
  }
266
310
  // Col items - use parsed items if available
267
311
  if (model.colItems && model.colItems.length > 0) {
@@ -271,58 +315,194 @@ class PivotTableXform extends base_xform_1.BaseXform {
271
315
  }
272
316
  xmlStream.closeNode();
273
317
  }
274
- else {
318
+ else if (model.hasColItems) {
275
319
  xmlStream.writeXml('<colItems count="1"><i t="grand"><x/></i></colItems>');
276
320
  }
321
+ // Page fields (report filters)
322
+ if (model.pageFields && model.pageFields.length > 0) {
323
+ xmlStream.openNode("pageFields", { count: model.pageFields.length });
324
+ for (const pf of model.pageFields) {
325
+ const pfAttrs = { fld: pf.fld };
326
+ if (pf.item !== undefined) {
327
+ pfAttrs.item = pf.item;
328
+ }
329
+ if (pf.hier !== undefined) {
330
+ pfAttrs.hier = pf.hier;
331
+ }
332
+ if (pf.name !== undefined) {
333
+ pfAttrs.name = pf.name;
334
+ }
335
+ xmlStream.leafNode("pageField", pfAttrs);
336
+ }
337
+ xmlStream.closeNode();
338
+ }
277
339
  // Data fields
278
340
  if (model.dataFields.length > 0) {
279
341
  xmlStream.openNode("dataFields", { count: model.dataFields.length });
280
342
  for (const dataField of model.dataFields) {
281
343
  const dfAttrs = {
282
344
  name: dataField.name,
283
- fld: dataField.fld,
284
- baseField: dataField.baseField ?? 0,
285
- baseItem: dataField.baseItem ?? 0
345
+ fld: dataField.fld
286
346
  };
287
- if (dataField.subtotal && dataField.subtotal !== "sum") {
347
+ if (dataField.baseField !== undefined) {
348
+ dfAttrs.baseField = dataField.baseField;
349
+ }
350
+ if (dataField.baseItem !== undefined) {
351
+ dfAttrs.baseItem = dataField.baseItem;
352
+ }
353
+ if (dataField.subtotal !== undefined && dataField.subtotal !== "sum") {
288
354
  dfAttrs.subtotal = dataField.subtotal;
289
355
  }
356
+ if (dataField.numFmtId !== undefined) {
357
+ dfAttrs.numFmtId = dataField.numFmtId;
358
+ }
290
359
  xmlStream.leafNode("dataField", dfAttrs);
291
360
  }
292
361
  xmlStream.closeNode();
293
362
  }
363
+ // Formats — preserved raw XML from loaded file
364
+ if (model.formatsXml) {
365
+ xmlStream.writeXml(model.formatsXml);
366
+ }
367
+ // Conditional formats — preserved raw XML from loaded file
368
+ // OOXML order: formats → conditionalFormats → chartFormats
369
+ if (model.conditionalFormatsXml) {
370
+ xmlStream.writeXml(model.conditionalFormatsXml);
371
+ }
294
372
  // Chart formats (for pivot charts) - preserve original pivotArea XML
295
373
  if (model.chartFormats && model.chartFormats.length > 0) {
296
- xmlStream.openNode("chartFormats", { count: model.chartFormats.length });
297
- for (const cf of model.chartFormats) {
298
- xmlStream.openNode("chartFormat", {
299
- chart: cf.chart,
300
- format: cf.format,
301
- series: cf.series ? "1" : undefined
302
- });
303
- // Use preserved pivotArea XML or fallback to default
304
- if (cf.pivotAreaXml) {
305
- xmlStream.writeXml(cf.pivotAreaXml);
306
- }
307
- else {
308
- // Fallback for newly created chart formats (shouldn't happen for loaded models)
309
- xmlStream.writeXml(`<pivotArea type="data" outline="0" fieldPosition="0"><references count="1"><reference field="4294967294" count="1" selected="0"><x v="0"/></reference></references></pivotArea>`);
310
- }
311
- xmlStream.closeNode();
312
- }
313
- xmlStream.closeNode();
374
+ this.renderChartFormats(xmlStream, model.chartFormats);
314
375
  }
315
376
  // Style info
377
+ const si = model.styleInfo;
316
378
  xmlStream.leafNode("pivotTableStyleInfo", {
317
- name: model.styleName || "PivotStyleLight16",
318
- showRowHeaders: "1",
319
- showColHeaders: "1",
320
- showRowStripes: "0",
321
- showColStripes: "0",
322
- showLastColumn: "1"
379
+ name: si?.name ?? model.styleName ?? DEFAULT_PIVOT_STYLE,
380
+ showRowHeaders: si?.showRowHeaders ?? "1",
381
+ showColHeaders: si?.showColHeaders ?? "1",
382
+ showRowStripes: si?.showRowStripes ?? "0",
383
+ showColStripes: si?.showColStripes ?? "0",
384
+ showLastColumn: si?.showLastColumn ?? "1"
323
385
  });
324
- // Extensions
325
- xmlStream.writeXml(`<extLst><ext uri="{962EF5D1-5CA2-4c93-8EF4-DBF5C05439D2}" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"><x14:pivotTableDefinition hideValuesRow="1" xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main"/></ext><ext uri="{747A6164-185A-40DC-8AA5-F01512510D54}" xmlns:xpdl="http://schemas.microsoft.com/office/spreadsheetml/2016/pivotdefaultlayout"><xpdl:pivotTableDefinition16/></ext></extLst>`);
386
+ // Filters — preserved raw XML from loaded file
387
+ // <filters> appears between pivotTableStyleInfo and extLst per OOXML schema
388
+ if (model.filtersXml) {
389
+ xmlStream.writeXml(model.filtersXml);
390
+ }
391
+ // Unknown top-level elements — preserved raw XML for roundtrip
392
+ if (model.unknownElementsXml) {
393
+ xmlStream.writeXml(model.unknownElementsXml);
394
+ }
395
+ // Extensions — use preserved XML from loaded file; only inject default for new tables
396
+ const extLstXml = model.extLstXml ?? (model.isLoaded ? "" : PivotTableXform.EXTLST_XML);
397
+ if (extLstXml) {
398
+ xmlStream.writeXml(extLstXml);
399
+ }
400
+ xmlStream.closeNode();
401
+ }
402
+ /**
403
+ * Build the root `<pivotTableDefinition>` attributes for a loaded (roundtrip) model.
404
+ * Extracted from renderLoaded to keep the render method focused on element structure.
405
+ */
406
+ buildLoadedRootAttributes(model) {
407
+ const attrs = {
408
+ ...PivotTableXform.PIVOT_TABLE_ATTRIBUTES,
409
+ name: model.name ?? "PivotTable1",
410
+ cacheId: String(model.cacheId),
411
+ applyNumberFormats: model.applyNumberFormats ?? "0",
412
+ applyBorderFormats: model.applyBorderFormats ?? "0",
413
+ applyFontFormats: model.applyFontFormats ?? "0",
414
+ applyPatternFormats: model.applyPatternFormats ?? "0",
415
+ applyAlignmentFormats: model.applyAlignmentFormats ?? "0",
416
+ applyWidthHeightFormats: model.applyWidthHeightFormats ?? "0",
417
+ dataCaption: model.dataCaption ?? "Values",
418
+ updatedVersion: model.updatedVersion ?? "8",
419
+ minRefreshableVersion: model.minRefreshableVersion ?? "3"
420
+ };
421
+ // Only emit these boolean-style attributes when they were present in the original.
422
+ // Absent means the OOXML default applies; emitting "0" explicitly changes semantics.
423
+ // Placed before createdVersion to match Excel's attribute ordering.
424
+ if (model.useAutoFormatting !== undefined) {
425
+ attrs.useAutoFormatting = model.useAutoFormatting;
426
+ }
427
+ if (model.itemPrintTitles !== undefined) {
428
+ attrs.itemPrintTitles = model.itemPrintTitles;
429
+ }
430
+ if (model.multipleFieldFilters !== undefined) {
431
+ attrs.multipleFieldFilters = model.multipleFieldFilters;
432
+ }
433
+ attrs.createdVersion = model.createdVersion ?? "8";
434
+ if (model.indent !== undefined) {
435
+ attrs.indent = String(model.indent);
436
+ }
437
+ // Preserve xr:uid on roundtrip
438
+ if (model.uid) {
439
+ attrs["xmlns:xr"] = "http://schemas.microsoft.com/office/spreadsheetml/2014/revision";
440
+ attrs["xr:uid"] = model.uid;
441
+ }
442
+ // Add outline attributes if present
443
+ if (model.outline) {
444
+ attrs.outline = "1";
445
+ }
446
+ if (model.outlineData) {
447
+ attrs.outlineData = "1";
448
+ }
449
+ if (model.chartFormat !== undefined) {
450
+ attrs.chartFormat = String(model.chartFormat);
451
+ }
452
+ // Grand totals and display option attributes — only emit when present in original
453
+ if (model.colGrandTotals !== undefined) {
454
+ attrs.colGrandTotals = model.colGrandTotals;
455
+ }
456
+ if (model.rowGrandTotals !== undefined) {
457
+ attrs.rowGrandTotals = model.rowGrandTotals;
458
+ }
459
+ if (model.showError !== undefined) {
460
+ attrs.showError = model.showError;
461
+ }
462
+ if (model.errorCaption !== undefined) {
463
+ attrs.errorCaption = model.errorCaption;
464
+ }
465
+ if (model.showMissing !== undefined) {
466
+ attrs.showMissing = model.showMissing;
467
+ }
468
+ if (model.missingCaption !== undefined) {
469
+ attrs.missingCaption = model.missingCaption;
470
+ }
471
+ if (model.grandTotalCaption !== undefined) {
472
+ attrs.grandTotalCaption = model.grandTotalCaption;
473
+ }
474
+ // Only write compact/compactData when false (non-default).
475
+ // OOXML spec: absent = true (default). So if the original file had compact="0",
476
+ // we must preserve it; omitting it would change semantics from false to true.
477
+ if (model.compact === false) {
478
+ attrs.compact = "0";
479
+ }
480
+ if (model.compactData === false) {
481
+ attrs.compactData = "0";
482
+ }
483
+ return attrs;
484
+ }
485
+ /**
486
+ * Render `<chartFormats>` with preserved pivotArea XML for pivot chart roundtrip.
487
+ */
488
+ renderChartFormats(xmlStream, chartFormats) {
489
+ xmlStream.openNode("chartFormats", { count: chartFormats.length });
490
+ for (const cf of chartFormats) {
491
+ xmlStream.openNode("chartFormat", {
492
+ chart: cf.chart,
493
+ format: cf.format,
494
+ series: cf.series === true ? "1" : cf.series === false ? "0" : undefined
495
+ });
496
+ // Use preserved pivotArea XML or fallback to default
497
+ if (cf.pivotAreaXml) {
498
+ xmlStream.writeXml(cf.pivotAreaXml);
499
+ }
500
+ else {
501
+ // Fallback for newly created chart formats (shouldn't happen for loaded models)
502
+ xmlStream.writeXml(`<pivotArea type="data" outline="0" fieldPosition="0"><references count="1"><reference field="${FIELD_INDEX_DATA_VALUES}" count="1" selected="0"><x v="0"/></reference></references></pivotArea>`);
503
+ }
504
+ xmlStream.closeNode();
505
+ }
326
506
  xmlStream.closeNode();
327
507
  }
328
508
  /**
@@ -330,13 +510,19 @@ class PivotTableXform extends base_xform_1.BaseXform {
330
510
  */
331
511
  renderRowColItem(xmlStream, item) {
332
512
  const attrs = {};
333
- if (item.t) {
513
+ if (item.t !== undefined) {
334
514
  attrs.t = item.t;
335
515
  }
336
- if (item.x && item.x.length > 0) {
516
+ if (item.r !== undefined) {
517
+ attrs.r = item.r;
518
+ }
519
+ if (item.i !== undefined) {
520
+ attrs.i = item.i;
521
+ }
522
+ if (item.x.length > 0) {
337
523
  xmlStream.openNode("i", attrs);
338
524
  for (const x of item.x) {
339
- if (x.v && x.v !== 0) {
525
+ if (x.v !== 0) {
340
526
  xmlStream.leafNode("x", { v: x.v });
341
527
  }
342
528
  else {
@@ -362,36 +548,103 @@ class PivotTableXform extends base_xform_1.BaseXform {
362
548
  if (field.dataField) {
363
549
  attrs.dataField = "1";
364
550
  }
365
- // showAll is typically always present
551
+ if (field.numFmtId !== undefined) {
552
+ attrs.numFmtId = String(field.numFmtId);
553
+ }
554
+ if (field.sortType) {
555
+ attrs.sortType = field.sortType;
556
+ }
557
+ // OOXML defaults: compact=true, outline=true, defaultSubtotal=true when absent.
558
+ // Only write the attribute when false (non-default) to preserve round-trip fidelity.
559
+ if (field.compact === false) {
560
+ attrs.compact = "0";
561
+ }
562
+ if (field.outline === false) {
563
+ attrs.outline = "0";
564
+ }
565
+ // showAll is typically always present — placed before defaultSubtotal to match Excel's ordering
366
566
  attrs.showAll = field.showAll ? "1" : "0";
367
- if (field.items && field.items.length > 0) {
567
+ if (field.defaultSubtotal === false) {
568
+ attrs.defaultSubtotal = "0";
569
+ }
570
+ if (field.subtotalTop === false) {
571
+ attrs.subtotalTop = "0";
572
+ }
573
+ if (field.insertBlankRow === true) {
574
+ attrs.insertBlankRow = "1";
575
+ }
576
+ if (field.multipleItemSelectionAllowed === true) {
577
+ attrs.multipleItemSelectionAllowed = "1";
578
+ }
579
+ // Spread extra unknown attributes for roundtrip preservation
580
+ if (field.extraAttrs) {
581
+ for (const [k, v] of Object.entries(field.extraAttrs)) {
582
+ attrs[k] = v;
583
+ }
584
+ }
585
+ const hasChildren = (field.items !== undefined && field.items.length > 0) || field.autoSortScopeXml !== undefined;
586
+ if (hasChildren) {
368
587
  xmlStream.openNode("pivotField", attrs);
369
- xmlStream.openNode("items", { count: field.items.length + 1 });
370
- for (const itemIndex of field.items) {
371
- xmlStream.leafNode("item", { x: itemIndex });
588
+ if (field.items !== undefined && field.items.length > 0) {
589
+ xmlStream.openNode("items", { count: field.items.length });
590
+ for (const item of field.items) {
591
+ const itemAttrs = {};
592
+ for (const key of PIVOT_FIELD_ITEM_KEYS) {
593
+ if (item[key] !== undefined) {
594
+ itemAttrs[key] = item[key];
595
+ }
596
+ }
597
+ xmlStream.leafNode("item", itemAttrs);
598
+ }
599
+ xmlStream.closeNode(); // items
600
+ }
601
+ if (field.autoSortScopeXml) {
602
+ xmlStream.writeXml(field.autoSortScopeXml);
372
603
  }
373
- // Grand total item
374
- xmlStream.writeXml('<item t="default"/>');
375
- xmlStream.closeNode(); // items
376
604
  xmlStream.closeNode(); // pivotField
377
605
  }
378
606
  else {
379
607
  xmlStream.leafNode("pivotField", attrs);
380
608
  }
381
609
  }
610
+ // TODO: Consider migrating to map-based child xform delegation (like table-xform.ts)
611
+ // to replace this large manual switch. Currently kept as-is because the manual SAX
612
+ // approach, while verbose, handles all OOXML edge cases correctly.
382
613
  parseOpen(node) {
383
614
  const { name, attributes } = node;
615
+ // Collect raw XML verbatim for roundtrip preservation (5 collectors)
616
+ if (this.extLstCollector.active) {
617
+ this.extLstCollector.feedOpen(name, attributes);
618
+ return true;
619
+ }
620
+ if (this.formatsCollector.active) {
621
+ this.formatsCollector.feedOpen(name, attributes);
622
+ return true;
623
+ }
624
+ if (this.conditionalFormatsCollector.active) {
625
+ this.conditionalFormatsCollector.feedOpen(name, attributes);
626
+ return true;
627
+ }
628
+ if (this.filtersCollector.active) {
629
+ this.filtersCollector.feedOpen(name, attributes);
630
+ return true;
631
+ }
632
+ if (this.unknownCollector.active) {
633
+ this.unknownCollector.feedOpen(name, attributes);
634
+ return true;
635
+ }
384
636
  switch (name) {
385
637
  case this.tag:
386
638
  // pivotTableDefinition root element
387
639
  this.reset();
388
640
  this.model = {
389
641
  name: attributes.name,
390
- cacheId: parseInt(attributes.cacheId || "0", 10),
642
+ cacheId: parseInt(attributes.cacheId ?? "0", 10),
391
643
  uid: attributes["xr:uid"],
392
644
  pivotFields: [],
393
645
  rowFields: [],
394
646
  colFields: [],
647
+ pageFields: [],
395
648
  dataFields: [],
396
649
  applyNumberFormats: attributes.applyNumberFormats,
397
650
  applyBorderFormats: attributes.applyBorderFormats,
@@ -403,15 +656,22 @@ class PivotTableXform extends base_xform_1.BaseXform {
403
656
  updatedVersion: attributes.updatedVersion,
404
657
  minRefreshableVersion: attributes.minRefreshableVersion,
405
658
  createdVersion: attributes.createdVersion,
406
- useAutoFormatting: attributes.useAutoFormatting === "1",
407
- itemPrintTitles: attributes.itemPrintTitles === "1",
408
- indent: attributes.indent ? parseInt(attributes.indent, 10) : 0,
409
- compact: attributes.compact === "1",
410
- compactData: attributes.compactData === "1",
411
- multipleFieldFilters: attributes.multipleFieldFilters === "1",
659
+ useAutoFormatting: attributes.useAutoFormatting,
660
+ itemPrintTitles: attributes.itemPrintTitles,
661
+ indent: attributes.indent !== undefined ? parseInt(attributes.indent, 10) : undefined,
662
+ compact: attributes.compact !== "0",
663
+ compactData: attributes.compactData !== "0",
664
+ multipleFieldFilters: attributes.multipleFieldFilters,
412
665
  outline: attributes.outline === "1",
413
666
  outlineData: attributes.outlineData === "1",
414
- chartFormat: attributes.chartFormat ? parseInt(attributes.chartFormat, 10) : undefined,
667
+ chartFormat: attributes.chartFormat !== undefined ? parseInt(attributes.chartFormat, 10) : undefined,
668
+ colGrandTotals: attributes.colGrandTotals,
669
+ rowGrandTotals: attributes.rowGrandTotals,
670
+ showError: attributes.showError,
671
+ errorCaption: attributes.errorCaption,
672
+ showMissing: attributes.showMissing,
673
+ missingCaption: attributes.missingCaption,
674
+ grandTotalCaption: attributes.grandTotalCaption,
415
675
  rowItems: [],
416
676
  colItems: [],
417
677
  chartFormats: [],
@@ -422,171 +682,346 @@ class PivotTableXform extends base_xform_1.BaseXform {
422
682
  if (this.model) {
423
683
  this.model.location = {
424
684
  ref: attributes.ref,
425
- firstHeaderRow: attributes.firstHeaderRow
685
+ firstHeaderRow: attributes.firstHeaderRow !== undefined
426
686
  ? parseInt(attributes.firstHeaderRow, 10)
427
687
  : undefined,
428
- firstDataRow: attributes.firstDataRow
688
+ firstDataRow: attributes.firstDataRow !== undefined
429
689
  ? parseInt(attributes.firstDataRow, 10)
430
690
  : undefined,
431
- firstDataCol: attributes.firstDataCol
691
+ firstDataCol: attributes.firstDataCol !== undefined
432
692
  ? parseInt(attributes.firstDataCol, 10)
693
+ : undefined,
694
+ rowPageCount: attributes.rowPageCount !== undefined
695
+ ? parseInt(attributes.rowPageCount, 10)
696
+ : undefined,
697
+ colPageCount: attributes.colPageCount !== undefined
698
+ ? parseInt(attributes.colPageCount, 10)
433
699
  : undefined
434
700
  };
435
701
  }
436
702
  break;
437
703
  case "pivotFields":
438
- this.state.inPivotFields = true;
704
+ this.state.currentSection = "pivotFields";
439
705
  break;
440
706
  case "pivotField":
441
- if (this.state.inPivotFields) {
707
+ if (this.state.currentSection === "pivotFields") {
708
+ // Collect unknown attributes into extraAttrs bag for roundtrip preservation
709
+ const extraAttrs = {};
710
+ for (const [k, v] of Object.entries(attributes)) {
711
+ if (!KNOWN_PIVOT_FIELD_KEYS.has(k)) {
712
+ extraAttrs[k] = String(v);
713
+ }
714
+ }
442
715
  this.currentPivotField = {
443
- axis: attributes.axis,
716
+ axis: VALID_PIVOT_AXES.has(attributes.axis)
717
+ ? attributes.axis
718
+ : undefined,
444
719
  dataField: attributes.dataField === "1",
445
720
  items: [],
446
- compact: attributes.compact === "1",
447
- outline: attributes.outline === "1",
448
- showAll: attributes.showAll === "1",
449
- defaultSubtotal: attributes.defaultSubtotal === "1"
721
+ compact: attributes.compact !== "0",
722
+ outline: attributes.outline !== "0",
723
+ showAll: attributes.showAll !== "0",
724
+ defaultSubtotal: attributes.defaultSubtotal !== "0",
725
+ numFmtId: attributes.numFmtId !== undefined ? parseInt(attributes.numFmtId, 10) : undefined,
726
+ sortType: attributes.sortType,
727
+ subtotalTop: attributes.subtotalTop !== undefined ? attributes.subtotalTop === "1" : undefined,
728
+ insertBlankRow: attributes.insertBlankRow === "1" ? true : undefined,
729
+ multipleItemSelectionAllowed: attributes.multipleItemSelectionAllowed === "1" ? true : undefined,
730
+ extraAttrs: Object.keys(extraAttrs).length > 0 ? extraAttrs : undefined
450
731
  };
451
732
  }
452
733
  break;
453
734
  case "items":
735
+ // No state needed — item parsing is guarded by currentPivotField
736
+ break;
737
+ case "item":
454
738
  if (this.currentPivotField) {
455
- this.state.inItems = true;
739
+ // R8-O1: Parse item attributes using a loop over PIVOT_FIELD_ITEM_KEYS
740
+ const item = {};
741
+ if (attributes.x !== undefined) {
742
+ item.x = parseInt(attributes.x, 10);
743
+ }
744
+ for (const key of PIVOT_FIELD_ITEM_KEYS) {
745
+ if (key !== "x" && attributes[key] !== undefined) {
746
+ item[key] = attributes[key];
747
+ }
748
+ }
749
+ // items is always initialized as [] when currentPivotField is created (see "pivotField" case)
750
+ this.currentPivotField.items.push(item);
456
751
  }
457
752
  break;
458
- case "item":
459
- if (this.state.inItems && this.currentPivotField && attributes.x !== undefined) {
460
- this.currentPivotField.items.push(parseInt(attributes.x, 10));
753
+ case "autoSortScope":
754
+ // Start collecting autoSortScope XML for the current pivotField
755
+ if (this.currentPivotField) {
756
+ this.state.inAutoSortScope = true;
757
+ this.autoSortScopeXmlBuffer = ["<autoSortScope>"];
461
758
  }
462
759
  break;
463
760
  case "rowFields":
464
- this.state.inRowFields = true;
761
+ this.state.currentSection = "rowFields";
465
762
  break;
466
763
  case "colFields":
467
- this.state.inColFields = true;
764
+ this.state.currentSection = "colFields";
468
765
  // Track that colFields element was present in original file
469
766
  if (this.model) {
470
767
  this.model.hasColFields = true;
471
768
  }
472
769
  break;
473
770
  case "dataFields":
474
- this.state.inDataFields = true;
771
+ this.state.currentSection = "dataFields";
772
+ break;
773
+ case "pageFields":
774
+ this.state.currentSection = "pageFields";
775
+ break;
776
+ case "pageField":
777
+ if (this.state.currentSection === "pageFields" && this.model) {
778
+ this.model.pageFields.push({
779
+ fld: parseInt(attributes.fld ?? "0", 10),
780
+ item: attributes.item !== undefined ? parseInt(attributes.item, 10) : undefined,
781
+ hier: attributes.hier !== undefined ? parseInt(attributes.hier, 10) : undefined,
782
+ name: attributes.name
783
+ });
784
+ }
475
785
  break;
476
786
  case "rowItems":
477
- this.state.inRowItems = true;
787
+ this.state.currentSection = "rowItems";
788
+ if (this.model) {
789
+ this.model.hasRowItems = true;
790
+ }
478
791
  break;
479
792
  case "colItems":
480
- this.state.inColItems = true;
793
+ this.state.currentSection = "colItems";
794
+ if (this.model) {
795
+ this.model.hasColItems = true;
796
+ }
481
797
  break;
482
798
  case "i":
483
799
  // Handle row/col item element
484
- if (this.state.inRowItems && this.model) {
485
- this.currentRowItem = { t: attributes.t, x: [] };
486
- }
487
- else if (this.state.inColItems && this.model) {
488
- this.currentColItem = { t: attributes.t, x: [] };
800
+ if (this.model) {
801
+ const rowColItem = this.state.currentSection === "rowItems" || this.state.currentSection === "colItems"
802
+ ? parseRowColItem(attributes)
803
+ : null;
804
+ if (this.state.currentSection === "rowItems") {
805
+ this.currentRowItem = rowColItem;
806
+ }
807
+ else if (this.state.currentSection === "colItems") {
808
+ this.currentColItem = rowColItem;
809
+ }
489
810
  }
490
811
  break;
491
812
  case "x":
492
813
  // Handle x element inside row/col items or pivotArea
493
814
  if (this.state.inPivotArea) {
494
- // Collect x element for pivotArea XML
495
- const xAttrs = Object.entries(attributes)
496
- .map(([k, v]) => `${k}="${v}"`)
497
- .join(" ");
498
- this.pivotAreaXmlBuffer.push(xAttrs ? `<x ${xAttrs}/>` : "<x/>");
815
+ // Collect x element for pivotArea XML (re-encode attribute values for XML safety)
816
+ const xAttrs = (0, raw_xml_collector_1.serializeAttributes)(attributes);
817
+ if (this.state.inAutoSortScope) {
818
+ this.autoSortScopeXmlBuffer.push(xAttrs ? `<x ${xAttrs}/>` : "<x/>");
819
+ }
820
+ else {
821
+ this.pivotAreaXmlBuffer.push(xAttrs ? `<x ${xAttrs}/>` : "<x/>");
822
+ }
499
823
  }
500
824
  else if (this.currentRowItem) {
501
- this.currentRowItem.x.push({ v: attributes.v ? parseInt(attributes.v, 10) : 0 });
825
+ this.currentRowItem.x.push({ v: parseInt(attributes.v ?? "0", 10) });
502
826
  }
503
827
  else if (this.currentColItem) {
504
- this.currentColItem.x.push({ v: attributes.v ? parseInt(attributes.v, 10) : 0 });
828
+ this.currentColItem.x.push({ v: parseInt(attributes.v ?? "0", 10) });
505
829
  }
506
830
  break;
507
831
  case "chartFormats":
508
- this.state.inChartFormats = true;
832
+ this.state.currentSection = "chartFormats";
509
833
  break;
510
834
  case "chartFormat":
511
- if (this.state.inChartFormats && this.model) {
835
+ if (this.state.currentSection === "chartFormats" && this.model) {
512
836
  this.currentChartFormat = {
513
- chart: attributes.chart ? parseInt(attributes.chart, 10) : 0,
514
- format: attributes.format ? parseInt(attributes.format, 10) : 0,
515
- series: attributes.series === "1"
837
+ chart: parseInt(attributes.chart ?? "0", 10),
838
+ format: parseInt(attributes.format ?? "0", 10),
839
+ series: attributes.series !== undefined ? attributes.series === "1" : undefined
516
840
  };
517
841
  }
518
842
  break;
519
843
  case "pivotArea":
520
- // Start collecting pivotArea XML for chartFormat
844
+ // Start collecting pivotArea XML for chartFormat or autoSortScope
521
845
  if (this.currentChartFormat) {
522
846
  this.state.inPivotArea = true;
523
- const attrsStr = Object.entries(attributes)
524
- .map(([k, v]) => `${k}="${v}"`)
525
- .join(" ");
847
+ const attrsStr = (0, raw_xml_collector_1.serializeAttributes)(attributes);
526
848
  this.pivotAreaXmlBuffer = [attrsStr ? `<pivotArea ${attrsStr}>` : "<pivotArea>"];
527
849
  }
850
+ else if (this.state.inAutoSortScope) {
851
+ this.state.inPivotArea = true;
852
+ const attrsStr = (0, raw_xml_collector_1.serializeAttributes)(attributes);
853
+ this.autoSortScopeXmlBuffer.push(attrsStr ? `<pivotArea ${attrsStr}>` : "<pivotArea>");
854
+ }
528
855
  break;
529
856
  case "references":
530
857
  case "reference":
531
858
  // Collect nested elements in pivotArea
532
859
  if (this.state.inPivotArea) {
533
- this.pivotAreaDepth++;
534
- const attrsStr = Object.entries(attributes)
535
- .map(([k, v]) => `${k}="${v}"`)
536
- .join(" ");
537
- this.pivotAreaXmlBuffer.push(`<${name}${attrsStr ? " " + attrsStr : ""}>`);
860
+ const attrsStr = (0, raw_xml_collector_1.serializeAttributes)(attributes);
861
+ if (this.state.inAutoSortScope) {
862
+ this.autoSortScopeXmlBuffer.push(`<${name}${attrsStr ? " " + attrsStr : ""}>`);
863
+ }
864
+ else {
865
+ this.pivotAreaXmlBuffer.push(`<${name}${attrsStr ? " " + attrsStr : ""}>`);
866
+ }
538
867
  }
539
868
  break;
540
869
  case "field":
541
870
  // Handle field element (used in rowFields, colFields)
542
871
  if (this.model) {
543
- const fieldIndex = parseInt(attributes.x || "0", 10);
544
- if (this.state.inRowFields) {
872
+ const fieldIndex = parseInt(attributes.x ?? "0", 10);
873
+ if (this.state.currentSection === "rowFields") {
545
874
  this.model.rowFields.push(fieldIndex);
546
875
  }
547
- else if (this.state.inColFields) {
876
+ else if (this.state.currentSection === "colFields") {
548
877
  this.model.colFields.push(fieldIndex);
549
878
  }
550
879
  }
551
880
  break;
552
881
  case "dataField":
553
- if (this.state.inDataFields && this.model) {
882
+ if (this.state.currentSection === "dataFields" && this.model) {
554
883
  this.model.dataFields.push({
555
- name: (0, utils_1.xmlDecode)(attributes.name || ""),
556
- fld: parseInt(attributes.fld || "0", 10),
557
- baseField: attributes.baseField ? parseInt(attributes.baseField, 10) : 0,
558
- baseItem: attributes.baseItem ? parseInt(attributes.baseItem, 10) : 0,
559
- subtotal: attributes.subtotal
884
+ name: attributes.name ?? "",
885
+ fld: parseInt(attributes.fld ?? "0", 10),
886
+ baseField: attributes.baseField !== undefined ? parseInt(attributes.baseField, 10) : undefined,
887
+ baseItem: attributes.baseItem !== undefined ? parseInt(attributes.baseItem, 10) : undefined,
888
+ subtotal: pivot_table_1.VALID_SUBTOTALS.has(attributes.subtotal)
889
+ ? attributes.subtotal
890
+ : undefined,
891
+ numFmtId: attributes.numFmtId !== undefined ? parseInt(attributes.numFmtId, 10) : undefined
560
892
  });
561
893
  }
562
894
  break;
563
895
  case "pivotTableStyleInfo":
564
896
  if (this.model) {
565
897
  this.model.styleName = attributes.name;
898
+ this.model.styleInfo = {
899
+ name: attributes.name,
900
+ showRowHeaders: attributes.showRowHeaders,
901
+ showColHeaders: attributes.showColHeaders,
902
+ showRowStripes: attributes.showRowStripes,
903
+ showColStripes: attributes.showColStripes,
904
+ showLastColumn: attributes.showLastColumn
905
+ };
906
+ }
907
+ break;
908
+ case "extLst":
909
+ // Start collecting extLst XML for roundtrip preservation
910
+ if (this.model) {
911
+ this.extLstCollector.start(attributes);
912
+ }
913
+ break;
914
+ case "formats":
915
+ // Start collecting formats XML for roundtrip preservation
916
+ if (this.model) {
917
+ this.formatsCollector.start(attributes);
918
+ }
919
+ break;
920
+ case "conditionalFormats":
921
+ // Start collecting conditionalFormats XML for roundtrip preservation
922
+ if (this.model) {
923
+ this.conditionalFormatsCollector.start(attributes);
924
+ }
925
+ break;
926
+ case "filters":
927
+ // Start collecting filters XML for roundtrip preservation
928
+ // <filters> appears between pivotTableStyleInfo and extLst per OOXML schema
929
+ if (this.model) {
930
+ this.filtersCollector.start(attributes);
931
+ }
932
+ break;
933
+ default:
934
+ // Catch-all: collect any unhandled top-level child element as raw XML.
935
+ // This preserves elements like pivotHierarchies, rowHierarchiesUsage,
936
+ // colHierarchiesUsage, etc. that we don't individually model.
937
+ // R8-B1: Only activate at the top level of pivotTableDefinition — NOT inside
938
+ // known sections (pivotFields, rowFields, etc.) or pivotArea/autoSortScope,
939
+ // otherwise the collector would steal subsequent tags from normal parsing.
940
+ if (this.model &&
941
+ this.state.currentSection === null &&
942
+ !this.state.inPivotArea &&
943
+ !this.state.inAutoSortScope) {
944
+ this.unknownCollector.startAs(name, attributes);
566
945
  }
567
946
  break;
568
947
  }
569
948
  return true;
570
949
  }
571
- parseText(_text) {
572
- // No text content in pivot table elements
950
+ parseText(text) {
951
+ // Forward text nodes to whichever raw-XML collector is active (B3 fix)
952
+ if (this.extLstCollector.active) {
953
+ this.extLstCollector.feedText(text);
954
+ }
955
+ else if (this.formatsCollector.active) {
956
+ this.formatsCollector.feedText(text);
957
+ }
958
+ else if (this.conditionalFormatsCollector.active) {
959
+ this.conditionalFormatsCollector.feedText(text);
960
+ }
961
+ else if (this.filtersCollector.active) {
962
+ this.filtersCollector.feedText(text);
963
+ }
964
+ else if (this.unknownCollector.active) {
965
+ this.unknownCollector.feedText(text);
966
+ }
967
+ }
968
+ /** Feed a close-tag to a collector; if it completes, store the result on the model. */
969
+ tryCloseCollector(collector, name, modelKey) {
970
+ if (collector.feedClose(name)) {
971
+ if (this.model) {
972
+ this.model[modelKey] = collector.result;
973
+ }
974
+ collector.reset();
975
+ }
573
976
  }
574
977
  parseClose(name) {
978
+ // Handle raw-XML collectors — close tags
979
+ if (this.extLstCollector.active) {
980
+ this.tryCloseCollector(this.extLstCollector, name, "extLstXml");
981
+ return true;
982
+ }
983
+ if (this.formatsCollector.active) {
984
+ this.tryCloseCollector(this.formatsCollector, name, "formatsXml");
985
+ return true;
986
+ }
987
+ if (this.conditionalFormatsCollector.active) {
988
+ this.tryCloseCollector(this.conditionalFormatsCollector, name, "conditionalFormatsXml");
989
+ return true;
990
+ }
991
+ if (this.filtersCollector.active) {
992
+ this.tryCloseCollector(this.filtersCollector, name, "filtersXml");
993
+ return true;
994
+ }
995
+ if (this.unknownCollector.active) {
996
+ if (this.unknownCollector.feedClose(name)) {
997
+ this.unknownElementsXmlParts.push(this.unknownCollector.result);
998
+ this.unknownCollector.reset();
999
+ }
1000
+ return true;
1001
+ }
575
1002
  // Handle pivotArea nested elements - close tags
576
1003
  if (this.state.inPivotArea) {
577
1004
  if (name === "pivotArea") {
578
- this.pivotAreaXmlBuffer.push("</pivotArea>");
579
- if (this.currentChartFormat) {
580
- this.currentChartFormat.pivotAreaXml = this.pivotAreaXmlBuffer.join("");
1005
+ if (this.state.inAutoSortScope) {
1006
+ this.autoSortScopeXmlBuffer.push("</pivotArea>");
1007
+ }
1008
+ else {
1009
+ this.pivotAreaXmlBuffer.push("</pivotArea>");
1010
+ if (this.currentChartFormat) {
1011
+ this.currentChartFormat.pivotAreaXml = this.pivotAreaXmlBuffer.join("");
1012
+ }
1013
+ this.pivotAreaXmlBuffer = [];
581
1014
  }
582
1015
  this.state.inPivotArea = false;
583
- this.pivotAreaXmlBuffer = [];
584
- this.pivotAreaDepth = 0;
585
1016
  return true;
586
1017
  }
587
1018
  else if (name === "references" || name === "reference") {
588
- this.pivotAreaXmlBuffer.push(`</${name}>`);
589
- this.pivotAreaDepth--;
1019
+ if (this.state.inAutoSortScope) {
1020
+ this.autoSortScopeXmlBuffer.push(`</${name}>`);
1021
+ }
1022
+ else {
1023
+ this.pivotAreaXmlBuffer.push(`</${name}>`);
1024
+ }
590
1025
  return true;
591
1026
  }
592
1027
  // x elements are self-closing, no need to handle close
@@ -594,10 +1029,20 @@ class PivotTableXform extends base_xform_1.BaseXform {
594
1029
  }
595
1030
  switch (name) {
596
1031
  case this.tag:
597
- // End of pivotTableDefinition
1032
+ // End of pivotTableDefinition — store any collected unknown elements
1033
+ if (this.model && this.unknownElementsXmlParts.length > 0) {
1034
+ this.model.unknownElementsXml = this.unknownElementsXmlParts.join("");
1035
+ }
598
1036
  return false;
599
1037
  case "pivotFields":
600
- this.state.inPivotFields = false;
1038
+ case "rowFields":
1039
+ case "colFields":
1040
+ case "dataFields":
1041
+ case "pageFields":
1042
+ case "rowItems":
1043
+ case "colItems":
1044
+ case "chartFormats":
1045
+ this.state.currentSection = null;
601
1046
  break;
602
1047
  case "pivotField":
603
1048
  if (this.currentPivotField && this.model) {
@@ -606,199 +1051,139 @@ class PivotTableXform extends base_xform_1.BaseXform {
606
1051
  }
607
1052
  break;
608
1053
  case "items":
609
- this.state.inItems = false;
610
- break;
611
- case "rowFields":
612
- this.state.inRowFields = false;
613
- break;
614
- case "colFields":
615
- this.state.inColFields = false;
616
- break;
617
- case "dataFields":
618
- this.state.inDataFields = false;
619
- break;
620
- case "rowItems":
621
- this.state.inRowItems = false;
1054
+ // No close handling needed — item parsing guarded by currentPivotField
622
1055
  break;
623
- case "colItems":
624
- this.state.inColItems = false;
1056
+ case "autoSortScope":
1057
+ // Finish collecting autoSortScope XML
1058
+ if (this.state.inAutoSortScope && this.currentPivotField) {
1059
+ this.autoSortScopeXmlBuffer.push("</autoSortScope>");
1060
+ this.currentPivotField.autoSortScopeXml = this.autoSortScopeXmlBuffer.join("");
1061
+ this.autoSortScopeXmlBuffer = [];
1062
+ this.state.inAutoSortScope = false;
1063
+ }
625
1064
  break;
626
1065
  case "i":
627
1066
  // Finish row/col item
628
1067
  if (this.currentRowItem && this.model) {
629
- this.model.rowItems.push(this.currentRowItem);
1068
+ this.model.rowItems?.push(this.currentRowItem);
630
1069
  this.currentRowItem = null;
631
1070
  }
632
1071
  else if (this.currentColItem && this.model) {
633
- this.model.colItems.push(this.currentColItem);
1072
+ this.model.colItems?.push(this.currentColItem);
634
1073
  this.currentColItem = null;
635
1074
  }
636
1075
  break;
637
- case "chartFormats":
638
- this.state.inChartFormats = false;
639
- break;
640
1076
  case "chartFormat":
641
1077
  if (this.currentChartFormat && this.model) {
642
- this.model.chartFormats.push(this.currentChartFormat);
1078
+ this.model.chartFormats?.push(this.currentChartFormat);
643
1079
  this.currentChartFormat = null;
644
1080
  }
645
1081
  break;
646
1082
  }
647
1083
  return true;
648
1084
  }
649
- reconcile(_model, _options) {
650
- // No reconciliation needed
651
- }
652
1085
  }
653
1086
  exports.PivotTableXform = PivotTableXform;
654
1087
  PivotTableXform.PIVOT_TABLE_ATTRIBUTES = {
655
1088
  xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
656
1089
  };
1090
+ PivotTableXform.EXTLST_XML = "<extLst>" +
1091
+ '<ext uri="{962EF5D1-5CA2-4c93-8EF4-DBF5C05439D2}" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main">' +
1092
+ '<x14:pivotTableDefinition hideValuesRow="1" xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main"/>' +
1093
+ "</ext>" +
1094
+ '<ext uri="{747A6164-185A-40DC-8AA5-F01512510D54}" xmlns:xpdl="http://schemas.microsoft.com/office/spreadsheetml/2016/pivotdefaultlayout">' +
1095
+ '<xpdl:pivotTableDefinition16 EnabledSubtotalsDefault="0" SubtotalsOnTopDefault="0"/>' +
1096
+ "</ext>" +
1097
+ "</extLst>";
657
1098
  // Helpers
658
- /**
659
- * Build rowItems XML - one item for each unique value in row fields, plus grand total.
660
- * Each <i> represents a row in the pivot table.
661
- * - Regular items: <i><x/></i> for index 0, <i><x v="index"/></i> for index > 0
662
- * - Grand total: <i t="grand"><x/></i>
663
- * Note: When v=0, the v attribute should be omitted (Excel convention)
664
- */
665
- function buildRowItems(rows, cacheFields) {
666
- if (rows.length === 0) {
667
- // No row fields - just grand total
668
- return { count: 1, xml: '<i t="grand"><x /></i>' };
669
- }
670
- // Get unique values count from the first row field
671
- const rowFieldIndex = rows[0];
672
- const sharedItems = cacheFields[rowFieldIndex]?.sharedItems || [];
673
- const itemCount = sharedItems.length;
674
- // Build items: one for each unique value + grand total
675
- const items = [];
676
- // Regular items - reference each unique value by index
677
- // Note: v="0" should be omitted (Excel uses <x/> instead of <x v="0"/>)
678
- for (let i = 0; i < itemCount; i++) {
679
- if (i === 0) {
680
- items.push("<i><x /></i>");
681
- }
682
- else {
683
- items.push(`<i><x v="${i}" /></i>`);
684
- }
685
- }
686
- // Grand total row
687
- items.push('<i t="grand"><x /></i>');
1099
+ /** Parse attributes of a row/col `<i>` element into a RowColItem. */
1100
+ function parseRowColItem(attributes) {
688
1101
  return {
689
- count: items.length,
690
- xml: items.join("\n ")
1102
+ t: attributes.t,
1103
+ r: attributes.r !== undefined ? parseInt(attributes.r, 10) : undefined,
1104
+ i: attributes.i !== undefined ? parseInt(attributes.i, 10) : undefined,
1105
+ x: []
691
1106
  };
692
1107
  }
693
1108
  /**
694
- * Build colItems XML - one item for each unique value in column fields, plus grand total.
695
- * When there are multiple data fields (values), each column value may have sub-columns.
696
- * Note: When v=0, the v attribute should be omitted (Excel convention)
1109
+ * Render dataField XML elements for all values in the pivot table.
1110
+ * Each value field gets its own metric from the `valueMetrics` array.
697
1111
  */
698
- function buildColItems(columns, cacheFields, valueCount) {
699
- if (columns.length === 0) {
700
- // No column fields - columns are based on data fields (values)
701
- if (valueCount > 1) {
702
- // Multiple values: one column per value + grand total
703
- const items = [];
704
- for (let i = 0; i < valueCount; i++) {
705
- if (i === 0) {
706
- items.push("<i><x /></i>");
707
- }
708
- else {
709
- items.push(`<i><x v="${i}" /></i>`);
710
- }
711
- }
712
- items.push('<i t="grand"><x /></i>');
713
- return { count: items.length, xml: items.join("\n ") };
1112
+ function renderDataFields(xmlStream, cacheFields, values, valueMetrics) {
1113
+ xmlStream.openNode("dataFields", { count: values.length });
1114
+ for (let i = 0; i < values.length; i++) {
1115
+ const valueIndex = values[i];
1116
+ const metric = valueMetrics[i] ?? "sum";
1117
+ const metricName = pivot_table_1.METRIC_DISPLAY_NAMES[metric];
1118
+ const field = cacheFields[valueIndex];
1119
+ if (!field) {
1120
+ throw new Error(`Value field index ${valueIndex} is out of bounds (cacheFields has ${cacheFields.length} entries)`);
714
1121
  }
715
- // Single value: just grand total
716
- return { count: 1, xml: '<i t="grand"><x /></i>' };
717
- }
718
- // Get unique values count from the first column field
719
- const colFieldIndex = columns[0];
720
- const sharedItems = cacheFields[colFieldIndex]?.sharedItems || [];
721
- const itemCount = sharedItems.length;
722
- // Build items: one for each unique value + grand total
723
- const items = [];
724
- // Regular items - reference each unique value by index
725
- // Note: v="0" should be omitted (Excel uses <x/> instead of <x v="0"/>)
726
- for (let i = 0; i < itemCount; i++) {
727
- if (i === 0) {
728
- items.push("<i><x /></i>");
729
- }
730
- else {
731
- items.push(`<i><x v="${i}" /></i>`);
1122
+ const attrs = {
1123
+ name: `${metricName} of ${field.name}`,
1124
+ fld: valueIndex,
1125
+ baseField: 0,
1126
+ baseItem: 0
1127
+ };
1128
+ // OOXML default is "sum", so omit subtotal attribute for sum
1129
+ if (metric !== "sum") {
1130
+ attrs.subtotal = metric;
732
1131
  }
1132
+ xmlStream.leafNode("dataField", attrs);
733
1133
  }
734
- // Grand total column
735
- items.push('<i t="grand"><x /></i>');
736
- return {
737
- count: items.length,
738
- xml: items.join("\n ")
739
- };
1134
+ xmlStream.closeNode();
740
1135
  }
741
- /**
742
- * Build dataField XML elements for all values in the pivot table.
743
- * Supports multiple values when columns is empty.
744
- */
745
- function buildDataFields(cacheFields, values, metric) {
746
- const metricName = metric === "count" ? "Count" : "Sum";
747
- // For 'count' metric, Excel requires subtotal="count" attribute
748
- const subtotalAttr = metric === "count" ? ' subtotal="count"' : "";
749
- return values
750
- .map(valueIndex => `<dataField
751
- name="${metricName} of ${(0, utils_1.xmlEncode)(cacheFields[valueIndex].name)}"
752
- fld="${valueIndex}"
753
- baseField="0"
754
- baseItem="0"${subtotalAttr}
755
- />`)
756
- .join("");
757
- }
758
- function renderPivotFields(pivotTable) {
1136
+ function renderPivotFields(xmlStream, pivotTable) {
759
1137
  // Pre-compute field type lookup for O(1) access
760
1138
  const rowSet = new Set(pivotTable.rows);
761
1139
  const colSet = new Set(pivotTable.columns);
762
1140
  const valueSet = new Set(pivotTable.values);
763
- return pivotTable.cacheFields
764
- .map((cacheField, fieldIndex) => {
1141
+ const pageSet = new Set(pivotTable.pages ?? []);
1142
+ xmlStream.openNode("pivotFields", { count: pivotTable.cacheFields.length });
1143
+ for (let fieldIndex = 0; fieldIndex < pivotTable.cacheFields.length; fieldIndex++) {
1144
+ const cacheField = pivotTable.cacheFields[fieldIndex];
765
1145
  const isRow = rowSet.has(fieldIndex);
766
1146
  const isCol = colSet.has(fieldIndex);
767
1147
  const isValue = valueSet.has(fieldIndex);
768
- return renderPivotField(isRow, isCol, isValue, cacheField.sharedItems);
769
- })
770
- .join("");
1148
+ const isPage = pageSet.has(fieldIndex);
1149
+ renderPivotField(xmlStream, isRow, isCol, isValue, isPage, cacheField.sharedItems);
1150
+ }
1151
+ xmlStream.closeNode();
771
1152
  }
772
- function renderPivotField(isRow, isCol, isValue, sharedItems) {
1153
+ function renderPivotField(xmlStream, isRow, isCol, isValue, isPage, sharedItems) {
773
1154
  // A field can be both a row/column field AND a value field
774
1155
  // In this case, it needs both axis attribute AND dataField="1"
775
- if (isRow || isCol) {
776
- const axis = isRow ? "axisRow" : "axisCol";
777
- // Row and column fields should NOT have defaultSubtotal="0"
778
- let axisAttributes = 'compact="0" outline="0" showAll="0"';
779
- // If also a value field, add dataField="1"
1156
+ if (isRow || isCol || isPage) {
1157
+ if (!sharedItems) {
1158
+ throw new Error("sharedItems is required for axis field (row/column/page)");
1159
+ }
1160
+ const axis = isRow ? "axisRow" : isCol ? "axisCol" : "axisPage";
1161
+ const attrs = { axis };
780
1162
  if (isValue) {
781
- axisAttributes = `dataField="1" ${axisAttributes}`;
1163
+ attrs.dataField = "1";
782
1164
  }
1165
+ attrs.compact = "0";
1166
+ attrs.outline = "0";
1167
+ attrs.showAll = "0";
1168
+ xmlStream.openNode("pivotField", attrs);
783
1169
  // items = one for each shared item + one default item
784
- const itemsXml = [
785
- ...sharedItems.map((_item, index) => `<item x="${index}" />`),
786
- '<item t="default" />' // Required default item for subtotals/grand totals
787
- ].join("\n ");
788
- return `
789
- <pivotField axis="${axis}" ${axisAttributes}>
790
- <items count="${sharedItems.length + 1}">
791
- ${itemsXml}
792
- </items>
793
- </pivotField>
794
- `;
1170
+ xmlStream.openNode("items", { count: sharedItems.length + 1 });
1171
+ for (let i = 0; i < sharedItems.length; i++) {
1172
+ xmlStream.leafNode("item", { x: i });
1173
+ }
1174
+ xmlStream.leafNode("item", { t: "default" }); // Required default item for subtotals/grand totals
1175
+ xmlStream.closeNode(); // items
1176
+ xmlStream.closeNode(); // pivotField
1177
+ return;
795
1178
  }
796
1179
  // Value fields and non-axis fields should have defaultSubtotal="0"
797
- const defaultAttributes = 'compact="0" outline="0" showAll="0" defaultSubtotal="0"';
798
- return `
799
- <pivotField
800
- ${isValue ? 'dataField="1"' : ""}
801
- ${defaultAttributes}
802
- />
803
- `;
1180
+ const attrs = {};
1181
+ if (isValue) {
1182
+ attrs.dataField = "1";
1183
+ }
1184
+ attrs.compact = "0";
1185
+ attrs.outline = "0";
1186
+ attrs.showAll = "0";
1187
+ attrs.defaultSubtotal = "0";
1188
+ xmlStream.leafNode("pivotField", attrs);
804
1189
  }