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/lib/doc/table.js CHANGED
@@ -1,6 +1,36 @@
1
1
  /* eslint-disable max-classes-per-file */
2
2
  const colCache = require('../utils/col-cache');
3
3
 
4
+ // Excel's rules for table names: start with a letter, '_' or '\\', then letters, digits, '.' or
5
+ // '_'; at most 255 characters; and the name must not be read as a cell reference.
6
+ // Any non-ASCII character is treated as a letter so that names Excel accepts keep working.
7
+ const tableNameRegex = /^[A-Za-z_\\\u0080-\uFFFF][A-Za-z0-9._\u0080-\uFFFF]*$/;
8
+
9
+ function isCellReference(name) {
10
+ if (/^[RC]$/i.test(name) || /^R\d*C\d*$/i.test(name)) {
11
+ // 'R', 'C' and R1C1-style references such as 'R1C1' or 'RC'
12
+ return true;
13
+ }
14
+ const match = /^([A-Z]{1,3})(\d+)$/i.exec(name);
15
+ if (!match) {
16
+ return false;
17
+ }
18
+ // letters past XFD are not a column, so a name like 'ZZZ1' is fine
19
+ const letters = match[1].toUpperCase();
20
+ const isColumn = letters.length < 3 || letters <= 'XFD';
21
+ const row = parseInt(match[2], 10);
22
+ return isColumn && row >= 1 && row <= 1048576;
23
+ }
24
+
25
+ function isValidTableName(name) {
26
+ return (
27
+ typeof name === 'string' &&
28
+ name.length <= 255 &&
29
+ tableNameRegex.test(name) &&
30
+ !isCellReference(name)
31
+ );
32
+ }
33
+
4
34
  class Column {
5
35
  // wrapper around column model, allowing access and manipulation
6
36
  constructor(table, column, index) {
@@ -149,6 +179,13 @@ class Table {
149
179
  throw new Error(message);
150
180
  }
151
181
  };
182
+ assert(table.name, 'Table must have a name');
183
+ assert(
184
+ isValidTableName(table.name),
185
+ `Invalid table name '${table.name}'. A table name must start with a letter, underscore ` +
186
+ 'or backslash, contain only letters, digits, periods and underscores, ' +
187
+ 'not look like a cell reference and be at most 255 characters long.'
188
+ );
152
189
  assert(table.ref, 'Table must have ref');
153
190
  assert(table.columns, 'Table must have column definitions');
154
191
  assert(table.rows, 'Table must have row definitions');
@@ -271,6 +271,23 @@ class Worksheet {
271
271
  spliceColumns(start, count, ...inserts) {
272
272
  const rows = this._rows;
273
273
  const nRows = rows.length;
274
+
275
+ // unmerge any merges that touch the spliced or shifted columns so their cells move
276
+ // as plain cells; they are re-merged at their new position below
277
+ const merges = Object.values(this._merges).filter(merge => merge.right >= start);
278
+ merges.forEach(merge => {
279
+ for (let i = merge.top; i <= merge.bottom; i++) {
280
+ for (let j = merge.left; j <= merge.right; j++) {
281
+ // keep each cell's own style (e.g. borders) while unmerging
282
+ const cell = this.getCell(i, j);
283
+ const {style} = cell;
284
+ cell.unmerge();
285
+ cell.style = style;
286
+ }
287
+ }
288
+ delete this._merges[colCache.encodeAddress(merge.top, merge.left)];
289
+ });
290
+
274
291
  if (inserts.length > 0) {
275
292
  // must iterate over all rows whether they exist yet or not
276
293
  for (let i = 0; i < nRows; i++) {
@@ -309,6 +326,26 @@ class Worksheet {
309
326
  this.getColumn(i).defn = null;
310
327
  }
311
328
 
329
+ // re-merge the merges that were unmerged above, shifted, shrunk or grown to match.
330
+ // Inserted columns join a merge only when it continues on both sides of them.
331
+ merges.forEach(merge => {
332
+ let {left, right} = merge;
333
+ if (left >= nKeep) {
334
+ left += nExpand;
335
+ } else if (left >= start) {
336
+ left = start + inserts.length;
337
+ }
338
+ if (right >= nKeep) {
339
+ right += nExpand;
340
+ } else if (right >= start) {
341
+ right = start - 1;
342
+ }
343
+ // drop merges whose columns were all deleted or that are down to a single cell
344
+ if (left < right || (left === right && merge.top < merge.bottom)) {
345
+ this.mergeCellsWithoutStyle(merge.top, left, merge.bottom, right);
346
+ }
347
+ });
348
+
312
349
  // account for defined names
313
350
  this.workbook.definedNames.spliceColumns(this.name, start, count, inserts.length);
314
351
  }
@@ -498,10 +498,10 @@ class WorkSheetXform extends BaseXform {
498
498
  model.media = [];
499
499
  if (model.drawing) {
500
500
  const drawingRel = rels[model.drawing.rId];
501
- const match = drawingRel.Target.match(/\/drawings\/([a-zA-Z0-9]+)[.][a-zA-Z]{3,4}$/);
502
- if (match) {
503
- const drawingName = match[1];
504
- const drawing = options.drawings[drawingName];
501
+ const match =
502
+ drawingRel && drawingRel.Target.match(/\/drawings\/([a-zA-Z0-9]+)[.][a-zA-Z]{3,4}$/);
503
+ const drawing = match && options.drawings[match[1]];
504
+ if (drawing) {
505
505
  drawing.anchors.forEach(anchor => {
506
506
  if (anchor.medium) {
507
507
  const image = {
package/lib/xlsx/xlsx.js CHANGED
@@ -95,7 +95,7 @@ class XLSX {
95
95
  Object.keys(model.drawings).forEach(name => {
96
96
  const drawing = model.drawings[name];
97
97
  const drawingRel = model.drawingRels[name];
98
- if (drawingRel) {
98
+ if (drawing && drawingRel) {
99
99
  drawingOptions.rels = drawingRel.reduce((o, rel) => {
100
100
  o[rel.Id] = rel;
101
101
  return o;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exceljs-lts",
3
- "version": "4.5.0",
3
+ "version": "4.5.1",
4
4
  "description": "Maintained fork of exceljs — drop-in replacement, fixes Excel \"We found a problem with some content\" repair prompts, the TypeError crash on rich-text shared strings, and lost formula results (0, false, ''). Read and write xlsx and csv files.",
5
5
  "private": false,
6
6
  "license": "MIT",