Skip to content

Commit 915d91d

Browse files
author
Pawel Siemienik
committed
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
1 parent 3e4abde commit 915d91d

18 files changed

+437
-121
lines changed

index.d.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -759,10 +759,31 @@ export interface Image {
759759
filename?: string;
760760
buffer?: Buffer;
761761
}
762-
762+
export interface IAnchor {
763+
col: number;
764+
row: number;
765+
nativeCol: number;
766+
nativeRow: number;
767+
nativeColOff: number;
768+
nativeRowOff: number;
769+
}
770+
export class Anchor implements IAnchor{
771+
col: number;
772+
nativeCol: number;
773+
nativeColOff: number;
774+
nativeRow: number;
775+
nativeRowOff: number;
776+
row: number;
777+
778+
private readonly colWidth: number;
779+
private readonly rowHeight: number;
780+
worksheet: Worksheet;
781+
782+
constructor(model: IAnchor|object = {});
783+
}
763784
export interface ImageRange {
764-
tl: { col: number; row: number };
765-
br: { col: number; row: number };
785+
tl: { col: number; row: number } | Anchor;
786+
br: { col: number; row: number } | Anchor;
766787
}
767788

768789
export interface Range extends Location {

lib/doc/anchor.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Copyright (c) 2018 Paweł Siemienik
3+
* LICENCE: MIT - please refer to LICENCE file included with this module
4+
* or https://github.com/guyonroche/exceljs/blob/master/LICENSE
5+
*/
6+
7+
'use strict';
8+
9+
var Anchor = module.exports = function (model = {}) {
10+
this.nativeCol = model.nativeCol || 0;
11+
this.nativeColOff = model.nativeColOff || 0;
12+
this.nativeRow = model.nativeRow || 0;
13+
this.nativeRowOff = model.nativeRowOff || 0;
14+
15+
if (model.col) {
16+
this.col = model.col;
17+
}
18+
19+
if (model.row) {
20+
this.row = model.row;
21+
}
22+
};
23+
24+
Anchor.asInstance = function (model) {
25+
return model instanceof Anchor || model == null ? model : new Anchor(model);
26+
};
27+
28+
Anchor.prototype = {
29+
worksheet: null,
30+
get col() {
31+
return this.nativeCol + Math.min(this.colWidth - 1, this.nativeColOff) / this.colWidth;
32+
},
33+
set col(v) {
34+
if (v === this.col) return;
35+
this.nativeCol = Math.floor(v);
36+
this.nativeColOff = Math.floor((v - this.nativeCol) * this.colWidth);
37+
},
38+
get row() {
39+
return this.nativeRow + Math.min(this.rowHeight - 1, this.nativeRowOff) / this.rowHeight;
40+
},
41+
set row(v) {
42+
if (v === this.row) return;
43+
this.nativeRow = Math.floor(v);
44+
this.nativeRowOff = Math.floor((v - this.nativeRow) * this.rowHeight);
45+
},
46+
get colWidth() {
47+
return this.worksheet && this.worksheet.getColumn(this.nativeCol + 1) && this.worksheet.getColumn(this.nativeCol + 1).isCustomWidth ?
48+
Math.floor(this.worksheet.getColumn(this.nativeCol + 1).width * 10000) :
49+
640000;
50+
},
51+
get rowHeight() {
52+
return this.worksheet && this.worksheet.getRow(this.nativeRow + 1) && this.worksheet.getRow(this.nativeRow + 1).height ?
53+
Math.floor(this.worksheet.getRow(this.nativeRow + 1).height * 10000) :
54+
180000;
55+
}
56+
};

lib/doc/worksheet.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ var Range = require('./range');
1313
var Row = require('./row');
1414
var Column = require('./column');
1515
var Enums = require('./enums');
16+
var Anchor = require('./anchor');
1617
var DataValidations = require('./data-validations');
1718

1819
// Worksheet requirements
@@ -515,6 +516,16 @@ Worksheet.prototype = {
515516
// =========================================================================
516517
// Images
517518
addImage: function addImageToCells(imageId, range) {
519+
if (range.tl && !(range.tl instanceof Anchor)) {
520+
range.tl = new Anchor(range.tl);
521+
range.tl.worksheet = this;
522+
}
523+
524+
if (range.br && !(range.br instanceof Anchor)) {
525+
range.br = new Anchor(range.br);
526+
range.br.worksheet = this;
527+
}
528+
518529
this._media.push({
519530
type: 'image',
520531
imageId,
@@ -620,5 +631,13 @@ Worksheet.prototype = {
620631
this.views = value.views;
621632
this.autoFilter = value.autoFilter;
622633
this._media = value.media;
634+
635+
var self = this;
636+
this._media
637+
.filter(m => m.type === 'image' && typeof m.range !== 'string')
638+
.forEach(i => {
639+
i.range.tl.worksheet = self;
640+
i.range.br.worksheet = self;
641+
})
623642
}
624643
};

lib/xlsx/xform/drawing/cell-position-xform.js

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
var utils = require('../../../utils/utils');
1010
var BaseXform = require('../base-xform');
1111
var IntegerXform = require('../simple/integer-xform');
12+
var Anchor = require('../../../doc/anchor');
1213

1314
var CellPositionXform = module.exports = function(options) {
1415
this.tag = options.tag;
@@ -19,21 +20,21 @@ var CellPositionXform = module.exports = function(options) {
1920
'xdr:rowOff': new IntegerXform({tag: 'xdr:rowOff', zero: true})
2021
};
2122
};
23+
CellPositionXform.buildModel = function (model) {
24+
return new Anchor(model);
25+
};
26+
2227

2328
utils.inherits(CellPositionXform, BaseXform, {
2429

2530
render: function(xmlStream, model) {
2631
xmlStream.openNode(this.tag);
2732

28-
var col = Math.floor(model.col);
29-
var colOff = Math.floor((model.col - col) * 640000);
30-
this.map['xdr:col'].render(xmlStream, col);
31-
this.map['xdr:colOff'].render(xmlStream, colOff);
33+
this.map['xdr:col'].render(xmlStream, model.nativeCol);
34+
this.map['xdr:colOff'].render(xmlStream, model.nativeColOff);
3235

33-
var row = Math.floor(model.row);
34-
var rowOff = Math.floor((model.row - row) * 180000);
35-
this.map['xdr:row'].render(xmlStream, row);
36-
this.map['xdr:rowOff'].render(xmlStream, rowOff);
36+
this.map['xdr:row'].render(xmlStream, model.nativeRow);
37+
this.map['xdr:rowOff'].render(xmlStream, model.nativeRowOff);
3738

3839
xmlStream.closeNode();
3940
},
@@ -72,10 +73,12 @@ utils.inherits(CellPositionXform, BaseXform, {
7273
}
7374
switch (name) {
7475
case this.tag:
75-
this.model = {
76-
col: this.map['xdr:col'].model + (this.map['xdr:colOff'].model / 640000),
77-
row: this.map['xdr:row'].model + (this.map['xdr:rowOff'].model / 180000)
78-
};
76+
this.model = CellPositionXform.buildModel({
77+
nativeCol: this.map['xdr:col'].model,
78+
nativeColOff: this.map['xdr:colOff'].model,
79+
nativeRow: this.map['xdr:row'].model,
80+
nativeRowOff: this.map['xdr:rowOff'].model
81+
});
7982
return false;
8083
default:
8184
// not quite sure how we get here!

lib/xlsx/xform/drawing/two-cell-anchor-xform.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ var utils = require('../../../utils/utils');
1010
var colCache = require('../../../utils/col-cache');
1111
var BaseXform = require('../base-xform');
1212
var StaticXform = require('../static-xform');
13+
var Anchor = require('../../../doc/anchor');
1314

1415
var CellPositionXform = require('./cell-position-xform');
1516
var PicXform = require('./pic-xform');
@@ -33,16 +34,18 @@ utils.inherits(TwoCellAnchorXform, BaseXform, {
3334
if (typeof model.range === 'string') {
3435
var range = colCache.decode(model.range);
3536
// Note - zero based
36-
model.tl = {
37-
col: range.left - 1,
38-
row: range.top - 1
39-
};
37+
model.tl = CellPositionXform.buildModel({
38+
nativeCol: range.left - 1,
39+
nativeRow: range.top - 1
40+
});
4041
// zero based but also +1 to cover to bottom right of br cell
41-
model.br = {
42-
col: range.right,
43-
row: range.bottom
44-
};
42+
model.br = CellPositionXform.buildModel({
43+
nativeCol: range.right,
44+
nativeRow: range.bottom
45+
});
4546
} else {
47+
model.range.tl = Anchor.asInstance(model.range.tl);
48+
model.range.br = Anchor.asInstance(model.range.br);
4649
model.tl = model.range.tl;
4750
model.br = model.range.br;
4851
}
@@ -121,6 +124,10 @@ utils.inherits(TwoCellAnchorXform, BaseXform, {
121124
model.medium = options.media[mediaId];
122125
}
123126
}
127+
128+
model.tl = Anchor.asInstance(model.tl);
129+
model.br = Anchor.asInstance(model.br);
130+
124131
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)) {
125132
model.range = colCache.encode(model.tl.row + 1, model.tl.col + 1, model.br.row, model.br.col);
126133
} else {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "exceljs",
3-
"version": "1.6.3",
3+
"version": "1.6.4",
44
"description": "Excel Workbook Manager - Read and Write xlsx and csv Files.",
55
"private": false,
66
"license": "MIT",

spec/integration/data/images.xlsx

23.2 KB
Binary file not shown.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy