@cj-tech-master/excelts 4.2.2 → 4.2.3-canary.20260122073152.a9bb6b0
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.
- package/dist/browser/modules/csv/csv-core.d.ts +0 -9
- package/dist/browser/modules/csv/csv.browser.js +3 -3
- package/dist/browser/modules/excel/utils/parse-sax.d.ts +3 -0
- package/dist/browser/modules/excel/utils/parse-sax.js +32 -13
- package/dist/browser/modules/excel/utils/passthrough-manager.d.ts +77 -0
- package/dist/browser/modules/excel/utils/passthrough-manager.js +129 -0
- package/dist/browser/modules/excel/workbook.d.ts +8 -0
- package/dist/browser/modules/excel/workbook.js +9 -1
- package/dist/browser/modules/excel/worksheet.d.ts +4 -0
- package/dist/browser/modules/excel/worksheet.js +4 -1
- package/dist/browser/modules/excel/xlsx/xform/core/app-xform.js +3 -3
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +16 -10
- package/dist/browser/modules/excel/xlsx/xform/core/core-xform.js +56 -68
- package/dist/browser/modules/excel/xlsx/xform/list-xform.js +8 -10
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +34 -11
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +256 -86
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +38 -11
- package/dist/browser/modules/excel/xlsx/xform/strings/shared-string-xform.js +2 -3
- package/dist/browser/modules/excel/xlsx/xform/strings/text-xform.js +5 -7
- package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +36 -1
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +213 -127
- package/dist/browser/modules/stream/streams.browser.js +0 -3
- package/dist/cjs/modules/csv/csv.browser.js +3 -3
- package/dist/cjs/modules/excel/utils/parse-sax.js +32 -13
- package/dist/cjs/modules/excel/utils/passthrough-manager.js +133 -0
- package/dist/cjs/modules/excel/workbook.js +9 -1
- package/dist/cjs/modules/excel/worksheet.js +4 -1
- package/dist/cjs/modules/excel/xlsx/xform/core/app-xform.js +3 -3
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +16 -10
- package/dist/cjs/modules/excel/xlsx/xform/core/core-xform.js +56 -68
- package/dist/cjs/modules/excel/xlsx/xform/list-xform.js +8 -10
- package/dist/cjs/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +256 -86
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +38 -11
- package/dist/cjs/modules/excel/xlsx/xform/strings/shared-string-xform.js +2 -3
- package/dist/cjs/modules/excel/xlsx/xform/strings/text-xform.js +5 -7
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +213 -127
- package/dist/cjs/modules/stream/streams.browser.js +0 -3
- package/dist/esm/modules/csv/csv.browser.js +3 -3
- package/dist/esm/modules/excel/utils/parse-sax.js +32 -13
- package/dist/esm/modules/excel/utils/passthrough-manager.js +129 -0
- package/dist/esm/modules/excel/workbook.js +9 -1
- package/dist/esm/modules/excel/worksheet.js +4 -1
- package/dist/esm/modules/excel/xlsx/xform/core/app-xform.js +3 -3
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +16 -10
- package/dist/esm/modules/excel/xlsx/xform/core/core-xform.js +56 -68
- package/dist/esm/modules/excel/xlsx/xform/list-xform.js +8 -10
- package/dist/esm/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +256 -86
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +38 -11
- package/dist/esm/modules/excel/xlsx/xform/strings/shared-string-xform.js +2 -3
- package/dist/esm/modules/excel/xlsx/xform/strings/text-xform.js +5 -7
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +213 -127
- package/dist/esm/modules/stream/streams.browser.js +0 -3
- package/dist/iife/excelts.iife.js +603 -333
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +25 -52
- package/dist/types/modules/csv/csv-core.d.ts +0 -9
- package/dist/types/modules/excel/utils/parse-sax.d.ts +3 -0
- package/dist/types/modules/excel/utils/passthrough-manager.d.ts +77 -0
- package/dist/types/modules/excel/workbook.d.ts +8 -0
- package/dist/types/modules/excel/worksheet.d.ts +4 -0
- package/dist/types/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +34 -11
- package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +36 -1
- package/package.json +2 -2
|
@@ -7,18 +7,30 @@ const base_xform_1 = require("../base-xform.js");
|
|
|
7
7
|
class PivotTableXform extends base_xform_1.BaseXform {
|
|
8
8
|
constructor() {
|
|
9
9
|
super();
|
|
10
|
+
// 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
|
+
};
|
|
24
|
+
// Current parsing context
|
|
25
|
+
this.currentPivotField = null;
|
|
26
|
+
this.currentRowItem = null;
|
|
27
|
+
this.currentColItem = null;
|
|
28
|
+
this.currentChartFormat = null;
|
|
29
|
+
// Buffer for collecting pivotArea XML
|
|
30
|
+
this.pivotAreaXmlBuffer = [];
|
|
31
|
+
this.pivotAreaDepth = 0;
|
|
10
32
|
this.map = {};
|
|
11
33
|
this.model = null;
|
|
12
|
-
this.inPivotFields = false;
|
|
13
|
-
this.inRowFields = false;
|
|
14
|
-
this.inColFields = false;
|
|
15
|
-
this.inDataFields = false;
|
|
16
|
-
this.inRowItems = false;
|
|
17
|
-
this.inColItems = false;
|
|
18
|
-
this.inLocation = false;
|
|
19
|
-
this.currentPivotField = null;
|
|
20
|
-
this.inItems = false;
|
|
21
|
-
this.inPivotTableStyleInfo = false;
|
|
22
34
|
}
|
|
23
35
|
prepare(_model) {
|
|
24
36
|
// No preparation needed
|
|
@@ -29,16 +41,17 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
29
41
|
}
|
|
30
42
|
reset() {
|
|
31
43
|
this.model = null;
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
this.inColItems = false;
|
|
38
|
-
this.inLocation = false;
|
|
44
|
+
// Reset all parser state flags using object
|
|
45
|
+
Object.keys(this.state).forEach(key => {
|
|
46
|
+
this.state[key] = false;
|
|
47
|
+
});
|
|
48
|
+
// Reset current context
|
|
39
49
|
this.currentPivotField = null;
|
|
40
|
-
this.
|
|
41
|
-
this.
|
|
50
|
+
this.currentRowItem = null;
|
|
51
|
+
this.currentColItem = null;
|
|
52
|
+
this.currentChartFormat = null;
|
|
53
|
+
this.pivotAreaXmlBuffer = [];
|
|
54
|
+
this.pivotAreaDepth = 0;
|
|
42
55
|
}
|
|
43
56
|
/**
|
|
44
57
|
* Render pivot table XML.
|
|
@@ -159,8 +172,7 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
159
172
|
* Render loaded pivot table (preserving original structure)
|
|
160
173
|
*/
|
|
161
174
|
renderLoaded(xmlStream, model) {
|
|
162
|
-
|
|
163
|
-
xmlStream.openNode(this.tag, {
|
|
175
|
+
const attrs = {
|
|
164
176
|
...PivotTableXform.PIVOT_TABLE_ATTRIBUTES,
|
|
165
177
|
name: model.name || "PivotTable1",
|
|
166
178
|
cacheId: model.cacheId,
|
|
@@ -169,7 +181,8 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
169
181
|
applyFontFormats: model.applyFontFormats || "0",
|
|
170
182
|
applyPatternFormats: model.applyPatternFormats || "0",
|
|
171
183
|
applyAlignmentFormats: model.applyAlignmentFormats || "0",
|
|
172
|
-
|
|
184
|
+
// Preserve original value when present; default to Excel's typical "0".
|
|
185
|
+
applyWidthHeightFormats: model.applyWidthHeightFormats ?? "0",
|
|
173
186
|
dataCaption: model.dataCaption || "Values",
|
|
174
187
|
updatedVersion: model.updatedVersion || "8",
|
|
175
188
|
minRefreshableVersion: model.minRefreshableVersion || "3",
|
|
@@ -177,10 +190,27 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
177
190
|
itemPrintTitles: model.itemPrintTitles ? "1" : "0",
|
|
178
191
|
createdVersion: model.createdVersion || "8",
|
|
179
192
|
indent: model.indent !== undefined ? String(model.indent) : "0",
|
|
180
|
-
compact: model.compact ? "1" : "0",
|
|
181
|
-
compactData: model.compactData ? "1" : "0",
|
|
182
193
|
multipleFieldFilters: model.multipleFieldFilters ? "1" : "0"
|
|
183
|
-
}
|
|
194
|
+
};
|
|
195
|
+
// Add outline attributes if present
|
|
196
|
+
if (model.outline) {
|
|
197
|
+
attrs.outline = "1";
|
|
198
|
+
}
|
|
199
|
+
if (model.outlineData) {
|
|
200
|
+
attrs.outlineData = "1";
|
|
201
|
+
}
|
|
202
|
+
if (model.chartFormat !== undefined) {
|
|
203
|
+
attrs.chartFormat = String(model.chartFormat);
|
|
204
|
+
}
|
|
205
|
+
// Only add compact/compactData if they are true (some files don't have them)
|
|
206
|
+
if (model.compact) {
|
|
207
|
+
attrs.compact = "1";
|
|
208
|
+
}
|
|
209
|
+
if (model.compactData) {
|
|
210
|
+
attrs.compactData = "1";
|
|
211
|
+
}
|
|
212
|
+
xmlStream.openXml(xml_stream_1.XmlStream.StdDocAttributes);
|
|
213
|
+
xmlStream.openNode(this.tag, attrs);
|
|
184
214
|
// Location
|
|
185
215
|
if (model.location) {
|
|
186
216
|
xmlStream.leafNode("location", {
|
|
@@ -206,12 +236,19 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
206
236
|
}
|
|
207
237
|
xmlStream.closeNode();
|
|
208
238
|
}
|
|
209
|
-
// Row items
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
239
|
+
// Row items - use parsed items if available; otherwise emit a minimal grand total
|
|
240
|
+
if (model.rowItems && model.rowItems.length > 0) {
|
|
241
|
+
xmlStream.openNode("rowItems", { count: model.rowItems.length });
|
|
242
|
+
for (const item of model.rowItems) {
|
|
243
|
+
this.renderRowColItem(xmlStream, item);
|
|
244
|
+
}
|
|
245
|
+
xmlStream.closeNode();
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
xmlStream.writeXml('<rowItems count="1"><i t="grand"><x/></i></rowItems>');
|
|
249
|
+
}
|
|
214
250
|
// Col fields
|
|
251
|
+
// Excel commonly emits a synthetic field x=-2 when there are no column fields.
|
|
215
252
|
const colFieldCount = model.colFields.length === 0 ? 1 : model.colFields.length;
|
|
216
253
|
xmlStream.openNode("colFields", { count: colFieldCount });
|
|
217
254
|
if (model.colFields.length === 0) {
|
|
@@ -223,25 +260,52 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
223
260
|
}
|
|
224
261
|
}
|
|
225
262
|
xmlStream.closeNode();
|
|
226
|
-
// Col items
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
263
|
+
// Col items - use parsed items if available
|
|
264
|
+
if (model.colItems && model.colItems.length > 0) {
|
|
265
|
+
xmlStream.openNode("colItems", { count: model.colItems.length });
|
|
266
|
+
for (const item of model.colItems) {
|
|
267
|
+
this.renderRowColItem(xmlStream, item);
|
|
268
|
+
}
|
|
269
|
+
xmlStream.closeNode();
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
xmlStream.writeXml('<colItems count="1"><i t="grand"><x/></i></colItems>');
|
|
273
|
+
}
|
|
231
274
|
// Data fields
|
|
232
275
|
if (model.dataFields.length > 0) {
|
|
233
276
|
xmlStream.openNode("dataFields", { count: model.dataFields.length });
|
|
234
277
|
for (const dataField of model.dataFields) {
|
|
235
|
-
const
|
|
278
|
+
const dfAttrs = {
|
|
236
279
|
name: dataField.name,
|
|
237
280
|
fld: dataField.fld,
|
|
238
281
|
baseField: dataField.baseField ?? 0,
|
|
239
282
|
baseItem: dataField.baseItem ?? 0
|
|
240
283
|
};
|
|
241
284
|
if (dataField.subtotal && dataField.subtotal !== "sum") {
|
|
242
|
-
|
|
285
|
+
dfAttrs.subtotal = dataField.subtotal;
|
|
286
|
+
}
|
|
287
|
+
xmlStream.leafNode("dataField", dfAttrs);
|
|
288
|
+
}
|
|
289
|
+
xmlStream.closeNode();
|
|
290
|
+
}
|
|
291
|
+
// Chart formats (for pivot charts) - preserve original pivotArea XML
|
|
292
|
+
if (model.chartFormats && model.chartFormats.length > 0) {
|
|
293
|
+
xmlStream.openNode("chartFormats", { count: model.chartFormats.length });
|
|
294
|
+
for (const cf of model.chartFormats) {
|
|
295
|
+
xmlStream.openNode("chartFormat", {
|
|
296
|
+
chart: cf.chart,
|
|
297
|
+
format: cf.format,
|
|
298
|
+
series: cf.series ? "1" : undefined
|
|
299
|
+
});
|
|
300
|
+
// Use preserved pivotArea XML or fallback to default
|
|
301
|
+
if (cf.pivotAreaXml) {
|
|
302
|
+
xmlStream.writeXml(cf.pivotAreaXml);
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
// Fallback for newly created chart formats (shouldn't happen for loaded models)
|
|
306
|
+
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>`);
|
|
243
307
|
}
|
|
244
|
-
xmlStream.
|
|
308
|
+
xmlStream.closeNode();
|
|
245
309
|
}
|
|
246
310
|
xmlStream.closeNode();
|
|
247
311
|
}
|
|
@@ -255,46 +319,48 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
255
319
|
showLastColumn: "1"
|
|
256
320
|
});
|
|
257
321
|
// Extensions
|
|
258
|
-
xmlStream.writeXml(
|
|
259
|
-
<extLst>
|
|
260
|
-
<ext
|
|
261
|
-
uri="{962EF5D1-5CA2-4c93-8EF4-DBF5C05439D2}"
|
|
262
|
-
xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
|
|
263
|
-
>
|
|
264
|
-
<x14:pivotTableDefinition
|
|
265
|
-
hideValuesRow="1"
|
|
266
|
-
xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main"
|
|
267
|
-
/>
|
|
268
|
-
</ext>
|
|
269
|
-
<ext
|
|
270
|
-
uri="{747A6164-185A-40DC-8AA5-F01512510D54}"
|
|
271
|
-
xmlns:xpdl="http://schemas.microsoft.com/office/spreadsheetml/2016/pivotdefaultlayout"
|
|
272
|
-
>
|
|
273
|
-
<xpdl:pivotTableDefinition16
|
|
274
|
-
EnabledSubtotalsDefault="0"
|
|
275
|
-
SubtotalsOnTopDefault="0"
|
|
276
|
-
/>
|
|
277
|
-
</ext>
|
|
278
|
-
</extLst>
|
|
279
|
-
`);
|
|
322
|
+
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>`);
|
|
280
323
|
xmlStream.closeNode();
|
|
281
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* Render a row or column item element
|
|
327
|
+
*/
|
|
328
|
+
renderRowColItem(xmlStream, item) {
|
|
329
|
+
const attrs = {};
|
|
330
|
+
if (item.t) {
|
|
331
|
+
attrs.t = item.t;
|
|
332
|
+
}
|
|
333
|
+
if (item.x && item.x.length > 0) {
|
|
334
|
+
xmlStream.openNode("i", attrs);
|
|
335
|
+
for (const x of item.x) {
|
|
336
|
+
if (x.v && x.v !== 0) {
|
|
337
|
+
xmlStream.leafNode("x", { v: x.v });
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
xmlStream.leafNode("x");
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
xmlStream.closeNode();
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
// Empty item (like <i/> in colItems)
|
|
347
|
+
xmlStream.leafNode("i", attrs);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
282
350
|
/**
|
|
283
351
|
* Render a loaded pivot field
|
|
284
352
|
*/
|
|
285
353
|
renderPivotFieldLoaded(xmlStream, field) {
|
|
286
|
-
const attrs = {
|
|
287
|
-
|
|
288
|
-
outline: field.outline ? "1" : "0",
|
|
289
|
-
showAll: field.showAll ? "1" : "0",
|
|
290
|
-
defaultSubtotal: field.defaultSubtotal ? "1" : "0"
|
|
291
|
-
};
|
|
354
|
+
const attrs = {};
|
|
355
|
+
// Only add attributes that were present in the original
|
|
292
356
|
if (field.axis) {
|
|
293
357
|
attrs.axis = field.axis;
|
|
294
358
|
}
|
|
295
359
|
if (field.dataField) {
|
|
296
360
|
attrs.dataField = "1";
|
|
297
361
|
}
|
|
362
|
+
// showAll is typically always present
|
|
363
|
+
attrs.showAll = field.showAll ? "1" : "0";
|
|
298
364
|
if (field.items && field.items.length > 0) {
|
|
299
365
|
xmlStream.openNode("pivotField", attrs);
|
|
300
366
|
xmlStream.openNode("items", { count: field.items.length + 1 });
|
|
@@ -302,7 +368,7 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
302
368
|
xmlStream.leafNode("item", { x: itemIndex });
|
|
303
369
|
}
|
|
304
370
|
// Grand total item
|
|
305
|
-
xmlStream.writeXml('<item t="default"
|
|
371
|
+
xmlStream.writeXml('<item t="default"/>');
|
|
306
372
|
xmlStream.closeNode(); // items
|
|
307
373
|
xmlStream.closeNode(); // pivotField
|
|
308
374
|
}
|
|
@@ -340,6 +406,12 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
340
406
|
compact: attributes.compact === "1",
|
|
341
407
|
compactData: attributes.compactData === "1",
|
|
342
408
|
multipleFieldFilters: attributes.multipleFieldFilters === "1",
|
|
409
|
+
outline: attributes.outline === "1",
|
|
410
|
+
outlineData: attributes.outlineData === "1",
|
|
411
|
+
chartFormat: attributes.chartFormat ? parseInt(attributes.chartFormat, 10) : undefined,
|
|
412
|
+
rowItems: [],
|
|
413
|
+
colItems: [],
|
|
414
|
+
chartFormats: [],
|
|
343
415
|
isLoaded: true
|
|
344
416
|
};
|
|
345
417
|
break;
|
|
@@ -360,10 +432,10 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
360
432
|
}
|
|
361
433
|
break;
|
|
362
434
|
case "pivotFields":
|
|
363
|
-
this.inPivotFields = true;
|
|
435
|
+
this.state.inPivotFields = true;
|
|
364
436
|
break;
|
|
365
437
|
case "pivotField":
|
|
366
|
-
if (this.inPivotFields) {
|
|
438
|
+
if (this.state.inPivotFields) {
|
|
367
439
|
this.currentPivotField = {
|
|
368
440
|
axis: attributes.axis,
|
|
369
441
|
dataField: attributes.dataField === "1",
|
|
@@ -377,43 +449,101 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
377
449
|
break;
|
|
378
450
|
case "items":
|
|
379
451
|
if (this.currentPivotField) {
|
|
380
|
-
this.inItems = true;
|
|
452
|
+
this.state.inItems = true;
|
|
381
453
|
}
|
|
382
454
|
break;
|
|
383
455
|
case "item":
|
|
384
|
-
if (this.inItems && this.currentPivotField && attributes.x !== undefined) {
|
|
456
|
+
if (this.state.inItems && this.currentPivotField && attributes.x !== undefined) {
|
|
385
457
|
this.currentPivotField.items.push(parseInt(attributes.x, 10));
|
|
386
458
|
}
|
|
387
459
|
break;
|
|
388
460
|
case "rowFields":
|
|
389
|
-
this.inRowFields = true;
|
|
461
|
+
this.state.inRowFields = true;
|
|
390
462
|
break;
|
|
391
463
|
case "colFields":
|
|
392
|
-
this.inColFields = true;
|
|
464
|
+
this.state.inColFields = true;
|
|
393
465
|
break;
|
|
394
466
|
case "dataFields":
|
|
395
|
-
this.inDataFields = true;
|
|
467
|
+
this.state.inDataFields = true;
|
|
396
468
|
break;
|
|
397
469
|
case "rowItems":
|
|
398
|
-
this.inRowItems = true;
|
|
470
|
+
this.state.inRowItems = true;
|
|
399
471
|
break;
|
|
400
472
|
case "colItems":
|
|
401
|
-
this.inColItems = true;
|
|
473
|
+
this.state.inColItems = true;
|
|
474
|
+
break;
|
|
475
|
+
case "i":
|
|
476
|
+
// Handle row/col item element
|
|
477
|
+
if (this.state.inRowItems && this.model) {
|
|
478
|
+
this.currentRowItem = { t: attributes.t, x: [] };
|
|
479
|
+
}
|
|
480
|
+
else if (this.state.inColItems && this.model) {
|
|
481
|
+
this.currentColItem = { t: attributes.t, x: [] };
|
|
482
|
+
}
|
|
483
|
+
break;
|
|
484
|
+
case "x":
|
|
485
|
+
// Handle x element inside row/col items or pivotArea
|
|
486
|
+
if (this.state.inPivotArea) {
|
|
487
|
+
// Collect x element for pivotArea XML
|
|
488
|
+
const xAttrs = Object.entries(attributes)
|
|
489
|
+
.map(([k, v]) => `${k}="${v}"`)
|
|
490
|
+
.join(" ");
|
|
491
|
+
this.pivotAreaXmlBuffer.push(xAttrs ? `<x ${xAttrs}/>` : "<x/>");
|
|
492
|
+
}
|
|
493
|
+
else if (this.currentRowItem) {
|
|
494
|
+
this.currentRowItem.x.push({ v: attributes.v ? parseInt(attributes.v, 10) : 0 });
|
|
495
|
+
}
|
|
496
|
+
else if (this.currentColItem) {
|
|
497
|
+
this.currentColItem.x.push({ v: attributes.v ? parseInt(attributes.v, 10) : 0 });
|
|
498
|
+
}
|
|
499
|
+
break;
|
|
500
|
+
case "chartFormats":
|
|
501
|
+
this.state.inChartFormats = true;
|
|
502
|
+
break;
|
|
503
|
+
case "chartFormat":
|
|
504
|
+
if (this.state.inChartFormats && this.model) {
|
|
505
|
+
this.currentChartFormat = {
|
|
506
|
+
chart: attributes.chart ? parseInt(attributes.chart, 10) : 0,
|
|
507
|
+
format: attributes.format ? parseInt(attributes.format, 10) : 0,
|
|
508
|
+
series: attributes.series === "1"
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
break;
|
|
512
|
+
case "pivotArea":
|
|
513
|
+
// Start collecting pivotArea XML for chartFormat
|
|
514
|
+
if (this.currentChartFormat) {
|
|
515
|
+
this.state.inPivotArea = true;
|
|
516
|
+
const attrsStr = Object.entries(attributes)
|
|
517
|
+
.map(([k, v]) => `${k}="${v}"`)
|
|
518
|
+
.join(" ");
|
|
519
|
+
this.pivotAreaXmlBuffer = [attrsStr ? `<pivotArea ${attrsStr}>` : "<pivotArea>"];
|
|
520
|
+
}
|
|
521
|
+
break;
|
|
522
|
+
case "references":
|
|
523
|
+
case "reference":
|
|
524
|
+
// Collect nested elements in pivotArea
|
|
525
|
+
if (this.state.inPivotArea) {
|
|
526
|
+
this.pivotAreaDepth++;
|
|
527
|
+
const attrsStr = Object.entries(attributes)
|
|
528
|
+
.map(([k, v]) => `${k}="${v}"`)
|
|
529
|
+
.join(" ");
|
|
530
|
+
this.pivotAreaXmlBuffer.push(`<${name}${attrsStr ? " " + attrsStr : ""}>`);
|
|
531
|
+
}
|
|
402
532
|
break;
|
|
403
533
|
case "field":
|
|
404
534
|
// Handle field element (used in rowFields, colFields)
|
|
405
535
|
if (this.model) {
|
|
406
536
|
const fieldIndex = parseInt(attributes.x || "0", 10);
|
|
407
|
-
if (this.inRowFields) {
|
|
537
|
+
if (this.state.inRowFields) {
|
|
408
538
|
this.model.rowFields.push(fieldIndex);
|
|
409
539
|
}
|
|
410
|
-
else if (this.inColFields) {
|
|
540
|
+
else if (this.state.inColFields) {
|
|
411
541
|
this.model.colFields.push(fieldIndex);
|
|
412
542
|
}
|
|
413
543
|
}
|
|
414
544
|
break;
|
|
415
545
|
case "dataField":
|
|
416
|
-
if (this.inDataFields && this.model) {
|
|
546
|
+
if (this.state.inDataFields && this.model) {
|
|
417
547
|
this.model.dataFields.push({
|
|
418
548
|
name: (0, utils_1.xmlDecode)(attributes.name || ""),
|
|
419
549
|
fld: parseInt(attributes.fld || "0", 10),
|
|
@@ -435,12 +565,32 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
435
565
|
// No text content in pivot table elements
|
|
436
566
|
}
|
|
437
567
|
parseClose(name) {
|
|
568
|
+
// Handle pivotArea nested elements - close tags
|
|
569
|
+
if (this.state.inPivotArea) {
|
|
570
|
+
if (name === "pivotArea") {
|
|
571
|
+
this.pivotAreaXmlBuffer.push("</pivotArea>");
|
|
572
|
+
if (this.currentChartFormat) {
|
|
573
|
+
this.currentChartFormat.pivotAreaXml = this.pivotAreaXmlBuffer.join("");
|
|
574
|
+
}
|
|
575
|
+
this.state.inPivotArea = false;
|
|
576
|
+
this.pivotAreaXmlBuffer = [];
|
|
577
|
+
this.pivotAreaDepth = 0;
|
|
578
|
+
return true;
|
|
579
|
+
}
|
|
580
|
+
else if (name === "references" || name === "reference") {
|
|
581
|
+
this.pivotAreaXmlBuffer.push(`</${name}>`);
|
|
582
|
+
this.pivotAreaDepth--;
|
|
583
|
+
return true;
|
|
584
|
+
}
|
|
585
|
+
// x elements are self-closing, no need to handle close
|
|
586
|
+
return true;
|
|
587
|
+
}
|
|
438
588
|
switch (name) {
|
|
439
589
|
case this.tag:
|
|
440
590
|
// End of pivotTableDefinition
|
|
441
591
|
return false;
|
|
442
592
|
case "pivotFields":
|
|
443
|
-
this.inPivotFields = false;
|
|
593
|
+
this.state.inPivotFields = false;
|
|
444
594
|
break;
|
|
445
595
|
case "pivotField":
|
|
446
596
|
if (this.currentPivotField && this.model) {
|
|
@@ -449,22 +599,42 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
449
599
|
}
|
|
450
600
|
break;
|
|
451
601
|
case "items":
|
|
452
|
-
this.inItems = false;
|
|
602
|
+
this.state.inItems = false;
|
|
453
603
|
break;
|
|
454
604
|
case "rowFields":
|
|
455
|
-
this.inRowFields = false;
|
|
605
|
+
this.state.inRowFields = false;
|
|
456
606
|
break;
|
|
457
607
|
case "colFields":
|
|
458
|
-
this.inColFields = false;
|
|
608
|
+
this.state.inColFields = false;
|
|
459
609
|
break;
|
|
460
610
|
case "dataFields":
|
|
461
|
-
this.inDataFields = false;
|
|
611
|
+
this.state.inDataFields = false;
|
|
462
612
|
break;
|
|
463
613
|
case "rowItems":
|
|
464
|
-
this.inRowItems = false;
|
|
614
|
+
this.state.inRowItems = false;
|
|
465
615
|
break;
|
|
466
616
|
case "colItems":
|
|
467
|
-
this.inColItems = false;
|
|
617
|
+
this.state.inColItems = false;
|
|
618
|
+
break;
|
|
619
|
+
case "i":
|
|
620
|
+
// Finish row/col item
|
|
621
|
+
if (this.currentRowItem && this.model) {
|
|
622
|
+
this.model.rowItems.push(this.currentRowItem);
|
|
623
|
+
this.currentRowItem = null;
|
|
624
|
+
}
|
|
625
|
+
else if (this.currentColItem && this.model) {
|
|
626
|
+
this.model.colItems.push(this.currentColItem);
|
|
627
|
+
this.currentColItem = null;
|
|
628
|
+
}
|
|
629
|
+
break;
|
|
630
|
+
case "chartFormats":
|
|
631
|
+
this.state.inChartFormats = false;
|
|
632
|
+
break;
|
|
633
|
+
case "chartFormat":
|
|
634
|
+
if (this.currentChartFormat && this.model) {
|
|
635
|
+
this.model.chartFormats.push(this.currentChartFormat);
|
|
636
|
+
this.currentChartFormat = null;
|
|
637
|
+
}
|
|
468
638
|
break;
|
|
469
639
|
}
|
|
470
640
|
return true;
|
|
@@ -191,6 +191,23 @@ class WorkSheetXform extends base_xform_1.BaseXform {
|
|
|
191
191
|
vmlDrawing: `vmlDrawing${model.id}`
|
|
192
192
|
});
|
|
193
193
|
}
|
|
194
|
+
// Handle pre-loaded drawing (from file read) that may contain charts or other non-image content
|
|
195
|
+
// This preserves drawings through round-trip even if they don't contain images
|
|
196
|
+
// Note: Always assign a new rId since rels are rebuilt during write
|
|
197
|
+
if (model.drawing && model.drawing.anchors) {
|
|
198
|
+
// This is a loaded drawing that needs to be added to relationships
|
|
199
|
+
const drawing = model.drawing;
|
|
200
|
+
drawing.rId = nextRid(rels);
|
|
201
|
+
if (!drawing.name) {
|
|
202
|
+
drawing.name = `drawing${++options.drawingsCount}`;
|
|
203
|
+
}
|
|
204
|
+
options.drawings.push(drawing);
|
|
205
|
+
rels.push({
|
|
206
|
+
Id: drawing.rId,
|
|
207
|
+
Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
|
|
208
|
+
Target: (0, ooxml_paths_1.drawingRelTargetFromWorksheet)(drawing.name)
|
|
209
|
+
});
|
|
210
|
+
}
|
|
194
211
|
const drawingRelsHash = [];
|
|
195
212
|
let bookImage;
|
|
196
213
|
model.media.forEach(medium => {
|
|
@@ -602,17 +619,27 @@ class WorkSheetXform extends base_xform_1.BaseXform {
|
|
|
602
619
|
if (match) {
|
|
603
620
|
const drawingName = match[1];
|
|
604
621
|
const drawing = options.drawings[drawingName];
|
|
605
|
-
drawing
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
622
|
+
if (drawing) {
|
|
623
|
+
// Preserve the drawing object for round-trip (charts, etc.)
|
|
624
|
+
// This includes the name, anchors, and rels
|
|
625
|
+
model.drawing = {
|
|
626
|
+
...drawing,
|
|
627
|
+
name: drawingName,
|
|
628
|
+
rels: options.drawingRels?.[drawingName] || drawing.rels || []
|
|
629
|
+
};
|
|
630
|
+
// Also extract images to model.media for backward compatibility
|
|
631
|
+
drawing.anchors.forEach(anchor => {
|
|
632
|
+
if (anchor.medium) {
|
|
633
|
+
const image = {
|
|
634
|
+
type: "image",
|
|
635
|
+
imageId: anchor.medium.index,
|
|
636
|
+
range: anchor.range,
|
|
637
|
+
hyperlinks: anchor.picture.hyperlinks
|
|
638
|
+
};
|
|
639
|
+
model.media.push(image);
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
}
|
|
616
643
|
}
|
|
617
644
|
}
|
|
618
645
|
const backgroundRel = model.background && rels[model.background.rId];
|
|
@@ -39,16 +39,15 @@ class SharedStringXform extends base_xform_1.BaseXform {
|
|
|
39
39
|
xmlStream.closeNode();
|
|
40
40
|
}
|
|
41
41
|
parseOpen(node) {
|
|
42
|
-
const { name } = node;
|
|
43
42
|
if (this.parser) {
|
|
44
43
|
this.parser.parseOpen(node);
|
|
45
44
|
return true;
|
|
46
45
|
}
|
|
47
|
-
if (name === this.tag) {
|
|
46
|
+
if (node.name === this.tag) {
|
|
48
47
|
this.model = {};
|
|
49
48
|
return true;
|
|
50
49
|
}
|
|
51
|
-
this.parser = this.map[name];
|
|
50
|
+
this.parser = this.map[node.name];
|
|
52
51
|
if (this.parser) {
|
|
53
52
|
this.parser.parseOpen(node);
|
|
54
53
|
return true;
|
|
@@ -16,14 +16,12 @@ class TextXform extends base_xform_1.BaseXform {
|
|
|
16
16
|
xmlStream.closeNode();
|
|
17
17
|
}
|
|
18
18
|
parseOpen(node) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return true;
|
|
24
|
-
default:
|
|
25
|
-
return false;
|
|
19
|
+
if (node.name === "t") {
|
|
20
|
+
this._text = [];
|
|
21
|
+
this.model = "";
|
|
22
|
+
return true;
|
|
26
23
|
}
|
|
24
|
+
return false;
|
|
27
25
|
}
|
|
28
26
|
parseText(text) {
|
|
29
27
|
this._text.push(text);
|