exceljs-lts 4.5.0 → 4.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.
package/dist/exceljs.js CHANGED
@@ -2674,6 +2674,29 @@ module.exports = Row;
2674
2674
 
2675
2675
  /* eslint-disable max-classes-per-file */
2676
2676
  const colCache = require('../utils/col-cache');
2677
+
2678
+ // Excel's rules for table names: start with a letter, '_' or '\\', then letters, digits, '.' or
2679
+ // '_'; at most 255 characters; and the name must not be read as a cell reference.
2680
+ // Any non-ASCII character is treated as a letter so that names Excel accepts keep working.
2681
+ const tableNameRegex = /^[A-Za-z_\\\u0080-\uFFFF][A-Za-z0-9._\u0080-\uFFFF]*$/;
2682
+ function isCellReference(name) {
2683
+ if (/^[RC]$/i.test(name) || /^R\d*C\d*$/i.test(name)) {
2684
+ // 'R', 'C' and R1C1-style references such as 'R1C1' or 'RC'
2685
+ return true;
2686
+ }
2687
+ const match = /^([A-Z]{1,3})(\d+)$/i.exec(name);
2688
+ if (!match) {
2689
+ return false;
2690
+ }
2691
+ // letters past XFD are not a column, so a name like 'ZZZ1' is fine
2692
+ const letters = match[1].toUpperCase();
2693
+ const isColumn = letters.length < 3 || letters <= 'XFD';
2694
+ const row = parseInt(match[2], 10);
2695
+ return isColumn && row >= 1 && row <= 1048576;
2696
+ }
2697
+ function isValidTableName(name) {
2698
+ return typeof name === 'string' && name.length <= 255 && tableNameRegex.test(name) && !isCellReference(name);
2699
+ }
2677
2700
  class Column {
2678
2701
  // wrapper around column model, allowing access and manipulation
2679
2702
  constructor(table, column, index) {
@@ -2807,6 +2830,8 @@ class Table {
2807
2830
  throw new Error(message);
2808
2831
  }
2809
2832
  };
2833
+ assert(table.name, 'Table must have a name');
2834
+ assert(isValidTableName(table.name), `Invalid table name '${table.name}'. A table name must start with a letter, underscore ` + 'or backslash, contain only letters, digits, periods and underscores, ' + 'not look like a cell reference and be at most 255 characters long.');
2810
2835
  assert(table.ref, 'Table must have ref');
2811
2836
  assert(table.columns, 'Table must have column definitions');
2812
2837
  assert(table.rows, 'Table must have row definitions');
@@ -3560,6 +3585,24 @@ class Worksheet {
3560
3585
  spliceColumns(start, count, ...inserts) {
3561
3586
  const rows = this._rows;
3562
3587
  const nRows = rows.length;
3588
+
3589
+ // unmerge any merges that touch the spliced or shifted columns so their cells move
3590
+ // as plain cells; they are re-merged at their new position below
3591
+ const merges = Object.values(this._merges).filter(merge => merge.right >= start);
3592
+ merges.forEach(merge => {
3593
+ for (let i = merge.top; i <= merge.bottom; i++) {
3594
+ for (let j = merge.left; j <= merge.right; j++) {
3595
+ // keep each cell's own style (e.g. borders) while unmerging
3596
+ const cell = this.getCell(i, j);
3597
+ const {
3598
+ style
3599
+ } = cell;
3600
+ cell.unmerge();
3601
+ cell.style = style;
3602
+ }
3603
+ }
3604
+ delete this._merges[colCache.encodeAddress(merge.top, merge.left)];
3605
+ });
3563
3606
  if (inserts.length > 0) {
3564
3607
  // must iterate over all rows whether they exist yet or not
3565
3608
  for (let i = 0; i < nRows; i++) {
@@ -3598,6 +3641,29 @@ class Worksheet {
3598
3641
  this.getColumn(i).defn = null;
3599
3642
  }
3600
3643
 
3644
+ // re-merge the merges that were unmerged above, shifted, shrunk or grown to match.
3645
+ // Inserted columns join a merge only when it continues on both sides of them.
3646
+ merges.forEach(merge => {
3647
+ let {
3648
+ left,
3649
+ right
3650
+ } = merge;
3651
+ if (left >= nKeep) {
3652
+ left += nExpand;
3653
+ } else if (left >= start) {
3654
+ left = start + inserts.length;
3655
+ }
3656
+ if (right >= nKeep) {
3657
+ right += nExpand;
3658
+ } else if (right >= start) {
3659
+ right = start - 1;
3660
+ }
3661
+ // drop merges whose columns were all deleted or that are down to a single cell
3662
+ if (left < right || left === right && merge.top < merge.bottom) {
3663
+ this.mergeCellsWithoutStyle(merge.top, left, merge.bottom, right);
3664
+ }
3665
+ });
3666
+
3601
3667
  // account for defined names
3602
3668
  this.workbook.definedNames.spliceColumns(this.name, start, count, inserts.length);
3603
3669
  }
@@ -13159,10 +13225,9 @@ class WorkSheetXform extends BaseXform {
13159
13225
  model.media = [];
13160
13226
  if (model.drawing) {
13161
13227
  const drawingRel = rels[model.drawing.rId];
13162
- const match = drawingRel.Target.match(/\/drawings\/([a-zA-Z0-9]+)[.][a-zA-Z]{3,4}$/);
13163
- if (match) {
13164
- const drawingName = match[1];
13165
- const drawing = options.drawings[drawingName];
13228
+ const match = drawingRel && drawingRel.Target.match(/\/drawings\/([a-zA-Z0-9]+)[.][a-zA-Z]{3,4}$/);
13229
+ const drawing = match && options.drawings[match[1]];
13230
+ if (drawing) {
13166
13231
  drawing.anchors.forEach(anchor => {
13167
13232
  if (anchor.medium) {
13168
13233
  const image = {
@@ -16326,7 +16391,7 @@ class XLSX {
16326
16391
  Object.keys(model.drawings).forEach(name => {
16327
16392
  const drawing = model.drawings[name];
16328
16393
  const drawingRel = model.drawingRels[name];
16329
- if (drawingRel) {
16394
+ if (drawing && drawingRel) {
16330
16395
  drawingOptions.rels = drawingRel.reduce((o, rel) => {
16331
16396
  o[rel.Id] = rel;
16332
16397
  return o;