From 915d91dc200bc33dcf95ea827d0032b4ad56cfd0 Mon Sep 17 00:00:00 2001 From: Pawel Siemienik Date: Wed, 28 Nov 2018 05:06:20 +0100 Subject: [PATCH 1/2] Improvements for images and their properly writing to xlsx and reading from xlsx with fully back compatible. NEW Anchor model NEW Integration tests test for image`s reader & writer NEW Unit tests for Anchor --- index.d.ts | 27 ++- lib/doc/anchor.js | 56 ++++++ lib/doc/worksheet.js | 19 ++ lib/xlsx/xform/drawing/cell-position-xform.js | 27 +-- .../xform/drawing/two-cell-anchor-xform.js | 23 ++- package.json | 2 +- spec/integration/data/images.xlsx | Bin 0 -> 23708 bytes spec/integration/workbook-xlsx-reader.spec.js | 163 ++++++++++++++---- spec/unit/doc/anchor.spec.js | 112 ++++++++++++ .../xform/drawing/cell-position-xform.spec.js | 9 +- .../data/{drawing.1.0.json => drawing.1.0.js} | 10 +- .../xlsx/xform/drawing/data/drawing.1.1.js | 27 +++ .../xlsx/xform/drawing/data/drawing.1.1.json | 25 --- .../xlsx/xform/drawing/data/drawing.1.3.js | 20 +++ .../xlsx/xform/drawing/data/drawing.1.3.json | 18 -- .../data/{drawing.1.4.json => drawing.1.4.js} | 10 +- .../xlsx/xform/drawing/drawing-xform.spec.js | 8 +- spec/unit/xlsx/xform/test-xform-helper.js | 2 +- 18 files changed, 437 insertions(+), 121 deletions(-) create mode 100644 lib/doc/anchor.js create mode 100644 spec/integration/data/images.xlsx create mode 100644 spec/unit/doc/anchor.spec.js rename spec/unit/xlsx/xform/drawing/data/{drawing.1.0.json => drawing.1.0.js} (50%) create mode 100644 spec/unit/xlsx/xform/drawing/data/drawing.1.1.js delete mode 100644 spec/unit/xlsx/xform/drawing/data/drawing.1.1.json create mode 100644 spec/unit/xlsx/xform/drawing/data/drawing.1.3.js delete mode 100644 spec/unit/xlsx/xform/drawing/data/drawing.1.3.json rename spec/unit/xlsx/xform/drawing/data/{drawing.1.4.json => drawing.1.4.js} (50%) diff --git a/index.d.ts b/index.d.ts index b95af83e9..0b44c442a 100644 --- a/index.d.ts +++ b/index.d.ts @@ -759,10 +759,31 @@ export interface Image { filename?: string; buffer?: Buffer; } - +export interface IAnchor { + col: number; + row: number; + nativeCol: number; + nativeRow: number; + nativeColOff: number; + nativeRowOff: number; +} +export class Anchor implements IAnchor{ + col: number; + nativeCol: number; + nativeColOff: number; + nativeRow: number; + nativeRowOff: number; + row: number; + + private readonly colWidth: number; + private readonly rowHeight: number; + worksheet: Worksheet; + + constructor(model: IAnchor|object = {}); +} export interface ImageRange { - tl: { col: number; row: number }; - br: { col: number; row: number }; + tl: { col: number; row: number } | Anchor; + br: { col: number; row: number } | Anchor; } export interface Range extends Location { diff --git a/lib/doc/anchor.js b/lib/doc/anchor.js new file mode 100644 index 000000000..3006b5dbd --- /dev/null +++ b/lib/doc/anchor.js @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2018 Paweł Siemienik + * LICENCE: MIT - please refer to LICENCE file included with this module + * or https://github.com/guyonroche/exceljs/blob/master/LICENSE + */ + +'use strict'; + +var Anchor = module.exports = function (model = {}) { + this.nativeCol = model.nativeCol || 0; + this.nativeColOff = model.nativeColOff || 0; + this.nativeRow = model.nativeRow || 0; + this.nativeRowOff = model.nativeRowOff || 0; + + if (model.col) { + this.col = model.col; + } + + if (model.row) { + this.row = model.row; + } +}; + +Anchor.asInstance = function (model) { + return model instanceof Anchor || model == null ? model : new Anchor(model); +}; + +Anchor.prototype = { + worksheet: null, + get col() { + return this.nativeCol + Math.min(this.colWidth - 1, this.nativeColOff) / this.colWidth; + }, + set col(v) { + if (v === this.col) return; + this.nativeCol = Math.floor(v); + this.nativeColOff = Math.floor((v - this.nativeCol) * this.colWidth); + }, + get row() { + return this.nativeRow + Math.min(this.rowHeight - 1, this.nativeRowOff) / this.rowHeight; + }, + set row(v) { + if (v === this.row) return; + this.nativeRow = Math.floor(v); + this.nativeRowOff = Math.floor((v - this.nativeRow) * this.rowHeight); + }, + get colWidth() { + return this.worksheet && this.worksheet.getColumn(this.nativeCol + 1) && this.worksheet.getColumn(this.nativeCol + 1).isCustomWidth ? + Math.floor(this.worksheet.getColumn(this.nativeCol + 1).width * 10000) : + 640000; + }, + get rowHeight() { + return this.worksheet && this.worksheet.getRow(this.nativeRow + 1) && this.worksheet.getRow(this.nativeRow + 1).height ? + Math.floor(this.worksheet.getRow(this.nativeRow + 1).height * 10000) : + 180000; + } +}; diff --git a/lib/doc/worksheet.js b/lib/doc/worksheet.js index 3b43e3b03..9dd4f8a53 100644 --- a/lib/doc/worksheet.js +++ b/lib/doc/worksheet.js @@ -13,6 +13,7 @@ var Range = require('./range'); var Row = require('./row'); var Column = require('./column'); var Enums = require('./enums'); +var Anchor = require('./anchor'); var DataValidations = require('./data-validations'); // Worksheet requirements @@ -515,6 +516,16 @@ Worksheet.prototype = { // ========================================================================= // Images addImage: function addImageToCells(imageId, range) { + if (range.tl && !(range.tl instanceof Anchor)) { + range.tl = new Anchor(range.tl); + range.tl.worksheet = this; + } + + if (range.br && !(range.br instanceof Anchor)) { + range.br = new Anchor(range.br); + range.br.worksheet = this; + } + this._media.push({ type: 'image', imageId, @@ -620,5 +631,13 @@ Worksheet.prototype = { this.views = value.views; this.autoFilter = value.autoFilter; this._media = value.media; + + var self = this; + this._media + .filter(m => m.type === 'image' && typeof m.range !== 'string') + .forEach(i => { + i.range.tl.worksheet = self; + i.range.br.worksheet = self; + }) } }; diff --git a/lib/xlsx/xform/drawing/cell-position-xform.js b/lib/xlsx/xform/drawing/cell-position-xform.js index e8047ff99..665c02e80 100644 --- a/lib/xlsx/xform/drawing/cell-position-xform.js +++ b/lib/xlsx/xform/drawing/cell-position-xform.js @@ -9,6 +9,7 @@ var utils = require('../../../utils/utils'); var BaseXform = require('../base-xform'); var IntegerXform = require('../simple/integer-xform'); +var Anchor = require('../../../doc/anchor'); var CellPositionXform = module.exports = function(options) { this.tag = options.tag; @@ -19,21 +20,21 @@ var CellPositionXform = module.exports = function(options) { 'xdr:rowOff': new IntegerXform({tag: 'xdr:rowOff', zero: true}) }; }; +CellPositionXform.buildModel = function (model) { + return new Anchor(model); +}; + utils.inherits(CellPositionXform, BaseXform, { render: function(xmlStream, model) { xmlStream.openNode(this.tag); - var col = Math.floor(model.col); - var colOff = Math.floor((model.col - col) * 640000); - this.map['xdr:col'].render(xmlStream, col); - this.map['xdr:colOff'].render(xmlStream, colOff); + this.map['xdr:col'].render(xmlStream, model.nativeCol); + this.map['xdr:colOff'].render(xmlStream, model.nativeColOff); - var row = Math.floor(model.row); - var rowOff = Math.floor((model.row - row) * 180000); - this.map['xdr:row'].render(xmlStream, row); - this.map['xdr:rowOff'].render(xmlStream, rowOff); + this.map['xdr:row'].render(xmlStream, model.nativeRow); + this.map['xdr:rowOff'].render(xmlStream, model.nativeRowOff); xmlStream.closeNode(); }, @@ -72,10 +73,12 @@ utils.inherits(CellPositionXform, BaseXform, { } switch (name) { case this.tag: - this.model = { - col: this.map['xdr:col'].model + (this.map['xdr:colOff'].model / 640000), - row: this.map['xdr:row'].model + (this.map['xdr:rowOff'].model / 180000) - }; + this.model = CellPositionXform.buildModel({ + nativeCol: this.map['xdr:col'].model, + nativeColOff: this.map['xdr:colOff'].model, + nativeRow: this.map['xdr:row'].model, + nativeRowOff: this.map['xdr:rowOff'].model + }); return false; default: // not quite sure how we get here! diff --git a/lib/xlsx/xform/drawing/two-cell-anchor-xform.js b/lib/xlsx/xform/drawing/two-cell-anchor-xform.js index aa0cbd9d7..743ce8bf0 100644 --- a/lib/xlsx/xform/drawing/two-cell-anchor-xform.js +++ b/lib/xlsx/xform/drawing/two-cell-anchor-xform.js @@ -10,6 +10,7 @@ var utils = require('../../../utils/utils'); var colCache = require('../../../utils/col-cache'); var BaseXform = require('../base-xform'); var StaticXform = require('../static-xform'); +var Anchor = require('../../../doc/anchor'); var CellPositionXform = require('./cell-position-xform'); var PicXform = require('./pic-xform'); @@ -33,16 +34,18 @@ utils.inherits(TwoCellAnchorXform, BaseXform, { if (typeof model.range === 'string') { var range = colCache.decode(model.range); // Note - zero based - model.tl = { - col: range.left - 1, - row: range.top - 1 - }; + model.tl = CellPositionXform.buildModel({ + nativeCol: range.left - 1, + nativeRow: range.top - 1 + }); // zero based but also +1 to cover to bottom right of br cell - model.br = { - col: range.right, - row: range.bottom - }; + model.br = CellPositionXform.buildModel({ + nativeCol: range.right, + nativeRow: range.bottom + }); } else { + model.range.tl = Anchor.asInstance(model.range.tl); + model.range.br = Anchor.asInstance(model.range.br); model.tl = model.range.tl; model.br = model.range.br; } @@ -121,6 +124,10 @@ utils.inherits(TwoCellAnchorXform, BaseXform, { model.medium = options.media[mediaId]; } } + + model.tl = Anchor.asInstance(model.tl); + model.br = Anchor.asInstance(model.br); + if (model.tl && model.br && Number.isInteger(model.tl.row) && Number.isInteger(model.tl.col) && Number.isInteger(model.br.row) && Number.isInteger(model.br.col)) { model.range = colCache.encode(model.tl.row + 1, model.tl.col + 1, model.br.row, model.br.col); } else { diff --git a/package.json b/package.json index 003995b71..46ac9ca2b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "exceljs", - "version": "1.6.3", + "version": "1.6.4", "description": "Excel Workbook Manager - Read and Write xlsx and csv Files.", "private": false, "license": "MIT", diff --git a/spec/integration/data/images.xlsx b/spec/integration/data/images.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..f240b5c34c9a83d3252872aa3c40ee02bc90b0f3 GIT binary patch literal 23708 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', - parsedModel: { row: 5, col: 7 }, + parsedModel:new Anchor({ row: 5, col: 7 }), tests: ['render', 'renderIn', 'parse'] }, { title: 'halves', create: function() { return new CellPositionXform({tag: 'xdr:to'}); }, - preparedModel: { row: 5.5, col: 7.5 }, + preparedModel: new Anchor({ row: 5.5, col: 7.5 }), xml: '7320000590000', - parsedModel: { row: 5.5, col: 7.5 }, + parsedModel: new Anchor({ row: 5.5, col: 7.5 }), tests: ['render', 'renderIn', 'parse'] } ]; diff --git a/spec/unit/xlsx/xform/drawing/data/drawing.1.0.json b/spec/unit/xlsx/xform/drawing/data/drawing.1.0.js similarity index 50% rename from spec/unit/xlsx/xform/drawing/data/drawing.1.0.json rename to spec/unit/xlsx/xform/drawing/data/drawing.1.0.js index 334fc4943..208db7f1b 100644 --- a/spec/unit/xlsx/xform/drawing/data/drawing.1.0.json +++ b/spec/unit/xlsx/xform/drawing/data/drawing.1.0.js @@ -1,4 +1,6 @@ -{ +var Anchor = require('../../../../../../lib/doc/anchor'); + +module.exports = { "anchors": [ { "range": "A1:C7", @@ -8,12 +10,12 @@ }, { "range": { - "tl": {"row": 2.5, "col": 5.5}, - "br": {"row": 10.5, "col": 8.5} + "tl": new Anchor({"row": 2.5, "col": 5.5}), + "br": new Anchor({"row": 10.5, "col": 8.5}), }, "picture": { "rId": "rId2" } } ] -} \ No newline at end of file +}; \ No newline at end of file diff --git a/spec/unit/xlsx/xform/drawing/data/drawing.1.1.js b/spec/unit/xlsx/xform/drawing/data/drawing.1.1.js new file mode 100644 index 000000000..70c0379bf --- /dev/null +++ b/spec/unit/xlsx/xform/drawing/data/drawing.1.1.js @@ -0,0 +1,27 @@ +var Anchor = require('../../../../../../lib/doc/anchor'); + +module.exports = { + "anchors":[ + { + "range": "A1:C7", + "tl": new Anchor({"row": 0, "col": 0}), + "br": new Anchor({"row": 7, "col": 3}), + "picture": { + "index": 1, + "rId": "rId1" + } + }, + { + "range": { + "tl": new Anchor({"row": 2.5, "col": 5.5}), + "br": new Anchor({"row": 10.5, "col": 8.5}), + }, + "tl": new Anchor({"row": 2.5, "col": 5.5}), + "br": new Anchor({"row": 10.5, "col": 8.5}), + "picture": { + "index": 2, + "rId": "rId2" + } + } + ] +}; \ No newline at end of file diff --git a/spec/unit/xlsx/xform/drawing/data/drawing.1.1.json b/spec/unit/xlsx/xform/drawing/data/drawing.1.1.json deleted file mode 100644 index 5bfdbc5e1..000000000 --- a/spec/unit/xlsx/xform/drawing/data/drawing.1.1.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "anchors":[ - { - "range": "A1:C7", - "tl": {"row": 0, "col": 0}, - "br": {"row": 7, "col": 3}, - "picture": { - "index": 1, - "rId": "rId1" - } - }, - { - "range": { - "tl": {"row": 2.5, "col": 5.5}, - "br": {"row": 10.5, "col": 8.5} - }, - "tl": {"row": 2.5, "col": 5.5}, - "br": {"row": 10.5, "col": 8.5}, - "picture": { - "index": 2, - "rId": "rId2" - } - } - ] -} \ No newline at end of file diff --git a/spec/unit/xlsx/xform/drawing/data/drawing.1.3.js b/spec/unit/xlsx/xform/drawing/data/drawing.1.3.js new file mode 100644 index 000000000..d5d29ba31 --- /dev/null +++ b/spec/unit/xlsx/xform/drawing/data/drawing.1.3.js @@ -0,0 +1,20 @@ +var Anchor = require('../../../../../../lib/doc/anchor'); + +module.exports = { + "anchors":[ + { + "tl": new Anchor({"row": 0, "col": 0}), + "br": new Anchor({"row": 7, "col": 3}), + "picture": { + "rId": "rId1" + } + }, + { + "tl": new Anchor({"row": 2.5, "col": 5.5}), + "br": new Anchor({"row": 10.5, "col": 8.5}), + "picture": { + "rId": "rId2" + } + } + ] +}; \ No newline at end of file diff --git a/spec/unit/xlsx/xform/drawing/data/drawing.1.3.json b/spec/unit/xlsx/xform/drawing/data/drawing.1.3.json deleted file mode 100644 index 9b891ad67..000000000 --- a/spec/unit/xlsx/xform/drawing/data/drawing.1.3.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "anchors":[ - { - "tl": {"row": 0, "col": 0}, - "br": {"row": 7, "col": 3}, - "picture": { - "rId": "rId1" - } - }, - { - "tl": {"row": 2.5, "col": 5.5}, - "br": {"row": 10.5, "col": 8.5}, - "picture": { - "rId": "rId2" - } - } - ] -} \ No newline at end of file diff --git a/spec/unit/xlsx/xform/drawing/data/drawing.1.4.json b/spec/unit/xlsx/xform/drawing/data/drawing.1.4.js similarity index 50% rename from spec/unit/xlsx/xform/drawing/data/drawing.1.4.json rename to spec/unit/xlsx/xform/drawing/data/drawing.1.4.js index ca9bc70f7..f4cbc582b 100644 --- a/spec/unit/xlsx/xform/drawing/data/drawing.1.4.json +++ b/spec/unit/xlsx/xform/drawing/data/drawing.1.4.js @@ -1,4 +1,6 @@ -{ +var Anchor = require('../../../../../../lib/doc/anchor'); + +module.exports = { "anchors":[ { "range": "A1:C7", @@ -8,12 +10,12 @@ }, { "range": { - "tl": {"row": 2.5, "col": 5.5}, - "br": {"row": 10.5, "col": 8.5} + "tl": new Anchor({"row": 2.5, "col": 5.5}), + "br": new Anchor({"row": 10.5, "col": 8.5}), }, "picture": { "rId": "rId2" } } ] -} \ No newline at end of file +}; \ No newline at end of file diff --git a/spec/unit/xlsx/xform/drawing/drawing-xform.spec.js b/spec/unit/xlsx/xform/drawing/drawing-xform.spec.js index e1d95b327..d144375b4 100644 --- a/spec/unit/xlsx/xform/drawing/drawing-xform.spec.js +++ b/spec/unit/xlsx/xform/drawing/drawing-xform.spec.js @@ -18,11 +18,11 @@ var expectations = [ { title: 'Drawing 1', create: function() { return new DrawingXform({tag: 'xdr:from'}); }, - initialModel: require('./data/drawing.1.0.json'), - preparedModel: require('./data/drawing.1.1.json'), + initialModel: require('./data/drawing.1.0.js'), + preparedModel: require('./data/drawing.1.1.js'), xml: fs.readFileSync(__dirname + '/data/drawing.1.2.xml').toString(), - parsedModel: require('./data/drawing.1.3.json'), - reconciledModel: require('./data/drawing.1.4.json'), + parsedModel: require('./data/drawing.1.3.js'), + reconciledModel: require('./data/drawing.1.4.js'), tests: ['prepare', 'render', 'renderIn', 'parse', 'reconcile'], options, } diff --git a/spec/unit/xlsx/xform/test-xform-helper.js b/spec/unit/xlsx/xform/test-xform-helper.js index 01082cffe..0bd08aaeb 100644 --- a/spec/unit/xlsx/xform/test-xform-helper.js +++ b/spec/unit/xlsx/xform/test-xform-helper.js @@ -34,7 +34,7 @@ var its = { var xform = expectation.create(); xform.prepare(model, expectation.options); - expect(model).to.deep.equal(result); + expect(_.cloneDeep(model)).to.deep.equal(result); resolve(); }); }); From c8e2ce5190a3b627a33c90a7fee1358351443440 Mon Sep 17 00:00:00 2001 From: Pawel Siemienik Date: Wed, 16 Jan 2019 22:52:14 +0100 Subject: [PATCH 2/2] fix --- lib/doc/worksheet.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/doc/worksheet.js b/lib/doc/worksheet.js index 9dd4f8a53..342ea18d3 100644 --- a/lib/doc/worksheet.js +++ b/lib/doc/worksheet.js @@ -631,13 +631,12 @@ Worksheet.prototype = { this.views = value.views; this.autoFilter = value.autoFilter; this._media = value.media; - - var self = this; + this._media .filter(m => m.type === 'image' && typeof m.range !== 'string') .forEach(i => { - i.range.tl.worksheet = self; - i.range.br.worksheet = self; + i.range.tl.worksheet = this; + i.range.br.worksheet = this; }) } };