一、前言
项目中需要excel文档在线编辑的功能,查询资料后,选择了Luckysheet,因为luckysheet可以方便获取表格中的数据,传给后端。文档导出功能使用的是exceljs插件,导出代码参考这篇文章(luckysheet导出excel表格(使用exceljs,支持图片)-CSDN博客),使用过程中发现导出的文档中的图片出现不展示或者错位的问题,在此记录一下解决方案。
二、正文
1、不显示
初始的导出代码用的是指定图片宽高的方式
worksheet.addImage(imageId, {
tl: { col: 0, row: 0 }, //图片左上角坐标
ext: { width: 500, height: 200 } //图片宽高(px)
});
完整代码为
var setImages = function (table, worksheet, workbook) {
let {
images,
visibledatacolumn,//所有行的位置
visibledatarow //所有列的位置
} = {...table}
if (typeof images != 'object') return;
for (let key in images) {
// 通过 base64 将图像添加到工作簿
const myBase64Image = images[key].src;
//开始行 开始列 结束行 结束列
const item = images[key];
const imageId = workbook.addImage({
base64: myBase64Image,
extension: 'png'
});
const col_st = getImagePosition(item.default.left,visibledatacolumn);
const row_st = getImagePosition(item.default.top,visibledatarow);
//模式1,图片左侧与luckysheet位置一样,像素比例保持不变,但是,右侧位置可能与原图所在单元格不一致
worksheet.addImage(imageId, {
tl: { col: col_st, row: row_st},
ext: { width: item.default.width, height: item.default.height },
});
//模式2,图片四个角位置没有变动,但是图片像素比例可能和原图不一样
// const w_ed = item.default.left+item.default.width;
// const h_ed = item.default.top+item.default.height;
// const col_ed = getImagePosition(w_ed,visibledatacolumn);
// const row_ed = getImagePosition(h_ed,visibledatarow);
// worksheet.addImage(imageId, {
// tl: { col: col_st, row: row_st},
// br: { col: col_ed, row: row_ed},
// });
}
};
本身代码没有问题,图片不显示是因为luckysheet只处理了模式二,就是需要指定图片的左上角和右下角的位置
// 在 B2:D6 的一部分上插入图像
worksheet.addImage(imageId2, {
tl: { col: 1.5, row: 1.5 },
br: { col: 3.5, row: 5.5 }
});
luckysheet源码
位置:node_modules/luckyexcel/dist/luckyexcel.cjs.js
var xdrFrom = xdrFroms[0], xdrTo = xdrTos[0], xdr_blipfill = xdr_blipfills[0];
var rembed = getXmlAttibute(xdr_blipfill.attributeList, "r:embed", null);
var imageObject = _this.getBase64ByRid(rembed, drawingRelsFile);
// let aoff = xdr_xfrm.getInnerElements("a:off"), aext = xdr_xfrm.getInnerElements("a:ext");
// if(aoff!=null && aext!=null && aoff.length>0 && aext.length>0){
// let aoffAttribute = aoff[0].attributeList, aextAttribute = aext[0].attributeList;
// let x = getXmlAttibute(aoffAttribute, "x", null);
// let y = getXmlAttibute(aoffAttribute, "y", null);
// let cx = getXmlAttibute(aextAttribute, "cx", null);
// let cy = getXmlAttibute(aextAttribute, "cy", null);
// if(x!=null && y!=null && cx!=null && cy!=null && imageObject !=null){
// let x_n = getPxByEMUs(parseInt(x), "c"),y_n = getPxByEMUs(parseInt(y));
// let cx_n = getPxByEMUs(parseInt(cx), "c"),cy_n = getPxByEMUs(parseInt(cy));
var x_n = 0, y_n = 0;
var cx_n = 0, cy_n = 0;
imageObject.fromCol = _this.getXdrValue(xdrFrom.getInnerElements("xdr:col"));
imageObject.fromColOff = getPxByEMUs(_this.getXdrValue(xdrFrom.getInnerElements("xdr:colOff")));
imageObject.fromRow = _this.getXdrValue(xdrFrom.getInnerElements("xdr:row"));
imageObject.fromRowOff = getPxByEMUs(_this.getXdrValue(xdrFrom.getInnerElements("xdr:rowOff")));
imageObject.toCol = _this.getXdrValue(xdrTo.getInnerElements("xdr:col"));
imageObject.toColOff = getPxByEMUs(_this.getXdrValue(xdrTo.getInnerElements("xdr:colOff")));
imageObject.toRow = _this.getXdrValue(xdrTo.getInnerElements("xdr:row"));
imageObject.toRowOff = getPxByEMUs(_this.getXdrValue(xdrTo.getInnerElements("xdr:rowOff")));
imageObject.originWidth = cx_n;
imageObject.originHeight = cy_n;
可以看到ext的处理代码被注释掉了,图片要展示的话必须要有fromCol、formRow、toCol、toRow四个参数,也就是图片左上角和右下角在excel中的坐标。所以使用前文导出代码中的模式二导出后,在luckysheet导入就能看到图片了。
2、图片错位
图片错位是exceljs的问题,excel中的图片计算出来的坐标有可能是浮点数,而exceljs插入图片,浮点数的话会出现错位,github上找到了解决方案,详情见(github.com/exceljs/exc… 设置tl和br的时候,需要传入nativeCol,nativeRow,nativeColOff,nativeRowOff四个参数,可以省略col和row,nativeCol和nativeRow表示图片所在单元格的行和列,值是整数,nativeColOff和nativeRowOff是锚点在单元格中的偏移量,单位为EMU。(单位换算:ExcelJsColWidthInEMU = ExcelJsColWidth * 10000;ExcelJsrowHeightInEMU = ExcelJsRowHeight * 10000)
改造setImages方法:
var setImages = function (table, worksheet, workbook) {
let {
images,
visibledatacolumn,//所有行的位置
visibledatarow //所有列的位置
} = { ...table }
if (typeof images != 'object') return;
for (let key in images) {
// 通过 base64 将图像添加到工作簿
const myBase64Image = images[key].src;
//开始行 开始列 结束行 结束列
const item = images[key];
const imageId = workbook.addImage({
base64: myBase64Image,
extension: 'png'
});
// 只有设置tl、br的图片可以被luckysheet识别并展示,设置ext宽高的不行
const col_st = getImagePosition(item.default.left, visibledatacolumn);
const row_st = getImagePosition(item.default.top, visibledatarow);
const w_ed = item.default.left + item.default.width;
const h_ed = item.default.top + item.default.height;
const col_ed = getImagePosition(w_ed, visibledatacolumn);
const row_ed = getImagePosition(h_ed, visibledatarow);
// 需要设置nativeCol,nativeColOff,nativeRow,nativeRowOff,可以省略col,row
worksheet.addImage(imageId, {
tl: { nativeCol: col_st.native, nativeColOff: col_st.nativeOff, nativeRow: row_st.native, nativeRowOff: row_st.nativeOff },
br: { nativeCol: col_ed.native, nativeColOff: col_ed.nativeOff, nativeRow: row_ed.native, nativeRowOff: row_ed.nativeOff },
editAs: 'oneCell',
});
}
};
改造getImagePosition方法
/**
* 计算获取图片的坐标
* @param {number} num 坐标点的top,left值
* @param {array} arr 所有的行、列的长度集合
* @returns 满足exceljs的坐标值,需要计算图片在cell中的偏移量nativeOff。
*/
var getImagePosition = function (num, arr) {
for (let i = 0; i < arr.length; i++) {
const item = arr[i]
if (num < item) {
const cell = i > 0 ? arr[i] - arr[i - 1]:item
// 偏移量单位为Emu,所以单元格的宽高需要转换为Emu的单位,cellWidth = cellWidth*10000
const cellInEmu = cell * 10000
const rowOrCol = i > 0 ? (num - arr[i - 1]) / (arr[i] - arr[i - 1]) + i:num/item
const native = Math.floor(rowOrCol)
const nativeOff = parseInt(new Big(rowOrCol).minus(native).toNumber() * cellInEmu)
return { rowOrCol, native, nativeOff }
}
}
}
注意:浮点数加减不能直接运算,直接运算可能会出现精度缺失,我使用的是big.js插件,偏移量要是整数,需要使用parseInt方法转换一下。