@trebco/treb 29.4.1 → 29.5.1

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.
@@ -34,15 +34,15 @@ import { PixelsToColumnWidth } from './column-width';
34
34
  const XMLDeclaration = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n`;
35
35
 
36
36
  import { template } from './template-2';
37
- import type { SerializedModel, SerializedSheet, DataValidation } from 'treb-data-model';
37
+ import type { SerializedModel, SerializedSheet } from 'treb-data-model';
38
38
 
39
- import type { IArea, ICellAddress, CellValue, CellStyle,
40
- AnnotationLayout, Corner as LayoutCorner, Cell, Rectangle } from 'treb-base-types';
41
- import { Area, Cells, ValueType, Style } from 'treb-base-types';
39
+ import type { IArea, ICellAddress, CellStyle,
40
+ AnnotationLayout, Corner as LayoutCorner, Cell, Rectangle, Color } from 'treb-base-types';
41
+ import { Area, Cells, ValueType, Style, IsHTMLColor, IsThemeColor, ThemeColorIndex } from 'treb-base-types';
42
42
 
43
43
  // import * as xmlparser from 'fast-xml-parser';
44
44
  import type { XmlBuilderOptions} from 'fast-xml-parser';
45
- import { XMLParser, XMLBuilder } from 'fast-xml-parser';
45
+ import { XMLParser } from 'fast-xml-parser';
46
46
 
47
47
  import { SharedStrings } from './shared-strings2';
48
48
  import type { XlColor, BorderEdge } from './workbook-style2';
@@ -51,7 +51,7 @@ import { Theme } from './workbook-theme2';
51
51
 
52
52
  import type { RelationshipMap} from './relationship';
53
53
  import { AddRel } from './relationship';
54
- import { type DOMContent, XMLOptions2 } from './xml-utils';
54
+ import { type DOMContent, XMLOptions2, PatchXMLBuilder } from './xml-utils';
55
55
 
56
56
  import type { UnitAddress, UnitRange, ExpressionUnit} from 'treb-parser';
57
57
  import { Parser } from 'treb-parser';
@@ -72,6 +72,39 @@ interface NestedDOMType {
72
72
  }
73
73
  */
74
74
 
75
+ /**
76
+ * utility function. given a Color object (our Color, from Style) returns
77
+ * an XML structure like
78
+ *
79
+ * { a$: { rgb: '123456 }}
80
+ *
81
+ * or
82
+ *
83
+ * { a$: { theme: 1, tint: .5 }}
84
+ *
85
+ */
86
+ const ColorAttrs = (color?: Color): DOMContent|undefined => {
87
+
88
+ if (IsHTMLColor(color)) {
89
+ return {
90
+ a$: {
91
+ rgb: `FF` + color.text.substring(1),
92
+ },
93
+ };
94
+ }
95
+ if (IsThemeColor(color)) {
96
+ return {
97
+ a$: {
98
+ theme: ThemeColorIndex(color),
99
+ tint: color.tint,
100
+ },
101
+ };
102
+ }
103
+
104
+ return undefined;
105
+
106
+ };
107
+
75
108
  export class Exporter {
76
109
 
77
110
  // public zip?: JSZip;
@@ -119,7 +152,7 @@ export class Exporter {
119
152
  };
120
153
 
121
154
  // public xmlparser = new xmlparser.j2xParser(this.xmloptions);
122
- public xmlbuilder1 = new XMLBuilder(this.xmloptions);
155
+ public xmlbuilder1 = PatchXMLBuilder(this.xmloptions);
123
156
  public xmlparser2 = new XMLParser(XMLOptions2);
124
157
 
125
158
  // FIXME: need a way to share/pass parser flags
@@ -217,7 +250,8 @@ export class Exporter {
217
250
  if (color.argb !== undefined) {
218
251
  attrs.rgb = color.argb;
219
252
  }
220
- return attrs;
253
+
254
+ return attrs as DOMContent;
221
255
 
222
256
  };
223
257
 
@@ -255,7 +289,7 @@ export class Exporter {
255
289
  return block;
256
290
  });
257
291
 
258
- const BorderColorAttributes = (edge: BorderEdge) => {
292
+ const BorderColorAttributes= (edge: BorderEdge): DOMContent|undefined => {
259
293
  if (edge.color) {
260
294
  return { indexed: edge.color };
261
295
  }
@@ -263,131 +297,174 @@ export class Exporter {
263
297
  return { rgb: edge.rgba };
264
298
  }
265
299
  if (edge.theme) {
266
- const attrs: any = {
300
+ return {
267
301
  theme: edge.theme,
268
- }
269
- if (edge.tint) {
270
- attrs.tint = edge.tint;
271
- }
272
- return attrs;
302
+ tint: edge.tint,
303
+ };
273
304
  }
274
305
  return undefined;
275
306
  };
276
307
 
277
308
  const borders = style_cache.borders.map(border => {
278
- const block: any = {
279
- left: {},
280
- right: {},
281
- top: {},
282
- bottom: {},
283
- diagonal: {},
284
- };
309
+
310
+ const top: DOMContent = {};
311
+ const left: DOMContent = {};
312
+ const right: DOMContent = {};
313
+ const bottom: DOMContent = {};
314
+ const diagonal: DOMContent = {};
285
315
 
286
316
  if (border.top.style) {
287
- block.top.a$ = {
317
+ top.a$ = {
288
318
  style: border.top.style,
289
319
  };
290
320
  const attrs = BorderColorAttributes(border.top);
291
- if (attrs) { block.top.color = {a$: attrs}; }
321
+ if (attrs) { top.color = {a$: attrs}; }
292
322
  }
293
323
 
294
324
  if (border.left.style) {
295
- block.left.a$ = {
325
+ left.a$ = {
296
326
  style: border.left.style,
297
327
  };
298
328
  const attrs = BorderColorAttributes(border.left);
299
- if (attrs) { block.left.color = {a$: attrs}; }
329
+ if (attrs) { left.color = {a$: attrs}; }
300
330
  }
301
331
 
302
332
  if (border.bottom.style) {
303
- block.bottom.a$ = {
333
+ bottom.a$ = {
304
334
  style: border.bottom.style,
305
335
  };
306
336
  const attrs = BorderColorAttributes(border.bottom);
307
- if (attrs) { block.bottom.color = {a$: attrs}; }
337
+ if (attrs) { bottom.color = {a$: attrs}; }
308
338
  }
309
339
 
310
340
  if (border.right.style) {
311
- block.right.a$ = {
341
+ right.a$ = {
312
342
  style: border.right.style,
313
343
  };
314
344
  const attrs = BorderColorAttributes(border.right);
315
- if (attrs) { block.right.color = {a$: attrs}; }
345
+ if (attrs) { right.color = {a$: attrs}; }
316
346
  }
317
347
 
318
348
  if (border.diagonal.style) {
319
- block.diagonal.a$ = {
349
+ diagonal.a$ = {
320
350
  style: border.diagonal.style,
321
351
  };
322
352
  const attrs = BorderColorAttributes(border.diagonal);
323
- if (attrs) { block.diagonal.color = {a$: attrs}; }
353
+ if (attrs) { diagonal.color = {a$: attrs}; }
324
354
  }
325
355
 
326
- return block;
356
+ return {
357
+ left,
358
+ right,
359
+ top,
360
+ bottom,
361
+ diagonal,
362
+ };
363
+
327
364
  });
328
365
 
329
- // console.info("SC", style_cache);
366
+ const fills: DOMContent[] = style_cache.fills.map(fill => ({
367
+ patternFill: {
368
+ a$: {
369
+ patternType: (fill.pattern_gray !== undefined) ? `gray${fill.pattern_gray}` : fill.pattern_type,
370
+ },
330
371
 
331
- const fills = style_cache.fills.map(fill => {
332
- const block: any = {
333
- a$: { patternType: fill.pattern_type },
334
- };
372
+ bgColor: fill.bg_color ? {
373
+ a$: ColorAttributes(fill.bg_color),
374
+ } : undefined,
335
375
 
336
- if (fill.pattern_gray !== undefined) {
337
- block.a$.patternType = `gray${fill.pattern_gray}`;
338
- }
339
- if (fill.bg_color) {
340
- block.bgColor = { a$: ColorAttributes(fill.bg_color) };
341
- }
342
- if (fill.fg_color) {
343
- block.fgColor = { a$: ColorAttributes(fill.fg_color) };
376
+ fgColor: fill.fg_color ? {
377
+ a$: ColorAttributes(fill.fg_color),
378
+ } : undefined,
379
+ },
380
+ }));
381
+
382
+ const ValProp = (prop: string|number|undefined) => {
383
+
384
+ if (typeof prop === 'undefined') {
385
+ return undefined;
344
386
  }
345
387
 
346
- return {patternFill: block};
347
- });
388
+ return {
389
+ a$: {
390
+ val: prop,
391
+ },
392
+ };
393
+
394
+ };
348
395
 
349
- const fonts = style_cache.fonts.map(font => {
350
- const block: any = {};
396
+ const fonts: DOMContent[] = style_cache.fonts.map(font => {
351
397
 
352
- // flags
398
+ return {
353
399
 
354
- if (font.bold) { block.b = ''; }
355
- if (font.italic) { block.i = ''; }
356
- if (font.underline) { block.u = ''; }
357
- if (font.strike) { block.strike = ''; }
400
+ // flags
358
401
 
359
- // "val" props
402
+ b: font.bold ? '' : undefined,
403
+ i: font.italic ? '' : undefined,
404
+ u: font.underline ? '' : undefined,
405
+ strike: font.strike ? '' : undefined,
360
406
 
361
- if (font.size !== undefined) {
362
- block.sz = { a$: { val: font.size }};
363
- }
364
- if (font.family !== undefined) {
365
- block.family = { a$: { val: font.family }};
366
- }
367
- if (font.name !== undefined) {
368
- block.name = { a$: { val: font.name }};
369
- }
370
- if (font.scheme !== undefined) {
371
- block.scheme = { a$: { val: font.scheme }};
372
- }
407
+ // 'val' props
373
408
 
374
- // color
409
+ sz: ValProp(font.size),
410
+ family: ValProp(font.family),
411
+ name: ValProp(font.name),
412
+ scheme: ValProp(font.scheme),
375
413
 
376
- if (font.color_argb !== undefined) {
377
- block.color = { a$: { rgb: font.color_argb }};
414
+ color: font.color_argb ? {
415
+ a$: { rgb: font.color_argb },
416
+ } : font.color_theme ? {
417
+ a$: {
418
+ theme: font.color_theme,
419
+ tint: font.color_tint,
420
+ },
421
+ } : undefined,
422
+
423
+ };
424
+
425
+ });
426
+
427
+ const WithCount = (key: string, source: DOMContent[]) => {
428
+ if (source.length) {
429
+ return {
430
+ a$: { count: source.length },
431
+ [key]: source,
432
+ };
378
433
  }
379
- else if (font.color_theme !== undefined) {
380
- block.color = { a$: { theme: font.color_theme }};
381
- if (font.color_tint) {
382
- block.color.a$.tint = font.color_tint;
383
- }
434
+ return undefined;
435
+ };
436
+
437
+
438
+ const dxf: DOMContent[] = style_cache.dxf_styles.map(style => {
439
+
440
+ const entry: DOMContent = {
441
+ fill: style.fill ? {
442
+ patternFill: {
443
+ bgColor: ColorAttrs(style.fill),
444
+ },
445
+ } : undefined,
446
+ };
447
+
448
+ if (style.text || style.bold || style.italic || style.underline) {
449
+ entry.font = {
450
+ b: style.bold ? {} : undefined,
451
+ i: style.italic ? {} : undefined,
452
+ u: style.underline ? {} : undefined,
453
+ strike: style.strike ? {} : undefined,
454
+ color: ColorAttrs(style.text),
455
+ };
384
456
  }
385
457
 
386
- return block;
458
+ return entry;
459
+
387
460
  });
388
461
 
462
+ // console.info({dxf});
463
+
389
464
  const dom: DOMContent = {
465
+
390
466
  styleSheet: {
467
+
391
468
  a$: {
392
469
  'xmlns': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
393
470
  'xmlns:mc': 'http://schemas.openxmlformats.org/markup-compatibility/2006',
@@ -396,131 +473,34 @@ export class Exporter {
396
473
  'xmlns:x16r2': 'http://schemas.microsoft.com/office/spreadsheetml/2015/02/main',
397
474
  'xmlns:xr': 'http://schemas.microsoft.com/office/spreadsheetml/2014/revision',
398
475
  },
399
- },
400
- };
401
-
402
- // we're only adding elements here if they are not empty, but in
403
- // practice only numFmts can be empty (because there are implicit
404
- // formats); everything else has a default 0 entry
405
-
406
- if (style_cache.number_formats.length) {
407
-
408
- (dom.styleSheet as DOMContent).numFmts = {
409
- a$: { count: style_cache.number_formats.length },
410
- numFmt: style_cache.number_formats.map(format => {
411
- return {
412
- a$: {
413
- numFmtId: format.id,
414
- formatCode: format.format,
415
- } as DOMContent,
416
- };
417
- }),
418
- };
419
-
420
- // console.info(style_cache.number_formats);
421
- // console.info(dom.styleSheet.numFmts);
422
-
423
- }
424
-
425
- if (fonts.length) {
426
- (dom.styleSheet as DOMContent).fonts = {
427
- a$: { count: fonts.length },
428
- font: fonts,
429
- };
430
- }
431
-
432
- if (fills.length) {
433
- (dom.styleSheet as DOMContent).fills = {
434
- a$: { count: fills.length },
435
- fill: fills,
436
- };
437
- }
438
476
 
439
- if (borders.length) {
440
- (dom.styleSheet as DOMContent).borders = {
441
- a$: { count: borders.length },
442
- border: borders,
443
- };
444
- }
445
-
446
- // console.info("B", borders, JSON.stringify(dom.styleSheet.borders, undefined, 2))
447
-
448
- if (xfs.length) {
449
- (dom.styleSheet as DOMContent).cellXfs = {
450
- a$: { count: xfs.length },
451
- xf: xfs,
452
- };
453
- }
477
+ // we're only adding elements here if they are not empty, but in
478
+ // practice only numFmts can be empty (because there are implicit
479
+ // formats); everything else has a default 0 entry
454
480
 
455
- if (style_cache.dxf_styles.length) {
456
- const dxf: DOMContent[] = [];
457
- for (const style of style_cache.dxf_styles) {
458
- const entry: DOMContent = {};
459
- if (style.text || style.bold || style.italic || style.underline) {
460
- const font: DOMContent = {};
461
- if (style.text) {
462
- font.color = { a$: {}};
463
- if (style.text.text) {
464
- (font.color.a$ as DOMContent).rgb = `FF` + style.text.text.substring(1);
465
- }
466
- else if (style.text.theme) {
467
- (font.color.a$ as DOMContent).theme = style.text.theme;
468
- if (style.text.tint) {
469
- (font.color.a$ as DOMContent).tint = style.text.tint;
470
- }
471
- }
472
- }
473
- if (style.bold) {
474
- font.b = {};
475
- }
476
- if (style.italic) {
477
- font.i = {};
478
- }
479
- if (style.underline) {
480
- font.u = {};
481
- }
482
- if (style.strike) {
483
- font.strike = {};
484
- }
485
- entry.font = font;
486
- }
487
- if (style.fill) {
488
- const attrs: DOMContent = {};
489
- if (style.fill.text) {
490
- attrs.rgb = `FF` + style.fill.text.substring(1);
491
- }
492
- else if (style.fill.theme) {
493
- attrs.theme = style.fill.theme;
494
- if (style.fill.tint) {
495
- attrs.tint = style.fill.tint;
496
- }
497
- }
498
- const color: DOMContent = { a$: attrs };
499
- entry.fill = { patternFill: { bgColor: color }};
500
- }
501
- dxf.push(entry);
502
- }
503
-
504
- if (dxf.length) {
505
- (dom.styleSheet as DOMContent).dxfs = {
506
- a$: { count: dxf.length },
507
- dxf,
508
- };
509
- }
481
+ numFmts: style_cache.number_formats.length ? {
482
+ a$: { count: style_cache.number_formats.length },
483
+ numFmt: style_cache.number_formats.map(format => {
484
+ return {
485
+ a$: {
486
+ numFmtId: format.id,
487
+ formatCode: format.format,
488
+ } as DOMContent,
489
+ };
490
+ }),
491
+ } : undefined,
492
+
493
+ fonts: WithCount('font', fonts),
494
+ fills: WithCount('fill', fills),
495
+ borders: WithCount('border', borders),
496
+ cellXfs: WithCount('xf', xfs),
497
+ dxfs: WithCount('dxf', dxf),
510
498
 
511
- }
499
+ },
512
500
 
513
- // not used:
514
- //
515
- // cellStyleXfs
516
- // cellStyles
517
- // tableStyles
518
-
519
- // ------------
501
+ };
520
502
 
521
503
  const xml = XMLDeclaration + this.xmlbuilder1.build(dom);
522
- // console.info(xml);
523
-
524
504
  this.zip?.Set('xl/styles.xml', xml);
525
505
 
526
506
  }
@@ -558,34 +538,6 @@ export class Exporter {
558
538
 
559
539
  }
560
540
 
561
- /**
562
- * FIXME: merge with workbook function (put somewhere else)
563
- * /
564
- public async ReadRels(zip?: JSZip, path = ''): Promise<RelationshipMap> {
565
-
566
- const rels: RelationshipMap = {};
567
- const data = await zip?.file(path)?.async('text') as string;
568
- //
569
- // force array on <Relationship/> elements, but be slack on the rest
570
- // (we know they are single elements)
571
- //
572
- const xml = this.xmlparser2.parse(data || '');
573
- console.info(path, xml);
574
-
575
- for (const relationship of xml.Relationships?.Relationship || []) {
576
- const id = relationship.a$.Id;
577
- rels[id] = {
578
- id,
579
- type: relationship.a$.Type,
580
- target: relationship.a$.Target,
581
- };
582
- }
583
-
584
- return rels;
585
-
586
- }
587
- */
588
-
589
541
  /**
590
542
  * FIXME: we might not always need this.
591
543
  */
@@ -1292,80 +1244,6 @@ export class Exporter {
1292
1244
 
1293
1245
  const default_row_height = sheet.default_row_height ? (sheet.default_row_height / 20 * 15) : 15;
1294
1246
 
1295
- const dom: any = {
1296
- worksheet: {
1297
- a$: {
1298
- ...sheet_attributes,
1299
- },
1300
- dimension: {
1301
- a$: {
1302
- ref: 'A1',
1303
- },
1304
- },
1305
- sheetViews: {
1306
- sheetView: {
1307
- a$: {
1308
- // tabSelected: (sheet_index === active_sheet ? 1 : 0),
1309
- workbookViewId: 0,
1310
- },
1311
- },
1312
- },
1313
- sheetFormatPr: {
1314
- a$: default_row_height === 15 ? {
1315
- 'x14ac:dyDescent': 0.25,
1316
- } : {
1317
- defaultRowHeight: default_row_height,
1318
- customHeight: 1,
1319
- 'x14ac:dyDescent': 0.25,
1320
- },
1321
- },
1322
- cols: {},
1323
- sheetData: {},
1324
- mergeCells: {
1325
- a$: { count: 0 },
1326
- },
1327
- dataValidations: {},
1328
- hyperlinks: {},
1329
- conditionalFormatting: {},
1330
- pageMargins: {
1331
- a$: {
1332
- left: 0.7,
1333
- right: 0.7,
1334
- top: 0.75,
1335
- bottom: 0.75,
1336
- header: 0.3,
1337
- footer: 0.3,
1338
- },
1339
- },
1340
- drawing: {},
1341
- tableParts: {
1342
- a$: {
1343
- count: 0,
1344
- },
1345
- },
1346
- extLst: {
1347
- ext: {
1348
- a$: {
1349
- uri: '{05C60535-1F16-4fd2-B633-F4F36F0B64E0}',
1350
- 'xmlns:x14': 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/main',
1351
- },
1352
- },
1353
- },
1354
-
1355
- },
1356
- };
1357
-
1358
- /*
1359
- const column_styles = (sheet.column_style as any) || {};
1360
-
1361
- for (const key of Object.keys(column_styles)) {
1362
- console.info(key, '=>', column_styles[key]);
1363
-
1364
- }
1365
-
1366
- return;
1367
- */
1368
-
1369
1247
  // data has different representations. it is either blocked into rows or
1370
1248
  // columns, or a set of individual cells. we could theoretically guarantee
1371
1249
  // a particular encoding if we wanted to (by row would be optimal for excel).
@@ -1400,7 +1278,8 @@ export class Exporter {
1400
1278
 
1401
1279
  // cells data is row-major, and sparse.
1402
1280
 
1403
- const sheet_data: any = { row: [] };
1281
+ // const sheet_data: any = { row: [] };
1282
+ const sheet_rows: DOMContent[] = [];
1404
1283
 
1405
1284
  const hyperlinks: Array<{
1406
1285
  rel: string,
@@ -1417,13 +1296,6 @@ export class Exporter {
1417
1296
  const merges: Area[] = [];
1418
1297
  const tables: TableDescription[] = [];
1419
1298
 
1420
- /*
1421
- const validations: Array<{
1422
- address: ICellAddress,
1423
- validation: DataValidation,
1424
- }> = [];
1425
- */
1426
-
1427
1299
  // --
1428
1300
 
1429
1301
  //
@@ -1449,7 +1321,7 @@ export class Exporter {
1449
1321
 
1450
1322
  // row span
1451
1323
  const span = {start: -1, end: -1};
1452
- const row: any = [];
1324
+ const row: DOMContent[] = [];
1453
1325
 
1454
1326
  for (let c = 0; c < cells.data[r].length; c++) {
1455
1327
 
@@ -1608,15 +1480,6 @@ export class Exporter {
1608
1480
  });
1609
1481
  }
1610
1482
 
1611
- /*
1612
- if (cell.validation && (cell.validation.type === ValidationType.List || cell.validation.type === ValidationType.Range)) {
1613
- validations.push({
1614
- address: {row: r, column: c},
1615
- validation: cell.validation,
1616
- });
1617
- }
1618
- */
1619
-
1620
1483
  // short-circuit here
1621
1484
  if (cell.type === ValueType.formula && /^=?sparkline\./i.test(cell.value as string)) {
1622
1485
  sparklines.push({
@@ -1656,21 +1519,22 @@ export class Exporter {
1656
1519
  }
1657
1520
 
1658
1521
  // v (child element) is the value
1659
- let v: CellValue = undefined;
1522
+
1523
+ let v: string|number|undefined;
1660
1524
  let t: string|undefined;
1661
- let f: any; // string|undefined;
1525
+ let f: DOMContent|string|undefined; // string|undefined;
1662
1526
 
1663
1527
  switch (cell.type) {
1664
1528
  case ValueType.formula:
1665
1529
  f = this.FormulaText(cell.value as string, cell);
1666
1530
  switch (cell.calculated_type) {
1667
1531
  case ValueType.string:
1668
- v = cell.calculated;
1532
+ v = cell.calculated as string;
1669
1533
  t = 'str';
1670
1534
  break;
1671
1535
 
1672
1536
  case ValueType.number:
1673
- v = cell.calculated;
1537
+ v = cell.calculated as number;
1674
1538
  break;
1675
1539
 
1676
1540
  case ValueType.boolean:
@@ -1686,7 +1550,7 @@ export class Exporter {
1686
1550
  break;
1687
1551
 
1688
1552
  case ValueType.number:
1689
- v = cell.value;
1553
+ v = cell.value as number;
1690
1554
  break;
1691
1555
 
1692
1556
  case ValueType.boolean:
@@ -1710,63 +1574,63 @@ export class Exporter {
1710
1574
  }
1711
1575
  }
1712
1576
 
1713
- // zerp
1714
- const element: any = {
1577
+ row.push({
1715
1578
  a$: {
1579
+
1716
1580
  r: Area.CellAddressToLabel({row: r, column: c}),
1717
- // t,
1718
- // s,
1719
- },
1720
- // v,
1721
- };
1581
+ t,
1722
1582
 
1723
- if (t !== undefined) {
1724
- element.a$.t = t;
1725
- }
1726
- if (s !== undefined) {
1583
+ // old comment regarding `s`:
1727
1584
 
1728
- // we could skip this if it's equal to row style,
1729
- // or there is no row style and it's equal to column style
1730
- // or there is no column style and it's equal to sheet style
1585
+ // we could skip this if it's equal to row style,
1586
+ // or there is no row style and it's equal to column style
1587
+ // or there is no column style and it's equal to sheet style
1731
1588
 
1732
- element.a$.s = s;
1589
+ s,
1590
+ },
1733
1591
 
1734
- }
1735
- if (f !== undefined) {
1736
- element.f = f;
1737
- }
1738
- if (v !== undefined) {
1739
- element.v = v;
1740
- }
1592
+ f,
1593
+ v,
1741
1594
 
1742
- row.push(element);
1595
+ });
1743
1596
 
1744
1597
  }
1745
1598
  }
1746
1599
 
1747
1600
  if (row.length || (row_style && row_style !== sheet_style)) {
1748
1601
 
1749
- const row_data: any = {
1750
- a$: {
1751
- r: r + 1,
1752
- spans: `${span.start + 1}:${span.end + 1}`, // this works out to 0:0 for an empty row, will that work?
1753
- },
1754
- c: row,
1755
- };
1602
+ let customHeight: number|undefined = undefined;
1603
+ let ht: number|undefined = undefined;
1604
+
1605
+ let s: number|undefined = undefined;
1606
+ let customFormat: number|undefined = undefined;
1607
+
1756
1608
  if (sheet.row_height
1757
- && (typeof sheet.row_height[r] === 'number')
1758
- && sheet.row_height[r] !== sheet.default_row_height) {
1759
-
1760
- row_data.a$.customHeight = 1;
1761
- row_data.a$.ht = sheet.row_height[r] * 3 / 4;
1609
+ && (typeof sheet.row_height[r] === 'number')
1610
+ && sheet.row_height[r] !== sheet.default_row_height) {
1611
+
1612
+ customHeight = 1;
1613
+ ht = sheet.row_height[r] * 3 / 4;
1762
1614
  }
1763
1615
 
1764
1616
  if (row_style && row_style !== sheet_style) {
1765
- row_data.a$.s = row_style;
1766
- row_data.a$.customFormat = 1;
1617
+ s = row_style;
1618
+ customFormat = 1;
1767
1619
  }
1768
1620
 
1769
- sheet_data.row.push(row_data);
1621
+ // sheet_data.row.
1622
+ sheet_rows.push({
1623
+ a$: {
1624
+ r: r + 1,
1625
+ spans: `${span.start + 1}:${span.end + 1}`, // this works out to 0:0 for an empty row, will that work?
1626
+ customHeight,
1627
+ ht,
1628
+ s,
1629
+ customFormat,
1630
+ },
1631
+ c: row,
1632
+ });
1633
+
1770
1634
  }
1771
1635
 
1772
1636
  }
@@ -1787,11 +1651,6 @@ export class Exporter {
1787
1651
  // because we'll have a default entry for columns that have the
1788
1652
  // sheet style. this is only for columns that are different.
1789
1653
 
1790
- if (sheet.default_column_width) {
1791
- dom.worksheet.sheetFormatPr.a$.defaultColWidth = // sheet.default_column_width * one_hundred_pixels / 100;
1792
- PixelsToColumnWidth(sheet.default_column_width);
1793
- }
1794
-
1795
1654
  for (let c = 0; c < sheet.columns; c++) {
1796
1655
  const entry: { style?: number, width?: number, index: number } = { index: c };
1797
1656
  if (sheet.column_width
@@ -1799,36 +1658,14 @@ export class Exporter {
1799
1658
  && (typeof sheet.column_width[c] === 'number')
1800
1659
  && sheet.column_width[c] !== sheet.default_column_width) {
1801
1660
 
1802
- entry.width = // sheet.column_width[c] * one_hundred_pixels / 100;
1803
- PixelsToColumnWidth(sheet.column_width[c]);
1804
-
1805
- // console.info("COLUMN", c, 'width', sheet.column_width[c], 'calc?', entry.width, '100p', one_hundred_pixels);
1806
-
1661
+ entry.width = PixelsToColumnWidth(sheet.column_width[c]);
1807
1662
  }
1808
1663
 
1809
- let style = column_style_map[c];
1664
+ const style = column_style_map[c];
1810
1665
  if (style && style !== sheet_style) {
1811
1666
  entry.style = style;
1812
1667
  }
1813
1668
 
1814
- /*
1815
- let style = sheet.column_style[c];
1816
-
1817
- if (typeof style === 'number') {
1818
- style = cell_style_refs[style];
1819
- if (style) {
1820
- entry.style = style_cache.EnsureStyle(style_cache.StyleOptionsFromProperties(style));
1821
- }
1822
- }
1823
- else if (style) {
1824
- entry.style = style_cache.EnsureStyle(style_cache.StyleOptionsFromProperties(style));
1825
- }
1826
- */
1827
-
1828
- //if (sheet.column_style[c]) {
1829
- // entry.style = style_cache.EnsureStyle(style_cache.StyleOptionsFromProperties(sheet.column_style[c]));
1830
- //}
1831
-
1832
1669
  if (entry.style !== undefined || entry.width !== undefined) {
1833
1670
  column_entries[c] = entry;
1834
1671
  }
@@ -1837,9 +1674,11 @@ export class Exporter {
1837
1674
  // we're short-cutting here, these should be arranged in blocks if
1838
1675
  // there's overlap. not sure how much of an issue that is though.
1839
1676
 
1677
+ let dom_cols: DOMContent|undefined;
1678
+
1840
1679
  if (column_entries.length || sheet_style) {
1841
1680
 
1842
- const filled: any[] = [];
1681
+ const filled: DOMContent[] = [];
1843
1682
  const default_column_width = PixelsToColumnWidth(sheet.default_column_width || 90);
1844
1683
 
1845
1684
  // FIXME: can merge these two branches
@@ -1863,25 +1702,13 @@ export class Exporter {
1863
1702
  });
1864
1703
  }
1865
1704
 
1866
- const a$: any = {
1705
+ filled.push({a$: {
1867
1706
  min: entry.index + 1,
1868
1707
  max: entry.index + 1,
1869
- };
1870
- if (entry.style === undefined) {
1871
- a$.style = sheet_style;
1872
- }
1873
- else {
1874
- a$.style = entry.style;
1875
- }
1876
- if (entry.width !== undefined) {
1877
- a$.width = entry.width;
1878
- a$.customWidth = 1;
1879
- }
1880
- else {
1881
- a$.width = default_column_width;
1882
- }
1883
-
1884
- filled.push({a$});
1708
+ style: entry.style === undefined ? sheet_style : entry.style,
1709
+ width: entry.width === undefined ? default_column_width : entry.width,
1710
+ customWidth: entry.width === undefined ? undefined : 1,
1711
+ }});
1885
1712
 
1886
1713
  start_index = entry.index;
1887
1714
 
@@ -1898,44 +1725,20 @@ export class Exporter {
1898
1725
  });
1899
1726
  }
1900
1727
 
1901
- dom.worksheet.cols.col = filled;
1728
+ dom_cols = { col: filled };
1902
1729
 
1903
1730
  }
1904
1731
 
1905
- /*
1906
- else {
1907
- dom.worksheet.cols.col = column_entries.map(entry => {
1908
- const a$: any = {
1909
- min: entry.index + 1,
1910
- max: entry.index + 1,
1911
- };
1912
- if (entry.style !== undefined) {
1913
- a$.style = entry.style;
1914
- }
1915
- if (entry.width !== undefined) {
1916
- a$.width = entry.width;
1917
- a$.customWidth = 1;
1918
- }
1919
- else {
1920
- a$.width = // (sheet.default_column_width || 100) / 100 * one_hundred_pixels;
1921
- default_column_width; // PixelsToColumnWidth(sheet.default_column_width || 90);
1922
- }
1923
- return {a$};
1924
- });
1925
- }
1926
- console.info({cols: dom.worksheet.cols});
1927
- */
1928
- }
1929
-
1930
- else {
1931
- delete dom.worksheet.cols;
1932
1732
  }
1933
1733
 
1934
1734
  // --- validation ----------------------------------------------------------
1935
1735
 
1736
+ let dataValidations: DOMContent|undefined;
1737
+
1936
1738
  if (sheet.data_validations?.length) {
1937
1739
 
1938
- dom.worksheet.dataValidations = {
1740
+ dataValidations = {
1741
+
1939
1742
  a$: { count: sheet.data_validations.length },
1940
1743
  dataValidation: sheet.data_validations.map(validation => {
1941
1744
 
@@ -1943,15 +1746,8 @@ export class Exporter {
1943
1746
  return new Area(target.start, target.end).spreadsheet_label;
1944
1747
  }).join(' ');
1945
1748
 
1946
- const entry: any = {
1947
- a$: {
1948
- type: 'list',
1949
- allowBlank: 1,
1950
- showInputMessage: 1,
1951
- showErrorMessage: 1,
1952
- sqref, // : new Area(validation.address).spreadsheet_label,
1953
- },
1954
- };
1749
+ let formula1: string|undefined = undefined;
1750
+
1955
1751
  if (validation.type === 'range') {
1956
1752
 
1957
1753
  const range: UnitRange = {
@@ -1965,54 +1761,54 @@ export class Exporter {
1965
1761
  ,
1966
1762
  }
1967
1763
 
1968
- console.info("AA", {validation});
1969
-
1970
1764
  if (typeof validation.area.start.sheet_id !== 'undefined') {
1971
1765
  range.start.sheet = sheet_name_map[validation.area.start.sheet_id];
1972
1766
  }
1973
1767
 
1974
- console.info("m1");
1975
-
1976
- /*
1977
- const area = new Area(
1978
- {...validation.validation.area.start, absolute_column: true, absolute_row: true},
1979
- {...validation.validation.area.end, absolute_column: true, absolute_row: true},
1980
- );
1981
-
1982
- entry.formula1 = `${area.spreadsheet_label}`;
1983
- */
1984
- entry.formula1 = this.parser.Render(range);
1768
+ formula1 = this.parser.Render(range);
1985
1769
 
1986
1770
  }
1987
1771
  else if (validation.type === 'list') {
1988
- entry.formula1 = `"${validation.list.join(',')}"`;
1772
+ formula1 = `"${validation.list.join(',')}"`;
1989
1773
  }
1990
- return entry;
1774
+
1775
+ return {
1776
+ a$: {
1777
+ type: 'list',
1778
+ allowBlank: 1,
1779
+ showInputMessage: 1,
1780
+ showErrorMessage: 1,
1781
+ sqref, // : new Area(validation.address).spreadsheet_label,
1782
+ },
1783
+ formula1,
1784
+ };
1785
+
1991
1786
  }),
1992
1787
  };
1993
1788
 
1994
1789
  }
1995
- else {
1996
- delete dom.worksheet.dataValidations;
1997
- }
1998
1790
 
1999
1791
  // --- tables ------------------------------------------------------------
2000
1792
 
1793
+ let tableParts: DOMContent|undefined;
1794
+
2001
1795
  if (tables.length) {
2002
- dom.worksheet.tableParts.a$.count = tables.length;
2003
- dom.worksheet.tableParts.tablePart = tables.map(table => {
2004
- return {
2005
- a$: {
2006
- 'r:id': table.rel || '',
2007
- }
2008
- };
2009
- });
2010
- }
2011
- else {
2012
- delete dom.worksheet.tableParts;
1796
+
1797
+ tableParts = {
1798
+ a$: {
1799
+ count: tables.length,
1800
+ },
1801
+ tablePart: tables.map(table => {
1802
+ return {
1803
+ a$: {
1804
+ 'r:id': table.rel || '',
1805
+ }
1806
+ };
1807
+ }),
1808
+ };
1809
+
2013
1810
  }
2014
1811
 
2015
- // const GUID = () => '{' + uuidv4().toUpperCase() + '}';
2016
1812
 
2017
1813
  for (const table of tables) {
2018
1814
 
@@ -2021,47 +1817,23 @@ export class Exporter {
2021
1817
  totals_attributes.totalsRowCount = 1;
2022
1818
  }
2023
1819
 
2024
- const tableColumns: any = {
1820
+ const tableColumns: DOMContent = {
2025
1821
  a$: {
2026
1822
  count: (table.columns || []).length,
2027
1823
  },
2028
- tableColumn: [],
2029
- /*
2030
- tableColumn: (table.columns||[]).map((column, index) => ({
2031
- a$: {
2032
- id: index + 1,
2033
- // 'xr3:uid': GUID(),
2034
- name: column || ('Column' + (index + 1)),
2035
- },
2036
- })),
2037
- */
2038
- };
2039
-
2040
- if (table.columns) {
2041
- for (let i = 0; i < table.columns.length; i++) {
2042
- const column = table.columns[i];
1824
+ tableColumn: (table.columns||[]).map((column, i) => {
2043
1825
  const footer = (table.footers || [])[i];
2044
- const obj: any = {
1826
+ return {
2045
1827
  a$: {
2046
1828
  id: i + 1,
2047
1829
  name: column || `Column${i + 1}`,
1830
+ totalsRowLabel: footer?.type === 'label' ? footer.value : undefined,
1831
+ totalsRowFunction: footer?.type === 'formula' ? 'custom' : undefined,
2048
1832
  },
1833
+ totalsRowFormula: footer?.type === 'formula' ? footer.value : undefined,
2049
1834
  };
2050
-
2051
- if (footer) {
2052
- if (footer.type === 'label') {
2053
- obj.a$.totalsRowLabel = footer.value;
2054
- }
2055
- else if (footer.type === 'formula') {
2056
- obj.a$.totalsRowFunction = 'custom';
2057
- obj.totalsRowFormula = footer.value;
2058
- }
2059
- }
2060
-
2061
- tableColumns.tableColumn.push(obj);
2062
-
2063
- }
2064
- }
1835
+ }),
1836
+ };
2065
1837
 
2066
1838
  const table_dom = {
2067
1839
  table: {
@@ -2072,18 +1844,15 @@ export class Exporter {
2072
1844
  'xmlns:xr': 'http://schemas.microsoft.com/office/spreadsheetml/2014/revision',
2073
1845
  'xmlns:xr3': 'http://schemas.microsoft.com/office/spreadsheetml/2016/revision3',
2074
1846
  id: table.index || 0,
2075
- // 'xr:uid': '{676B775D-AA84-41B6-8450-8515A94D2D7B}',
2076
1847
  name: table.name,
2077
1848
  displayName: table.display_name,
2078
1849
  ...totals_attributes,
2079
1850
  ref: table.ref,
2080
- // 'xr:uid': GUID(),
2081
1851
  },
2082
1852
 
2083
1853
  autoFilter: {
2084
1854
  a$: {
2085
1855
  ref: table.filterRef || table.ref,
2086
- // 'xr:uid': GUID(),
2087
1856
  },
2088
1857
  },
2089
1858
 
@@ -2103,14 +1872,19 @@ export class Exporter {
2103
1872
  };
2104
1873
 
2105
1874
  const xml = XMLDeclaration + this.xmlbuilder1.build(table_dom);
1875
+ // console.info(xml);
1876
+
2106
1877
  this.zip?.Set(`xl/tables/table${table.index}.xml`, xml);
2107
1878
 
2108
1879
  }
2109
1880
 
2110
1881
  // --- conditional formats -----------------------------------------------
2111
1882
 
1883
+ let conditionalFormatting: DOMContent|DOMContent[]|undefined;
1884
+
2112
1885
  if (sheet.conditional_formats?.length) {
2113
- const conditionalFormatting: any[] = [];
1886
+
1887
+ const format_list: DOMContent[] = [];
2114
1888
  let priority_index = 1;
2115
1889
 
2116
1890
  const reverse_operator_map: Record<string, string> = {};
@@ -2150,7 +1924,7 @@ export class Exporter {
2150
1924
  }
2151
1925
  if (operator) {
2152
1926
 
2153
- conditionalFormatting.push({
1927
+ format_list.push({
2154
1928
  a$: { sqref: new Area(format.area.start, format.area.end).spreadsheet_label },
2155
1929
  cfRule: {
2156
1930
  a$: { type: 'cellIs', dxfId: dxf_index, operator, priority: priority_index++ },
@@ -2162,7 +1936,7 @@ export class Exporter {
2162
1936
  break;
2163
1937
 
2164
1938
  case 'expression':
2165
- conditionalFormatting.push({
1939
+ format_list.push({
2166
1940
  a$: { sqref: new Area(format.area.start, format.area.end).spreadsheet_label },
2167
1941
  cfRule: {
2168
1942
  a$: { type: 'expression', dxfId: dxf_index, priority: priority_index++ },
@@ -2172,7 +1946,7 @@ export class Exporter {
2172
1946
  break;
2173
1947
 
2174
1948
  case 'duplicate-values':
2175
- conditionalFormatting.push({
1949
+ format_list.push({
2176
1950
  a$: { sqref: new Area(format.area.start, format.area.end).spreadsheet_label },
2177
1951
  cfRule: {
2178
1952
  a$: { type: format.unique ? 'uniqueValues' : 'duplicateValues', dxfId: dxf_index, priority: priority_index++ },
@@ -2182,10 +1956,11 @@ export class Exporter {
2182
1956
 
2183
1957
  case 'gradient':
2184
1958
  {
2185
- let cfvo: any[] = [];
2186
- let color: any[] = [];
1959
+ const cfvo: DOMContent[] = [];
1960
+ const color: DOMContent[] = [];
2187
1961
 
2188
1962
  for (const stop of format.stops) {
1963
+
2189
1964
  if (stop.value === 0) {
2190
1965
  cfvo.push({ a$: { type: 'min' }});
2191
1966
  }
@@ -2195,18 +1970,13 @@ export class Exporter {
2195
1970
  else {
2196
1971
  cfvo.push({ a$: { type: 'percentile', val: stop.value * 100 }});
2197
1972
  }
2198
- const stop_color: any = { a$: {}};
2199
- if (stop.color.text) {
2200
- stop_color.a$.rgb = 'FF' + stop.color.text.substring(1);
2201
- }
2202
- else if (stop.color.theme) {
2203
- stop_color.a$.theme = stop.color.theme;
2204
- stop_color.a$.tint = stop.color.tint || undefined;
2205
- }
2206
- color.push(stop_color);
1973
+
1974
+ const attrs = ColorAttrs(stop.color);
1975
+ if (attrs) { color.push(attrs); }
1976
+
2207
1977
  }
2208
1978
 
2209
- const generated = {
1979
+ const generated: DOMContent = {
2210
1980
  a$: { sqref: new Area(format.area.start, format.area.end).spreadsheet_label },
2211
1981
  cfRule: {
2212
1982
  a$: { type: 'colorScale', priority: priority_index++ },
@@ -2217,60 +1987,60 @@ export class Exporter {
2217
1987
  }
2218
1988
  };
2219
1989
 
2220
- conditionalFormatting.push(generated);
1990
+ format_list.push(generated);
2221
1991
 
2222
1992
  }
2223
1993
  break;
2224
1994
  }
2225
1995
  }
2226
1996
 
2227
- if (conditionalFormatting.length) {
2228
- dom.worksheet.conditionalFormatting = (conditionalFormatting.length > 1) ? conditionalFormatting : conditionalFormatting[0];
2229
- }
2230
- else {
2231
- delete dom.worksheet.conditionalFormatting;
1997
+ if (format_list.length) {
1998
+ conditionalFormatting = (format_list.length > 1) ? format_list : format_list[0];
2232
1999
  }
2233
2000
 
2234
2001
  }
2235
- else {
2236
- delete dom.worksheet.conditionalFormatting;
2237
- }
2238
2002
 
2239
2003
  // --- merges ------------------------------------------------------------
2240
2004
 
2005
+ let mergeCells: DOMContent|undefined;
2241
2006
  if (merges.length) {
2242
- dom.worksheet.mergeCells.a$.count = merges.length;
2243
- dom.worksheet.mergeCells.mergeCell = merges.map(merge => {
2244
- return {
2245
- a$: { ref: merge.spreadsheet_label }
2246
- };
2247
- });
2248
- }
2249
- else {
2250
- delete dom.worksheet.mergeCells;
2007
+
2008
+ mergeCells = {
2009
+ a$: { count: merges.length },
2010
+ mergeCell: merges.map(merge => {
2011
+ return {
2012
+ a$: { ref: merge.spreadsheet_label }
2013
+ };
2014
+ }),
2015
+ };
2016
+
2251
2017
  }
2252
2018
 
2253
2019
  // --- hyperlinks --------------------------------------------------------
2254
2020
 
2021
+ let dom_hyperlinks: DOMContent|undefined;
2022
+
2255
2023
  if (hyperlinks.length) {
2256
- dom.worksheet.hyperlinks.hyperlink = hyperlinks.map(link => {
2257
- return {
2258
- a$: {
2259
- 'r:id': link.rel,
2260
- ref: new Area(link.address).spreadsheet_label,
2261
- 'xr:uid': '{0C6B7792-7EA0-4932-BF15-D49C453C565D}',
2262
- },
2263
- };
2264
- });
2265
- }
2266
- else {
2267
- delete dom.worksheet.hyperlinks;
2024
+ dom_hyperlinks = {
2025
+ hyperlink: hyperlinks.map(link => {
2026
+ return {
2027
+ a$: {
2028
+ 'r:id': link.rel,
2029
+ ref: new Area(link.address).spreadsheet_label,
2030
+ 'xr:uid': '{0C6B7792-7EA0-4932-BF15-D49C453C565D}',
2031
+ },
2032
+ };
2033
+ }),
2034
+ };
2268
2035
  }
2269
2036
 
2270
2037
  // --- sparklines --------------------------------------------------------
2271
2038
 
2039
+ let extLst: DOMContent|undefined;
2040
+
2272
2041
  if (sparklines.length) {
2273
- dom.worksheet.extLst.ext['x14:sparklineGroups'] = {
2042
+
2043
+ const groups: DOMContent = {
2274
2044
  a$: {
2275
2045
  'xmlns:xm': 'http://schemas.microsoft.com/office/excel/2006/main',
2276
2046
  },
@@ -2298,16 +2068,11 @@ export class Exporter {
2298
2068
  }
2299
2069
  }
2300
2070
 
2301
- const a$: any = {
2302
- displayEmptyCellsAs: 'gap',
2303
- };
2304
-
2305
- if (/column/i.test(sparkline.formula)) {
2306
- a$.type = 'column';
2307
- }
2308
-
2309
2071
  return {
2310
- a$,
2072
+ a$: {
2073
+ displayEmptyCellsAs: 'gap',
2074
+ type: /column/i.test(sparkline.formula) ? 'column' : undefined,
2075
+ },
2311
2076
  'x14:colorSeries': { a$: { rgb: 'FF376092' }},
2312
2077
  'x14:sparklines': {
2313
2078
  'x14:sparkline': {
@@ -2318,15 +2083,25 @@ export class Exporter {
2318
2083
  }
2319
2084
  }),
2320
2085
  };
2321
- }
2322
- else {
2323
- delete dom.worksheet.extLst;
2086
+
2087
+ extLst = {
2088
+ ext: {
2089
+ a$: {
2090
+ uri: '{05C60535-1F16-4fd2-B633-F4F36F0B64E0}',
2091
+ 'xmlns:x14': 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/main',
2092
+ },
2093
+ 'x14:sparklineGroups': groups
2094
+ }
2095
+ };
2096
+
2097
+
2324
2098
  }
2325
2099
 
2326
- dom.worksheet.sheetData = sheet_data;
2327
2100
 
2328
2101
  // --- charts ------------------------------------------------------------
2329
2102
 
2103
+ let dom_drawing: DOMContent|undefined;
2104
+
2330
2105
  const charts = this.ParseCharts(sheet);
2331
2106
  const images = this.ParseImages(sheet);
2332
2107
 
@@ -2398,7 +2173,8 @@ export class Exporter {
2398
2173
  `http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing`,
2399
2174
  `../drawings/drawing${drawing.index}.xml`);
2400
2175
 
2401
- dom.worksheet.drawing = {
2176
+ // dom.worksheet.drawing = {
2177
+ dom_drawing = {
2402
2178
  a$: {
2403
2179
  'r:id': drawing_rel,
2404
2180
  },
@@ -2406,7 +2182,7 @@ export class Exporter {
2406
2182
 
2407
2183
  }
2408
2184
  else {
2409
- delete dom.worksheet.drawing;
2185
+ // delete dom.worksheet.drawing;
2410
2186
  }
2411
2187
 
2412
2188
  // --- move page margins -------------------------------------------------
@@ -2417,13 +2193,75 @@ export class Exporter {
2417
2193
 
2418
2194
  // --- end? --------------------------------------------------------------
2419
2195
 
2196
+ const sheetFormatPr: DOMContent = {
2197
+ a$: {
2198
+ 'x14ac:dyDescent': 0.25,
2199
+ defaultRowHeight: default_row_height === 15 ? undefined : default_row_height,
2200
+ customHeight: default_row_height === 15 ? undefined : 1,
2201
+ defaultColWidth: sheet.default_column_width ? PixelsToColumnWidth(sheet.default_column_width) : undefined,
2202
+ },
2203
+ }
2204
+
2205
+ //------------------------------------------------------------------------
2206
+ //
2207
+ // NOTE: order matters. that's why we define the layout here. we
2208
+ // can't just append entries to the worksheet object.
2209
+ //
2210
+ //------------------------------------------------------------------------
2211
+
2212
+ const dom: DOMContent = {
2213
+
2214
+ worksheet: {
2215
+ a$: { ...sheet_attributes },
2216
+ dimension: {
2217
+ a$: {
2218
+ ref: new Area(extent.start, extent.end).spreadsheet_label,
2219
+ },
2220
+ },
2221
+ sheetViews: {
2222
+ sheetView: {
2223
+ a$: {
2224
+ workbookViewId: 0,
2225
+ },
2226
+ },
2227
+ },
2228
+
2229
+ sheetFormatPr,
2230
+ cols: dom_cols,
2231
+ sheetData: { row: sheet_rows },
2232
+
2233
+ mergeCells,
2234
+ conditionalFormatting,
2235
+ dataValidations,
2236
+ hyperlinks: dom_hyperlinks,
2237
+
2238
+ pageMargins: {
2239
+ a$: {
2240
+ left: 0.7,
2241
+ right: 0.7,
2242
+ top: 0.75,
2243
+ bottom: 0.75,
2244
+ header: 0.3,
2245
+ footer: 0.3,
2246
+ },
2247
+ },
2248
+ drawing: dom_drawing,
2249
+ tableParts,
2250
+ extLst,
2251
+
2252
+ },
2253
+ };
2254
+
2255
+ // -----------------------------------------------------------------------
2256
+
2420
2257
  // it seems like chrome, at least, will maintain order. but this is
2421
2258
  // not gauranteed and we can't rely on it. the best thing to do might
2422
2259
  // be to use the renderer on blocks and then assemble the blocks ourselves.
2423
-
2424
- dom.worksheet.dimension.a$.ref = new Area(extent.start, extent.end).spreadsheet_label;
2260
+
2425
2261
  const xml = XMLDeclaration + this.xmlbuilder1.build(dom);
2426
2262
 
2263
+ // console.info(xml);
2264
+
2427
2265
  // write this into the file
2428
2266
 
2429
2267
  this.zip?.Set(`xl/worksheets/sheet${sheet_index + 1}.xml`, xml);
@@ -2459,15 +2297,12 @@ export class Exporter {
2459
2297
 
2460
2298
  this.WriteRels(workbook_rels, `xl/_rels/workbook.xml.rels`);
2461
2299
 
2462
- let definedNames: any = {definedName: []};
2463
-
2464
- if (source.named) {
2465
- for (const entry of source.named) {
2300
+ const definedNames: DOMContent|undefined = source.named?.length ? {
2301
+ definedName: (source.named||[]).map(entry => {
2466
2302
 
2467
2303
  let scope: string|undefined = undefined;
2468
2304
 
2469
2305
  if (entry.scope) {
2470
-
2471
2306
  const test = entry.scope.toLowerCase();
2472
2307
  for (const [index, sheet] of source.sheet_data.entries()) {
2473
2308
  if (sheet.name?.toLowerCase() === test) {
@@ -2477,20 +2312,16 @@ export class Exporter {
2477
2312
  }
2478
2313
  }
2479
2314
 
2480
- definedNames.definedName.push({
2315
+ return {
2481
2316
  a$: { name: entry.name, localSheetId: scope },
2482
2317
  t$: entry.expression,
2483
- });
2484
-
2485
- }
2486
- }
2318
+ };
2487
2319
 
2488
- if (!definedNames.definedName.length) {
2489
- definedNames = undefined;
2490
- }
2491
- // else { console.info({definedNames}); }
2320
+ }),
2321
+ } : undefined;
2322
+
2492
2323
 
2493
- const workbook_dom: any = {
2324
+ const workbook_dom: DOMContent = {
2494
2325
  workbook: {
2495
2326
  a$: {
2496
2327
  'xmlns': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
@@ -2516,17 +2347,14 @@ export class Exporter {
2516
2347
  },
2517
2348
  },
2518
2349
  sheets: {
2519
- sheet: source.sheet_data.map((sheet, index) => {
2520
- const a$: any = {
2350
+ sheet: source.sheet_data.map((sheet, index) => ({
2351
+ a$: {
2521
2352
  name: sheet.name || `Sheet${index + 1}`,
2522
2353
  sheetId: index + 1,
2523
2354
  'r:id': worksheet_rels_map[index],
2524
- };
2525
- if (sheet.visible === false) {
2526
- a$.state = 'hidden';
2355
+ state: (sheet.visible === false) ? 'hidden' : undefined,
2527
2356
  }
2528
- return { a$ };
2529
- }),
2357
+ })),
2530
2358
  },
2531
2359
  definedNames,
2532
2360
  },
@@ -2554,7 +2382,7 @@ export class Exporter {
2554
2382
  }
2555
2383
  }
2556
2384
 
2557
- const content_types_dom: any = {
2385
+ const content_types_dom: DOMContent = {
2558
2386
  Types: {
2559
2387
  a$: {
2560
2388
  'xmlns': 'http://schemas.openxmlformats.org/package/2006/content-types',
@@ -2578,7 +2406,7 @@ export class Exporter {
2578
2406
  }),
2579
2407
 
2580
2408
  // charts and drawings
2581
- ...drawings.reduce((a: any, drawing) => {
2409
+ ...drawings.reduce((a: DOMContent[], drawing) => {
2582
2410
  return a.concat([
2583
2411
  ...drawing.charts.map(chart => {
2584
2412
  return { a$: {
@@ -2633,34 +2461,4 @@ export class Exporter {
2633
2461
  return new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
2634
2462
  }
2635
2463
 
2636
- /* * zip -> binary string * /
2637
- public async AsBinaryString(compression_level?: number) {
2638
- if (!this.zip) {
2639
- throw new Error('missing zip');
2640
- }
2641
- const opts: JSZip.JSZipGeneratorOptions = { type: 'binarystring' };
2642
- if (typeof compression_level !== 'undefined') {
2643
- opts.compression = 'DEFLATE';
2644
- opts.compressionOptions = {level: compression_level };
2645
- }
2646
- const output = await this.zip.generateAsync(opts);
2647
- return output;
2648
- }
2649
-
2650
- /* * zip -> blob * /
2651
- public async AsBlob(compression_level?: number) {
2652
- if (!this.zip) {
2653
- throw new Error('missing zip');
2654
- }
2655
- const opts: JSZip.JSZipGeneratorOptions = { type: 'blob' };
2656
- if (typeof compression_level !== 'undefined') {
2657
- opts.compression = 'DEFLATE';
2658
- opts.compressionOptions = {level: compression_level };
2659
- }
2660
- const output = await this.zip.generateAsync(opts);
2661
- return output;
2662
- }
2663
- */
2664
-
2665
-
2666
- }
2464
+ }