@shbernal/ts-xlsx 3.1.0 → 3.2.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 (214) hide show
  1. package/README.md +30 -23
  2. package/dist/bytes.d.ts +3 -2
  3. package/dist/core/address.d.ts +36 -0
  4. package/dist/core/address.js +47 -9
  5. package/dist/core/array-formula-ranges.d.ts +33 -0
  6. package/dist/core/array-formula-ranges.js +62 -0
  7. package/dist/core/color-resolution.js +2 -2
  8. package/dist/core/column.js +3 -2
  9. package/dist/core/comment-thread.d.ts +7 -3
  10. package/dist/core/conditional-formatting-overlay.d.ts +9 -0
  11. package/dist/core/conditional-formatting-overlay.js +28 -1
  12. package/dist/core/conditional-formatting.d.ts +98 -10
  13. package/dist/core/conditional-formatting.js +42 -1
  14. package/dist/core/data-validation-overlay.d.ts +5 -0
  15. package/dist/core/data-validation-overlay.js +15 -0
  16. package/dist/core/data-validation.d.ts +16 -0
  17. package/dist/core/data-validation.js +15 -0
  18. package/dist/core/date-format.js +14 -4
  19. package/dist/core/date.js +1 -1
  20. package/dist/core/formula-references.d.ts +26 -0
  21. package/dist/core/formula-references.js +173 -0
  22. package/dist/core/formula-scan.d.ts +2 -0
  23. package/dist/core/formula-scan.js +58 -0
  24. package/dist/core/formula.d.ts +51 -18
  25. package/dist/core/formula.js +77 -86
  26. package/dist/core/function-values.d.ts +4 -0
  27. package/dist/core/function-values.js +58 -0
  28. package/dist/core/future-functions.d.ts +3 -0
  29. package/dist/core/{modern-functions.js → future-functions.js} +119 -88
  30. package/dist/core/grid-edits.d.ts +28 -0
  31. package/dist/core/grid-edits.js +193 -25
  32. package/dist/core/hyperlink.d.ts +39 -0
  33. package/dist/core/hyperlink.js +88 -0
  34. package/dist/core/image.d.ts +36 -5
  35. package/dist/core/image.js +9 -0
  36. package/dist/core/internal.d.ts +3 -1
  37. package/dist/core/limits.d.ts +5 -3
  38. package/dist/core/merge.d.ts +13 -0
  39. package/dist/core/merge.js +15 -1
  40. package/dist/core/page-setup.d.ts +26 -11
  41. package/dist/core/pivot-table.d.ts +49 -10
  42. package/dist/core/pivot-table.js +55 -21
  43. package/dist/core/protection.d.ts +66 -4
  44. package/dist/core/protection.js +4 -0
  45. package/dist/core/row.js +3 -2
  46. package/dist/core/structured-reference.d.ts +7 -0
  47. package/dist/core/structured-reference.js +7 -0
  48. package/dist/core/table.d.ts +38 -8
  49. package/dist/core/table.js +37 -7
  50. package/dist/core/theme.d.ts +2 -0
  51. package/dist/core/theme.js +4 -1
  52. package/dist/core/value.d.ts +51 -28
  53. package/dist/core/value.js +15 -12
  54. package/dist/core/workbook-styles.d.ts +17 -1
  55. package/dist/core/workbook-styles.js +4 -0
  56. package/dist/core/workbook-theme.js +5 -3
  57. package/dist/core/workbook.d.ts +20 -3
  58. package/dist/core/workbook.js +26 -5
  59. package/dist/core/worksheet-merges.d.ts +9 -4
  60. package/dist/core/worksheet-merges.js +17 -11
  61. package/dist/core/worksheet-model.d.ts +1 -1
  62. package/dist/core/worksheet-model.js +4 -0
  63. package/dist/core/worksheet-pictures.d.ts +3 -3
  64. package/dist/core/worksheet-pictures.js +7 -6
  65. package/dist/core/worksheet.d.ts +110 -21
  66. package/dist/core/worksheet.js +174 -49
  67. package/dist/entries/core.d.ts +7 -5
  68. package/dist/entries/core.js +4 -3
  69. package/dist/entries/errors.d.ts +1 -0
  70. package/dist/entries/errors.js +1 -0
  71. package/dist/errors.js +1 -1
  72. package/dist/io/cell-metadata/metadata.d.ts +42 -0
  73. package/dist/io/cell-metadata/metadata.js +35 -0
  74. package/dist/io/cell-metadata/rich-values.d.ts +11 -0
  75. package/dist/io/cell-metadata/rich-values.js +80 -0
  76. package/dist/io/csv/delimiter.d.ts +14 -2
  77. package/dist/io/csv/delimiter.js +12 -0
  78. package/dist/io/csv/errors.d.ts +14 -0
  79. package/dist/io/csv/errors.js +5 -0
  80. package/dist/io/csv/read.d.ts +6 -1
  81. package/dist/io/csv/read.js +46 -24
  82. package/dist/io/csv/write.d.ts +10 -3
  83. package/dist/io/csv/write.js +16 -7
  84. package/dist/io/opc/part-paths.js +4 -4
  85. package/dist/io/opc/read-opc.d.ts +4 -0
  86. package/dist/io/opc/read-opc.js +10 -4
  87. package/dist/io/opc/strict-relationships.d.ts +2 -0
  88. package/dist/io/opc/strict-relationships.js +12 -0
  89. package/dist/io/read-policy/column-budget.d.ts +26 -0
  90. package/dist/io/{xlsx → read-policy}/column-budget.js +4 -8
  91. package/dist/io/read-policy/read-repair.d.ts +74 -0
  92. package/dist/io/read-policy/read-repair.js +119 -0
  93. package/dist/io/style/cell-style-resolution.d.ts +25 -0
  94. package/dist/io/style/cell-style-resolution.js +30 -0
  95. package/dist/io/xlsb/formula.js +4 -2
  96. package/dist/io/xlsb/ptg-functions.js +110 -0
  97. package/dist/io/xlsb/read-metadata.d.ts +7 -0
  98. package/dist/io/xlsb/read-metadata.js +69 -0
  99. package/dist/io/xlsb/read-worksheet.d.ts +6 -0
  100. package/dist/io/xlsb/read-worksheet.js +95 -37
  101. package/dist/io/xlsb/read.js +26 -8
  102. package/dist/io/xlsb/record-types.d.ts +13 -0
  103. package/dist/io/xlsb/record-types.js +13 -0
  104. package/dist/io/xlsb/sheet-protection.d.ts +20 -0
  105. package/dist/io/xlsb/sheet-protection.js +60 -0
  106. package/dist/io/xlsx/cell-accumulator.d.ts +20 -12
  107. package/dist/io/xlsx/cell-accumulator.js +49 -19
  108. package/dist/io/xlsx/cell-metadata.d.ts +39 -0
  109. package/dist/io/xlsx/cell-metadata.js +162 -0
  110. package/dist/io/xlsx/cell-value.d.ts +16 -4
  111. package/dist/io/xlsx/cell-value.js +38 -21
  112. package/dist/io/xlsx/color-xml.js +1 -1
  113. package/dist/io/xlsx/column-span.d.ts +11 -0
  114. package/dist/io/xlsx/column-span.js +9 -0
  115. package/dist/io/xlsx/comments.d.ts +3 -28
  116. package/dist/io/xlsx/comments.js +3 -65
  117. package/dist/io/xlsx/conditional-formatting.d.ts +42 -25
  118. package/dist/io/xlsx/conditional-formatting.js +424 -129
  119. package/dist/io/xlsx/data-validation.d.ts +12 -7
  120. package/dist/io/xlsx/data-validation.js +41 -29
  121. package/dist/io/xlsx/edit-vba.js +43 -36
  122. package/dist/io/xlsx/hyperlinks.d.ts +16 -50
  123. package/dist/io/xlsx/hyperlinks.js +8 -37
  124. package/dist/io/xlsx/images.d.ts +49 -16
  125. package/dist/io/xlsx/images.js +145 -44
  126. package/dist/io/xlsx/namespaces.d.ts +8 -0
  127. package/dist/io/xlsx/namespaces.js +4 -0
  128. package/dist/io/xlsx/package-plan.d.ts +48 -15
  129. package/dist/io/xlsx/package-plan.js +46 -9
  130. package/dist/io/xlsx/part-names.d.ts +6 -8
  131. package/dist/io/xlsx/part-names.js +3 -7
  132. package/dist/io/xlsx/pivot.d.ts +5 -5
  133. package/dist/io/xlsx/pivot.js +23 -22
  134. package/dist/io/xlsx/preserved-splices.d.ts +6 -0
  135. package/dist/io/xlsx/preserved-splices.js +74 -0
  136. package/dist/io/xlsx/read-comments.d.ts +29 -0
  137. package/dist/io/xlsx/read-comments.js +75 -0
  138. package/dist/io/xlsx/read-parts.d.ts +2 -2
  139. package/dist/io/xlsx/read-parts.js +44 -37
  140. package/dist/io/xlsx/read-pivot.js +9 -3
  141. package/dist/io/xlsx/read-rich-runs.d.ts +8 -5
  142. package/dist/io/xlsx/read-rich-runs.js +18 -1
  143. package/dist/io/xlsx/read-rows.d.ts +18 -7
  144. package/dist/io/xlsx/read-rows.js +47 -37
  145. package/dist/io/xlsx/read-styles.d.ts +0 -25
  146. package/dist/io/xlsx/read-styles.js +39 -34
  147. package/dist/io/xlsx/read-workbook-xml.d.ts +6 -0
  148. package/dist/io/xlsx/read-workbook-xml.js +9 -4
  149. package/dist/io/xlsx/read-worksheet.d.ts +8 -3
  150. package/dist/io/xlsx/read-worksheet.js +45 -23
  151. package/dist/io/xlsx/read.d.ts +0 -4
  152. package/dist/io/xlsx/read.js +17 -21
  153. package/dist/io/xlsx/relationships.d.ts +3 -0
  154. package/dist/io/xlsx/relationships.js +4 -0
  155. package/dist/io/xlsx/row-position.d.ts +3 -1
  156. package/dist/io/xlsx/row-xml.d.ts +18 -7
  157. package/dist/io/xlsx/row-xml.js +54 -35
  158. package/dist/io/xlsx/sheet-properties.js +68 -42
  159. package/dist/io/xlsx/strict-parts.d.ts +7 -0
  160. package/dist/io/xlsx/strict-parts.js +171 -0
  161. package/dist/io/xlsx/style-elements.d.ts +0 -2
  162. package/dist/io/xlsx/style-elements.js +9 -29
  163. package/dist/io/xlsx/styles.d.ts +5 -0
  164. package/dist/io/xlsx/styles.js +11 -5
  165. package/dist/io/xlsx/tables.d.ts +10 -2
  166. package/dist/io/xlsx/tables.js +49 -42
  167. package/dist/io/xlsx/theme-xml.d.ts +6 -0
  168. package/dist/io/xlsx/theme-xml.js +16 -19
  169. package/dist/io/xlsx/threaded-comments.d.ts +6 -1
  170. package/dist/io/xlsx/threaded-comments.js +14 -7
  171. package/dist/io/xlsx/workbook-xml.d.ts +13 -24
  172. package/dist/io/xlsx/workbook-xml.js +15 -33
  173. package/dist/io/xlsx/worksheet-xml.d.ts +5 -2
  174. package/dist/io/xlsx/worksheet-xml.js +37 -69
  175. package/dist/io/xlsx/write-stream.d.ts +28 -6
  176. package/dist/io/xlsx/write-stream.js +42 -17
  177. package/dist/io/xlsx/write.d.ts +11 -4
  178. package/dist/io/xlsx/write.js +153 -90
  179. package/dist/rel-type.d.ts +9 -0
  180. package/dist/rel-type.js +3 -0
  181. package/dist/vba/bytes.d.ts +2 -0
  182. package/dist/vba/bytes.js +11 -0
  183. package/dist/vba/cfb-format.d.ts +47 -0
  184. package/dist/vba/cfb-format.js +34 -0
  185. package/dist/vba/cfb-writer.d.ts +1 -2
  186. package/dist/vba/cfb-writer.js +33 -36
  187. package/dist/vba/cfb.d.ts +9 -4
  188. package/dist/vba/cfb.js +55 -43
  189. package/dist/vba/dir-records.d.ts +6 -0
  190. package/dist/vba/dir-records.js +10 -2
  191. package/dist/vba/errors.d.ts +4 -4
  192. package/dist/vba/index.d.ts +0 -2
  193. package/dist/vba/index.js +0 -2
  194. package/dist/vba/ms-ovba.d.ts +6 -2
  195. package/dist/vba/ms-ovba.js +15 -6
  196. package/dist/vba/project-editor.js +102 -87
  197. package/dist/vba/project.d.ts +4 -0
  198. package/dist/vba/project.js +12 -6
  199. package/dist/vba/vba-encoding.d.ts +4 -10
  200. package/dist/vba/vba-encoding.js +2 -15
  201. package/dist/xml/xml-read.d.ts +29 -1
  202. package/dist/xml/xml-read.js +56 -0
  203. package/dist/xml/xml-scan.d.ts +13 -0
  204. package/dist/xml/xml-scan.js +58 -7
  205. package/dist/xml/xml.d.ts +33 -1
  206. package/dist/xml/xml.js +22 -0
  207. package/package.json +5 -4
  208. package/skills/ts-xlsx-upstream/SKILL.md +21 -13
  209. package/dist/core/modern-functions.d.ts +0 -1
  210. package/dist/io/xlsx/cell-style-resolution.d.ts +0 -25
  211. package/dist/io/xlsx/cell-style-resolution.js +0 -29
  212. package/dist/io/xlsx/column-budget.d.ts +0 -26
  213. package/dist/io/xlsx/read-repair.d.ts +0 -26
  214. package/dist/io/xlsx/read-repair.js +0 -39
@@ -1,12 +1,11 @@
1
1
  import { isImageEditAs, isOneCellAnchor, } from '../../core/image.js';
2
- import { enumToken, numFinite } from '../../xml/xml-attrs.js';
3
- import { openElements, parseXml, TextCapture } from '../../xml/xml-read.js';
2
+ import { assertWritableNumber, AuthoringError } from '../../errors.js';
3
+ import { enumToken, numFinite, numInteger } from '../../xml/xml-attrs.js';
4
+ import { elementRange, openElements, parseXml, TextCapture } from '../../xml/xml-read.js';
4
5
  import { localName } from '../../xml/xml-scan.js';
5
- import { checkedToken, numAttr, numberText, XML_DECLARATION } from '../../xml/xml.js';
6
+ import { checkedToken, escapeAttr, intAttr, numAttr, numberText, textAttr, XML_DECLARATION, } from '../../xml/xml.js';
6
7
  import { relAttr, RELATIONSHIPS_NS } from '../opc/namespaces.js';
7
- import { relationship, relationshipsPart } from '../opc/rels.js';
8
8
  import { DRAWINGML_NS, XDR_NS } from './namespaces.js';
9
- const IMAGE_REL_TYPE = `${RELATIONSHIPS_NS}/image`;
10
9
  const IMAGE_CONTENT_TYPES = new Map([
11
10
  ['png', 'image/png'],
12
11
  ['jpg', 'image/jpeg'],
@@ -23,81 +22,185 @@ export function imageContentType(extension) {
23
22
  const ext = extension.toLowerCase();
24
23
  return IMAGE_CONTENT_TYPES.get(ext) ?? `image/${ext}`;
25
24
  }
25
+ const ANCHOR_NAMESPACES = ` xmlns:xdr="${XDR_NS}" xmlns:a="${DRAWINGML_NS}" xmlns:r="${RELATIONSHIPS_NS}"`;
26
26
  export function drawingXml(images) {
27
- const anchors = images.map((image, i) => anchorXml(image, i + 1)).join('');
28
- return (XML_DECLARATION +
29
- `<xdr:wsDr xmlns:xdr="${XDR_NS}" xmlns:a="${DRAWINGML_NS}" xmlns:r="${RELATIONSHIPS_NS}">` +
30
- anchors +
31
- '</xdr:wsDr>');
27
+ const anchors = images.map((image, i) => anchorXml(image, i + 1, '')).join('');
28
+ return `${XML_DECLARATION}<xdr:wsDr${ANCHOR_NAMESPACES}>${anchors}</xdr:wsDr>`;
32
29
  }
33
- function anchorXml(image, id) {
30
+ export function mergePictures(drawing, images) {
31
+ const root = elementRange(drawing, ['wsDr']);
32
+ if (root === undefined) {
33
+ throw new AuthoringError('cannot add a picture to a kept drawing that has no <wsDr> root');
34
+ }
35
+ let lastId = 0;
36
+ for (const { attrs } of openElements(drawing, 'cNvPr')) {
37
+ lastId = Math.max(lastId, numInteger(attrs.id, 0) ?? 0);
38
+ }
39
+ const anchors = images
40
+ .map((image, i) => anchorXml(image, lastId + i + 1, ANCHOR_NAMESPACES))
41
+ .join('');
42
+ if (root.contentStart === root.end) {
43
+ const open = drawing.slice(root.start, root.end).replace(/\s*\/>$/, '>');
44
+ return `${drawing.slice(0, root.start)}${open}${anchors}</${root.name}>${drawing.slice(root.end)}`;
45
+ }
46
+ return drawing.slice(0, root.contentEnd) + anchors + drawing.slice(root.contentEnd);
47
+ }
48
+ function anchorXml(image, id, namespaces) {
34
49
  const { anchor } = image;
50
+ const pic = picXml(image, id, anchor.rotation);
35
51
  return isOneCellAnchor(anchor)
36
- ? oneCellAnchorXml(anchor.from, anchor.ext, anchor.rotation, image.embedId, id)
37
- : twoCellAnchorXml(anchor.from, anchor.to, anchor.editAs ?? 'oneCell', anchor.rotation, image.embedId, id);
52
+ ? oneCellAnchorXml(anchor.from, anchor.ext, pic, namespaces)
53
+ : twoCellAnchorXml(anchor.from, anchor.to, anchor.editAs ?? 'oneCell', pic, namespaces);
38
54
  }
39
- function twoCellAnchorXml(from, to, editAs, rotation, embedId, id) {
40
- return (`<xdr:twoCellAnchor editAs="${checkedToken(editAs, isImageEditAs, 'image anchor edit mode')}">` +
55
+ function twoCellAnchorXml(from, to, editAs, pic, namespaces) {
56
+ return (`<xdr:twoCellAnchor${namespaces} editAs="${checkedToken(editAs, isImageEditAs, 'image anchor edit mode')}">` +
41
57
  `<xdr:from>${anchorPointXml(from)}</xdr:from>` +
42
58
  `<xdr:to>${anchorPointXml(to)}</xdr:to>` +
43
- picXml(embedId, id, rotation) +
59
+ pic +
44
60
  '<xdr:clientData/>' +
45
61
  '</xdr:twoCellAnchor>');
46
62
  }
47
- function oneCellAnchorXml(from, ext, rotation, embedId, id) {
48
- return ('<xdr:oneCellAnchor>' +
63
+ function oneCellAnchorXml(from, ext, pic, namespaces) {
64
+ return (`<xdr:oneCellAnchor${namespaces}>` +
49
65
  `<xdr:from>${anchorPointXml(from)}</xdr:from>` +
50
66
  `<xdr:ext${numAttr('cx', ext.cx)}${numAttr('cy', ext.cy)}/>` +
51
- picXml(embedId, id, rotation) +
67
+ pic +
52
68
  '<xdr:clientData/>' +
53
69
  '</xdr:oneCellAnchor>');
54
70
  }
55
- function picXml(embedId, id, rotation) {
71
+ function picXml(image, id, rotation) {
72
+ const { embedId, properties, hyperlinkId } = image;
56
73
  const xfrm = rotation !== undefined ? `<a:xfrm${numAttr('rot', rotation)}/>` : '';
74
+ const attributes = textAttr('descr', properties.description) + textAttr('title', properties.title);
75
+ const link = hyperlinkId === undefined
76
+ ? ''
77
+ : `<a:hlinkClick r:id="${escapeAttr(hyperlinkId)}"` +
78
+ `${textAttr('tooltip', properties.hyperlink?.tooltip)}/>`;
79
+ const cNvPr = link === ''
80
+ ? `<xdr:cNvPr id="${id}" name="Picture ${id}"${attributes}/>`
81
+ : `<xdr:cNvPr id="${id}" name="Picture ${id}"${attributes}>${link}</xdr:cNvPr>`;
57
82
  return ('<xdr:pic>' +
58
- `<xdr:nvPicPr><xdr:cNvPr id="${id}" name="Picture ${id}"/>` +
83
+ `<xdr:nvPicPr>${cNvPr}` +
59
84
  '<xdr:cNvPicPr><a:picLocks noChangeAspect="1"/></xdr:cNvPicPr></xdr:nvPicPr>' +
60
85
  `<xdr:blipFill><a:blip r:embed="${embedId}"/>` +
86
+ srcRectXml(properties.crop) +
61
87
  '<a:stretch><a:fillRect/></a:stretch></xdr:blipFill>' +
62
88
  `<xdr:spPr>${xfrm}<a:prstGeom prst="rect"><a:avLst/></a:prstGeom></xdr:spPr>` +
63
89
  '</xdr:pic>');
64
90
  }
91
+ const CROP_UNITS = 100_000;
92
+ const INT_MAX = 2_147_483_647;
93
+ function srcRectXml(crop) {
94
+ if (crop === undefined)
95
+ return '';
96
+ const edge = (name, fraction) => {
97
+ if (fraction === undefined)
98
+ return '';
99
+ assertWritableNumber(fraction);
100
+ const units = Math.round(fraction * CROP_UNITS);
101
+ if (units > INT_MAX) {
102
+ throw new AuthoringError(`cannot write the picture crop ${name}="${units}": past xsd:int`);
103
+ }
104
+ return units === 0 ? '' : intAttr(name, units, -INT_MAX - 1);
105
+ };
106
+ const attributes = edge('l', crop.left) + edge('t', crop.top) + edge('r', crop.right) + edge('b', crop.bottom);
107
+ return attributes === '' ? '' : `<a:srcRect${attributes}/>`;
108
+ }
109
+ function cropEdge(value) {
110
+ if (value === undefined)
111
+ return undefined;
112
+ const percent = value.endsWith('%') ? numFinite(value.slice(0, -1)) : undefined;
113
+ const units = percent === undefined ? numInteger(value) : Math.round(percent * 1000);
114
+ if (units === undefined || units === 0 || units > INT_MAX || units < -INT_MAX - 1) {
115
+ return undefined;
116
+ }
117
+ return units / CROP_UNITS;
118
+ }
65
119
  function anchorPointXml(point) {
66
120
  return (`<xdr:col>${numberText(point.col)}</xdr:col>` +
67
121
  `<xdr:colOff>${numberText(point.colOff ?? 0)}</xdr:colOff>` +
68
122
  `<xdr:row>${numberText(point.row)}</xdr:row>` +
69
123
  `<xdr:rowOff>${numberText(point.rowOff ?? 0)}</xdr:rowOff>`);
70
124
  }
71
- export function drawingRelsXml(mediaTargets) {
72
- return relationshipsPart(mediaTargets.map((target, i) => relationship(`rId${i + 1}`, IMAGE_REL_TYPE, target)));
73
- }
74
125
  function blankPoint() {
75
126
  return { col: 0, row: 0, colOff: 0, rowOff: 0 };
76
127
  }
128
+ const UNMODELED_CONTENT = new Set([
129
+ 'graphicFrame',
130
+ 'sp',
131
+ 'cxnSp',
132
+ 'grpSp',
133
+ 'contentPart',
134
+ 'absoluteAnchor',
135
+ 'AlternateContent',
136
+ ]);
77
137
  export function parseDrawing(xml) {
78
138
  const anchors = [];
139
+ let fullyModeled = true;
79
140
  let from = null;
80
141
  let to = null;
81
142
  let ext;
82
143
  let editAs;
83
144
  let rotation;
84
145
  let embed;
146
+ let pictures = 0;
147
+ let properties = {};
85
148
  let target = null;
86
149
  let picDepth = 0;
87
150
  const coord = new TextCapture(COORDINATES);
88
151
  parseXml(xml, {
89
152
  onOpen(name, attrs, selfClosing, scope) {
90
153
  const local = localName(name);
91
- if (local === 'twoCellAnchor' || local === 'oneCellAnchor') {
154
+ if (UNMODELED_CONTENT.has(local)) {
155
+ fullyModeled = false;
156
+ }
157
+ else if (local === 'twoCellAnchor' || local === 'oneCellAnchor') {
92
158
  from = blankPoint();
93
159
  to = local === 'twoCellAnchor' ? blankPoint() : null;
94
160
  ext = undefined;
95
161
  rotation = undefined;
96
162
  embed = undefined;
97
- editAs = enumToken(attrs.editAs, isImageEditAs);
163
+ pictures = 0;
164
+ properties = {};
165
+ editAs =
166
+ local === 'twoCellAnchor'
167
+ ? (enumToken(attrs.editAs, isImageEditAs) ?? 'twoCell')
168
+ : undefined;
98
169
  }
99
170
  else if (local === 'pic') {
100
171
  picDepth++;
172
+ pictures++;
173
+ }
174
+ else if (local === 'cNvPr' && picDepth > 0) {
175
+ if (attrs.descr !== undefined && attrs.descr !== '')
176
+ properties.description = attrs.descr;
177
+ if (attrs.title !== undefined && attrs.title !== '')
178
+ properties.title = attrs.title;
179
+ }
180
+ else if (local === 'hlinkClick' && picDepth > 0) {
181
+ const id = relAttr(scope, attrs, 'id');
182
+ if (id !== undefined && id !== '') {
183
+ properties.hyperlinkId = id;
184
+ if (attrs.tooltip !== undefined)
185
+ properties.tooltip = attrs.tooltip;
186
+ }
187
+ }
188
+ else if (local === 'srcRect' && picDepth > 0) {
189
+ const crop = {};
190
+ const left = cropEdge(attrs.l);
191
+ const top = cropEdge(attrs.t);
192
+ const right = cropEdge(attrs.r);
193
+ const bottom = cropEdge(attrs.b);
194
+ if (left !== undefined)
195
+ crop.left = left;
196
+ if (top !== undefined)
197
+ crop.top = top;
198
+ if (right !== undefined)
199
+ crop.right = right;
200
+ if (bottom !== undefined)
201
+ crop.bottom = bottom;
202
+ if (Object.keys(crop).length > 0)
203
+ properties.crop = crop;
101
204
  }
102
205
  else if (local === 'xfrm' && picDepth > 0) {
103
206
  const rot = numFinite(attrs.rot);
@@ -120,6 +223,8 @@ export function parseDrawing(xml) {
120
223
  const value = relAttr(scope, attrs, 'embed');
121
224
  if (value !== undefined)
122
225
  embed = value;
226
+ if (relAttr(scope, attrs, 'link') !== undefined)
227
+ fullyModeled = false;
123
228
  }
124
229
  else if (target !== null) {
125
230
  coord.open(local, selfClosing);
@@ -143,15 +248,19 @@ export function parseDrawing(xml) {
143
248
  picDepth--;
144
249
  }
145
250
  else if (local === 'twoCellAnchor' || local === 'oneCellAnchor') {
146
- if (from !== null && embed !== undefined) {
147
- const rot = rotation !== undefined ? { rotation } : {};
148
- if (to !== null) {
149
- const mode = editAs !== undefined ? { editAs } : {};
150
- anchors.push({ from: { ...from }, to: { ...to }, ...mode, ...rot, embed });
151
- }
152
- else if (ext !== undefined) {
153
- anchors.push({ from: { ...from }, ext, ...rot, embed });
154
- }
251
+ const rot = rotation !== undefined ? { rotation } : {};
252
+ if (from === null || embed === undefined || pictures !== 1) {
253
+ fullyModeled = false;
254
+ }
255
+ else if (to !== null) {
256
+ const mode = editAs !== undefined ? { editAs } : {};
257
+ anchors.push({ from: { ...from }, to: { ...to }, ...mode, ...rot, embed, ...properties });
258
+ }
259
+ else if (ext !== undefined) {
260
+ anchors.push({ from: { ...from }, ext, ...rot, embed, ...properties });
261
+ }
262
+ else {
263
+ fullyModeled = false;
155
264
  }
156
265
  from = null;
157
266
  to = null;
@@ -159,15 +268,7 @@ export function parseDrawing(xml) {
159
268
  }
160
269
  },
161
270
  });
162
- return anchors;
163
- }
164
- const UNMODELED_DRAWING_CONTENT = new Set(['graphicFrame', 'sp', 'cxnSp', 'grpSp']);
165
- export function drawingHasUnmodeledContent(xml) {
166
- for (const { local } of openElements(xml)) {
167
- if (UNMODELED_DRAWING_CONTENT.has(local))
168
- return true;
169
- }
170
- return false;
271
+ return { anchors, fullyModeled };
171
272
  }
172
273
  const COORDINATES = new Set(['col', 'colOff', 'row', 'rowOff']);
173
274
  function setCoordinate(point, coord, value) {
@@ -54,6 +54,14 @@ export declare const DATABAR_LINK_EXT_URI = "{B025F937-C7B1-47D3-B67F-A62EFF666E
54
54
  export declare const DATA_VALIDATION_EXT_URI = "{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}";
55
55
  export declare const SLICER_LIST_EXT_URI = "{A8765BA9-456A-4dab-B4F3-ACF838C121DE}";
56
56
  export declare const SLICER_CACHES_EXT_URI = "{BBE1A952-AA13-448e-AADC-164F8A28A991}";
57
+ /** The extension a dynamic-array formula's cell-metadata block carries its properties in. */
58
+ export declare const DYNAMIC_ARRAY_PROPERTIES_EXT_URI = "{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}";
59
+ /** The 2017 dynamic-array namespace (`xda:`), which scopes those properties in `xl/metadata.xml`. */
60
+ export declare const DYNAMIC_ARRAY_NS = "http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray";
61
+ /** The extension a rich-value metadata block names its rich value in (`<xlrd:rvb i>`). */
62
+ export declare const RICH_VALUE_BLOCK_EXT_URI = "{3e2802c4-a4d2-4d8b-9148-e3be6c30e623}";
63
+ /** The 2017 rich-data namespace (`xlrd:`): the rich values, their structures, and the blocks naming them. */
64
+ export declare const RICH_DATA_NS = "http://schemas.microsoft.com/office/spreadsheetml/2017/richdata";
57
65
  /**
58
66
  * Whether an element belongs to the standard SpreadsheetML vocabulary rather than to an extension.
59
67
  *
@@ -11,6 +11,10 @@ export const DATABAR_LINK_EXT_URI = '{B025F937-C7B1-47D3-B67F-A62EFF666E3E}';
11
11
  export const DATA_VALIDATION_EXT_URI = '{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}';
12
12
  export const SLICER_LIST_EXT_URI = '{A8765BA9-456A-4dab-B4F3-ACF838C121DE}';
13
13
  export const SLICER_CACHES_EXT_URI = '{BBE1A952-AA13-448e-AADC-164F8A28A991}';
14
+ export const DYNAMIC_ARRAY_PROPERTIES_EXT_URI = '{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}';
15
+ export const DYNAMIC_ARRAY_NS = 'http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray';
16
+ export const RICH_VALUE_BLOCK_EXT_URI = '{3e2802c4-a4d2-4d8b-9148-e3be6c30e623}';
17
+ export const RICH_DATA_NS = 'http://schemas.microsoft.com/office/spreadsheetml/2017/richdata';
14
18
  export function isMainNamespaceElement(scope, name) {
15
19
  const namespace = scope.elementNamespace(name);
16
20
  return namespace === SPREADSHEETML_NS || (namespace === undefined && !name.includes(':'));
@@ -6,29 +6,59 @@ import type { Workbook } from '../../core/workbook.ts';
6
6
  import type { Worksheet } from '../../core/worksheet.ts';
7
7
  import type { CommentCell } from './comments.ts';
8
8
  import type { HyperlinkPlan } from './hyperlinks.ts';
9
- import type { DrawingImage } from './images.ts';
10
- export declare class RelIdAllocator {
9
+ import { type DrawingImage } from './images.ts';
10
+ /** One relationship a generated `.rels` part declares, its target already relative to the owner. */
11
+ export interface PlannedRelationship {
12
+ readonly id: string;
13
+ readonly type: string;
14
+ readonly target: string;
15
+ readonly external?: boolean;
16
+ }
17
+ /**
18
+ * The relationships one part owns, recorded as their ids are handed out.
19
+ *
20
+ * An id and the relationship it names are one fact, so they are written down in one call. When the
21
+ * ids came from a counter and the `.rels` part was listed somewhere else, a sheet's set was spelled
22
+ * three times: once where ids were drawn, once in the rels renderer in another order, and once more in
23
+ * a nine-way condition deciding whether the rels part existed at all. A tenth kind of part needed all
24
+ * three edits, and missing the last dropped the `.rels` part while the sheet XML still cited the id.
25
+ * Now the ledger is the rels part, and a part with an empty ledger has none.
26
+ *
27
+ * Ids run `rId1`, `rId2`, … in the order parts are added, so no id is derived by summing the ones
28
+ * before it, and the `.rels` part lists them in that same order. One ledger per owning part; ids are
29
+ * scoped to it. A ledger adding to a part kept from a file skips the ids that part already holds.
30
+ */
31
+ export declare class RelationshipLedger {
11
32
  #private;
12
- /** The next relationship id (`rId1`, `rId2`, …), advancing the counter. */
13
- next(): string;
33
+ /**
34
+ * @param owner the package path of the part whose `.rels` this is, `''` for the package root.
35
+ * @param taken the ids the part's `.rels` already holds, none of which a new relationship may take.
36
+ */
37
+ constructor(owner: string, taken?: ReadonlySet<string>);
38
+ /** Record a relationship to a package part, named by its package path, and return its id. */
39
+ add(type: string, partPath: string): string;
40
+ /** Record a relationship to a target outside the package (a hyperlink's URL) and return its id. */
41
+ addExternal(type: string, url: string): string;
42
+ /** Record a relationship to a place inside this document (a picture link's `#Sheet1!C3`), kept as
43
+ * written rather than resolved as a part path, and return its id. */
44
+ addInDocument(type: string, location: string): string;
45
+ /** Every relationship recorded, in id order. */
46
+ get relationships(): readonly PlannedRelationship[];
14
47
  }
15
48
  export interface PivotPlan {
16
49
  readonly number: number;
17
50
  readonly cacheId: string;
18
51
  readonly table: PivotTable;
19
- readonly sheetRelId: string;
20
52
  workbookRelId: string;
21
53
  }
22
54
  export interface CommentPlan {
23
55
  readonly number: number;
24
56
  readonly comments: readonly CommentCell[];
25
57
  readonly vmlRelId: string;
26
- readonly commentsRelId: string;
27
58
  }
28
59
  export interface ThreadedCommentPlan {
29
60
  readonly number: number;
30
61
  readonly threads: readonly CommentThread[];
31
- readonly relId: string;
32
62
  }
33
63
  export interface PrinterSettingsPlan {
34
64
  readonly number: number;
@@ -42,8 +72,6 @@ export interface TablePlan {
42
72
  }
43
73
  export interface BackgroundPlan {
44
74
  readonly relId: string;
45
- readonly mediaNumber: number;
46
- readonly extension: string;
47
75
  }
48
76
  export interface PreservedPartPlan {
49
77
  readonly path: string;
@@ -85,11 +113,8 @@ export interface PreservedPlan {
85
113
  export interface DrawingPlan {
86
114
  readonly number: number;
87
115
  readonly relId: string;
88
- readonly images: readonly ImagePlan[];
89
- }
90
- export interface ImagePlan extends DrawingImage {
91
- readonly mediaNumber: number;
92
- readonly extension: string;
116
+ readonly images: readonly DrawingImage[];
117
+ readonly relationships: readonly PlannedRelationship[];
93
118
  }
94
119
  export interface MediaPart {
95
120
  readonly number: number;
@@ -109,8 +134,16 @@ export interface MediaPlan {
109
134
  resolve(id: number): ResolvedMedia;
110
135
  }
111
136
  export declare function planMedia(workbook: Workbook, sheets: readonly Worksheet[]): MediaPlan;
112
- export declare function planPreservedParts(workbook: Workbook, generatedDrawingCount: number, generatedMediaCount: number, generatedPivotCount: number): PreservedPlan;
137
+ /**
138
+ * The pictures a sheet adds to the drawing it kept from a file, laid out once that drawing's path in the
139
+ * package is known: each picture's media and link relationships go into `ledger`, the drawing's own,
140
+ * which already skips the ids the drawing holds.
141
+ */
142
+ export type PicturesJoiningDrawing = (ledger: RelationshipLedger) => readonly DrawingImage[];
143
+ export declare function planPreservedParts(workbook: Workbook, generatedDrawingCount: number, generatedMediaCount: number, generatedPivotCount: number, pictures: ReadonlyMap<string, PicturesJoiningDrawing>): PreservedPlan;
113
144
  export interface SheetPlan {
145
+ /** Every relationship the sheet's `.rels` part declares, in id order; empty when it needs none. */
146
+ readonly relationships: readonly PlannedRelationship[];
114
147
  readonly tables: TablePlan[];
115
148
  readonly drawing: DrawingPlan | null;
116
149
  readonly comments: CommentPlan | null;
@@ -1,12 +1,39 @@
1
+ import { INTERNAL } from '../../core/internal.js';
1
2
  import { AuthoringError, InternalError, quoted } from '../../errors.js';
2
3
  import { extensionOf, relativePartPath, relsPathFor, THEME_PART_PATH } from '../opc/part-paths.js';
3
4
  import { relsPartXml } from '../opc/rels.js';
5
+ import { mergePictures } from './images.js';
4
6
  import { drawingPart, mediaPart, pivotCacheDefinitionPart, pivotCacheRecordsPart, pivotTablePart, vmlDrawingPart, } from './part-names.js';
7
+ import { splicePreservedPart } from './preserved-splices.js';
5
8
  import { applyThemeOverrides } from './theme-xml.js';
6
- export class RelIdAllocator {
9
+ export class RelationshipLedger {
10
+ #owner;
11
+ #taken;
12
+ #relationships = [];
7
13
  #next = 1;
8
- next() {
9
- return `rId${this.#next++}`;
14
+ constructor(owner, taken = new Set()) {
15
+ this.#owner = owner;
16
+ this.#taken = taken;
17
+ }
18
+ add(type, partPath) {
19
+ return this.#record({ type, target: relativePartPath(this.#owner, partPath) });
20
+ }
21
+ addExternal(type, url) {
22
+ return this.#record({ type, target: url, external: true });
23
+ }
24
+ addInDocument(type, location) {
25
+ return this.#record({ type, target: location });
26
+ }
27
+ get relationships() {
28
+ return this.#relationships;
29
+ }
30
+ #record(relationship) {
31
+ while (this.#taken.has(`rId${this.#next}`))
32
+ this.#next += 1;
33
+ const id = `rId${this.#next}`;
34
+ this.#next += 1;
35
+ this.#relationships.push({ id, ...relationship });
36
+ return id;
10
37
  }
11
38
  }
12
39
  export function planMedia(workbook, sheets) {
@@ -51,7 +78,7 @@ export function planMedia(workbook, sheets) {
51
78
  },
52
79
  };
53
80
  }
54
- export function planPreservedParts(workbook, generatedDrawingCount, generatedMediaCount, generatedPivotCount) {
81
+ export function planPreservedParts(workbook, generatedDrawingCount, generatedMediaCount, generatedPivotCount, pictures) {
55
82
  const sheets = workbook.worksheets;
56
83
  const numbering = {
57
84
  drawing: generatedDrawingCount,
@@ -78,6 +105,7 @@ export function planPreservedParts(workbook, generatedDrawingCount, generatedMed
78
105
  }
79
106
  }
80
107
  const emitted = new Map();
108
+ const splices = workbook[INTERNAL].splices();
81
109
  for (const reference of allReferences) {
82
110
  for (const part of reference.parts) {
83
111
  const newPath = resolveRemapped(remap, part.path);
@@ -93,14 +121,23 @@ export function planPreservedParts(workbook, generatedDrawingCount, generatedMed
93
121
  : [{ id: rel.id, type: rel.type, target: relativePartPath(newPath, target) }];
94
122
  });
95
123
  const overrides = newPath === THEME_PART_PATH ? workbook.themeOverrides : undefined;
124
+ let bytes = overrides === undefined
125
+ ? splicePreservedPart(part.bytes, part.contentType, splices)
126
+ : new TextEncoder().encode(applyThemeOverrides(new TextDecoder().decode(part.bytes), overrides));
127
+ let relationships = rels;
128
+ const joining = pictures.get(part.path);
129
+ if (joining !== undefined) {
130
+ const ledger = new RelationshipLedger(newPath, new Set(rels.map((rel) => rel.id)));
131
+ const images = joining(ledger);
132
+ bytes = new TextEncoder().encode(mergePictures(new TextDecoder().decode(bytes), images));
133
+ relationships = [...rels, ...ledger.relationships];
134
+ }
96
135
  emitted.set(newPath, {
97
136
  path: newPath,
98
- bytes: overrides === undefined
99
- ? part.bytes
100
- : new TextEncoder().encode(applyThemeOverrides(new TextDecoder().decode(part.bytes), overrides)),
137
+ bytes,
101
138
  contentType: part.contentType,
102
- relsPath: rels.length === 0 ? null : relsPathFor(newPath),
103
- relsXml: rels.length === 0 ? null : relsPartXml(rels),
139
+ relsPath: relationships.length === 0 ? null : relsPathFor(newPath),
140
+ relsXml: relationships.length === 0 ? null : relsPartXml(relationships),
104
141
  });
105
142
  }
106
143
  }
@@ -5,6 +5,12 @@ export declare const SHARED_STRINGS_PART = "xl/sharedStrings.xml";
5
5
  /** Singular and unnumbered, unlike the per-sheet thread parts: one identity registry serves the
6
6
  * whole workbook's threaded comments. */
7
7
  export declare const PERSONS_PART = "xl/persons/person.xml";
8
+ /** The metadata a `<c cm>` and `<c vm>` index: what marks a formula a dynamic array, and what names
9
+ * the error behind a `#VALUE!`. */
10
+ export declare const METADATA_PART = "xl/metadata.xml";
11
+ /** The rich values a value-metadata block names, and the structures they are read against. */
12
+ export declare const RICH_VALUES_PART = "xl/richData/rdrichvalue.xml";
13
+ export declare const RICH_VALUE_STRUCTURES_PART = "xl/richData/rdrichvaluestructure.xml";
8
14
  export declare const CORE_PROPS_PART = "docProps/core.xml";
9
15
  export declare const APP_PROPS_PART = "docProps/app.xml";
10
16
  /** `xl/worksheets/sheet{n}.xml`, numbered by the sheet's position in the workbook (1-based). */
@@ -31,11 +37,3 @@ export declare function pivotTablePart(number: number): string;
31
37
  export declare function pivotCacheDefinitionPart(number: number): string;
32
38
  /** `xl/pivotCache/pivotCacheRecords{n}.xml`: the cached rows the definition points at. */
33
39
  export declare function pivotCacheRecordsPart(number: number): string;
34
- /**
35
- * A part named as a Target of a relationship carried by a *worksheet*. Every worksheet part lives in
36
- * `xl/worksheets/`, so one sheet's view of a target is every sheet's, and the sheet number the
37
- * arithmetic runs against does not matter.
38
- */
39
- export declare function targetFromWorksheet(partPath: string): string;
40
- /** A part named as a Target of a relationship carried by the workbook part. */
41
- export declare function targetFromWorkbook(partPath: string): string;
@@ -1,8 +1,10 @@
1
- import { relativePartPath } from '../opc/part-paths.js';
2
1
  export const WORKBOOK_PART = 'xl/workbook.xml';
3
2
  export const STYLES_PART = 'xl/styles.xml';
4
3
  export const SHARED_STRINGS_PART = 'xl/sharedStrings.xml';
5
4
  export const PERSONS_PART = 'xl/persons/person.xml';
5
+ export const METADATA_PART = 'xl/metadata.xml';
6
+ export const RICH_VALUES_PART = 'xl/richData/rdrichvalue.xml';
7
+ export const RICH_VALUE_STRUCTURES_PART = 'xl/richData/rdrichvaluestructure.xml';
6
8
  export const CORE_PROPS_PART = 'docProps/core.xml';
7
9
  export const APP_PROPS_PART = 'docProps/app.xml';
8
10
  export function worksheetPart(number) {
@@ -38,9 +40,3 @@ export function pivotCacheDefinitionPart(number) {
38
40
  export function pivotCacheRecordsPart(number) {
39
41
  return `xl/pivotCache/pivotCacheRecords${number}.xml`;
40
42
  }
41
- export function targetFromWorksheet(partPath) {
42
- return relativePartPath(worksheetPart(1), partPath);
43
- }
44
- export function targetFromWorkbook(partPath) {
45
- return relativePartPath(WORKBOOK_PART, partPath);
46
- }
@@ -1,9 +1,9 @@
1
1
  import type { PivotTable } from '../../core/pivot-table.ts';
2
- /** The `pivotCacheDefinition` part: the source reference and the field catalogue. `r:id="rId1"`
3
- * names the cache-records part through this cache's own rels part. `refreshOnLoad="1"` tells Excel to
4
- * rebuild the pivot body from the cache on open, so the layout stays correct without us computing it
5
- * to the pixel. `recordCount` is the number of source data rows. */
6
- export declare function pivotCacheDefinitionXml(table: PivotTable): string;
2
+ /** The `pivotCacheDefinition` part: the source reference and the field catalogue. `recordsRelId` is
3
+ * the id this cache's own rels part gives the cache-records part, which `r:id` cites.
4
+ * `refreshOnLoad="1"` tells Excel to rebuild the pivot body from the cache on open, so the layout stays
5
+ * correct without us computing it to the pixel. `recordCount` is the number of source data rows. */
6
+ export declare function pivotCacheDefinitionXml(table: PivotTable, recordsRelId: string): string;
7
7
  /** The `pivotCacheRecords` part: one `<r>` per source data row, each cell either an index into an
8
8
  * axis field's shared-items catalogue (`<x>`) or an inline value (`<n>`/`<s>`/`<m>`). */
9
9
  export declare function pivotCacheRecordsXml(table: PivotTable): string;
@@ -1,5 +1,5 @@
1
1
  import { encodeAddress } from '../../core/address.js';
2
- import { boolAttr, escapeAttr, numberText, XML_DECLARATION } from '../../xml/xml.js';
2
+ import { escapeAttr, numberText, XML_DECLARATION } from '../../xml/xml.js';
3
3
  import { RELATIONSHIPS_NS } from '../opc/namespaces.js';
4
4
  import { SPREADSHEETML_NS } from './namespaces.js';
5
5
  const METRIC_CAPTIONS = {
@@ -15,30 +15,19 @@ const METRIC_CAPTIONS = {
15
15
  var: 'Var',
16
16
  varp: 'Varp',
17
17
  };
18
- export function pivotCacheDefinitionXml(table) {
18
+ export function pivotCacheDefinitionXml(table, recordsRelId) {
19
19
  const fields = table.cacheFields
20
20
  .map((field) => {
21
21
  const shared = field.sharedItems;
22
- if (shared !== null) {
23
- const items = shared.map(sharedItemXml).join('');
24
- const blank = field.containsBlank ? ' containsBlank="1"' : '';
25
- return (`<cacheField name="${escapeAttr(field.name)}" numFmtId="0">` +
26
- `<sharedItems${blank} count="${shared.length}">${items}</sharedItems>` +
27
- `</cacheField>`);
28
- }
29
- const blank = field.containsBlank ? ' containsBlank="1"' : '';
30
- const numeric = field.numeric;
31
- const descriptor = numeric === null
32
- ? `<sharedItems${blank}/>`
33
- : `<sharedItems containsSemiMixedTypes="0" containsString="0" containsNumber="1"` +
34
- boolAttr('containsInteger', numeric.allInteger) +
35
- blank +
36
- ` minValue="${numberText(numeric.min)}" maxValue="${numberText(numeric.max)}"/>`;
22
+ const kinds = sharedItemsKinds(field);
23
+ const descriptor = shared === null
24
+ ? `<sharedItems${kinds}/>`
25
+ : `<sharedItems${kinds} count="${shared.length}">${shared.map(sharedItemXml).join('')}</sharedItems>`;
37
26
  return `<cacheField name="${escapeAttr(field.name)}" numFmtId="0">${descriptor}</cacheField>`;
38
27
  })
39
28
  .join('');
40
29
  return (XML_DECLARATION +
41
- `<pivotCacheDefinition xmlns="${SPREADSHEETML_NS}" xmlns:r="${RELATIONSHIPS_NS}" r:id="rId1" refreshOnLoad="1" ` +
30
+ `<pivotCacheDefinition xmlns="${SPREADSHEETML_NS}" xmlns:r="${RELATIONSHIPS_NS}" r:id="${escapeAttr(recordsRelId)}" refreshOnLoad="1" ` +
42
31
  `refreshedBy="ts-xlsx" createdVersion="8" refreshedVersion="8" minRefreshableVersion="3" ` +
43
32
  `recordCount="${table.records.length}">` +
44
33
  `<cacheSource type="worksheet">` +
@@ -64,15 +53,14 @@ export function pivotTableXml(table, name, cacheId) {
64
53
  const location = `A1:${encodeAddress(2 + columnGroups, 3 + rowGroups)}`;
65
54
  const pivotFields = table.cacheFields
66
55
  .map((field, index) => {
56
+ const dataField = index === table.valueField ? ' dataField="1"' : '';
67
57
  if (table.rowFields.includes(index) || table.columnFields.includes(index)) {
68
58
  const axis = table.rowFields.includes(index) ? 'axisRow' : 'axisCol';
69
59
  const items = field.sharedItems ?? [];
70
60
  const entries = `${items.map((_item, i) => `<item x="${i}"/>`).join('')}<item t="default"/>`;
71
- return `<pivotField axis="${axis}" showAll="0"><items count="${items.length + 1}">${entries}</items></pivotField>`;
61
+ return `<pivotField axis="${axis}"${dataField} showAll="0"><items count="${items.length + 1}">${entries}</items></pivotField>`;
72
62
  }
73
- if (index === table.valueField)
74
- return '<pivotField dataField="1" showAll="0"/>';
75
- return '<pivotField showAll="0"/>';
63
+ return `<pivotField${dataField} showAll="0"/>`;
76
64
  })
77
65
  .join('');
78
66
  const rowFields = table.rowFields.map((index) => `<field x="${index}"/>`).join('');
@@ -103,6 +91,19 @@ function dataFieldXml(table) {
103
91
  return (`<dataField name="${escapeAttr(caption)}" fld="${table.valueField}"${subtotal} ` +
104
92
  `baseField="0" baseItem="0"/>`);
105
93
  }
94
+ function sharedItemsKinds(field) {
95
+ const { containsString, containsBlank, numeric } = field;
96
+ const hasNumber = numeric !== null;
97
+ return ((!containsString && !containsBlank && hasNumber ? ' containsSemiMixedTypes="0"' : '') +
98
+ (containsString ? '' : ' containsString="0"') +
99
+ (containsBlank ? ' containsBlank="1"' : '') +
100
+ (containsString && hasNumber ? ' containsMixedTypes="1"' : '') +
101
+ (numeric === null
102
+ ? ''
103
+ : ' containsNumber="1"' +
104
+ (numeric.allInteger ? ' containsInteger="1"' : '') +
105
+ ` minValue="${numberText(numeric.min)}" maxValue="${numberText(numeric.max)}"`));
106
+ }
106
107
  function sharedItemXml(item) {
107
108
  switch (item.kind) {
108
109
  case 'string':