Open ThibautSF opened 3 years ago
Import functions Known bugs:
If spreadsheet doesn't start at column 1, merged cells will be incorrect (due to XLSX.utils.sheet_to_json 'trimming' empty columns)
XLSX.utils.sheet_to_json
will trim empty rows and columns for it transform data for a specified range, default is safe_decode_range(sheet["!ref"]), for example, if sheet["!ref"] === 'B2:C3', the range is start at (1, 1), then the first row and columns was ignored:
So, we can specify range start at (0, 0) manually, such as:
……
const sheet: any = { name: name, rows: {} }
if (!ws || !ws['!ref']) {
console.error('!ref not found for worksheet')
return sheet
}
const range = utils.decode_range(ws['!ref'])
if (!range || !range.e) {
console.error(
'decode_range for !ref failed, worksheet !ref is: ',
ws['!ref']
)
return sheet
}
// sheet_to_json will lost empty row and col at begin as default
range.s = { r: 0, c: 0 }
const aoa = XLSX.utils.sheet_to_json(ws, {
raw: false,
header: 1,
range: range,
})
……
@ThibautSF please raise an issue / send a PR to the actual demo. https://github.com/SheetJS/sheetjs/blob/master/demos/xspreadsheet/xlsxspread.js the import and export functions have been moved into their own script
Ok, I will do that. About functions in "Shared dependencies" should I add them in this same file too?
And I suppose that I need to update Readme too.
columnToLetter(col)
is XLSX.utils.encode_col(col - 1)
coordinateToReference(row, col)
is either XLSX.utils.encode_col(col-1) + String(row)
or XLSX.utils.encode_cell({r: row - 1, c: col - 1})
Ok thanks
(The code samples in the README can be removed. Instead, it should state that the xtos
and stox
are defined in xlsxspread.js
)
Issue and PR created on SheetJS/sheetjs#2410
Code updated here too (0.2) to incorporate suggestions by @Liyaa and @reviewher (and in order to be even with PR).
Is this any update for export excel with styles?
maybe now some updates ?
I used 'xlsx-js-style', which extends 'xlsx' and provides to export styles. The data structure of x-spreadsheet is different from the 'xlsx-js-style' input and I made a transformation.
Known bug:
xtos(sdata: any[], keepMerges = true, keepFormulas = true) {
function transformStyle(styleObj: Object):Object {
const result = {};
if (styleObj)
Object.keys(styleObj).map(key => {
switch (key) {
case 'align':
result['alignment'] = Object.assign(result['alignment'] || {}, { horizontal: styleObj[key] });
break;
case 'valign':
result['alignment'] = Object.assign(result['alignment'] || {}, { vertical: styleObj[key] });
break;
case 'font':
result['font'] = Object.assign(result['font'] || {}, styleObj[key]);
if (result['font'] && result['font']['size'])
result['font'] = Object.assign(result['font'] || {}, { sz: styleObj[key]['size'] });
break;
case 'underline':
result['font'] = Object.assign(result['font'] || {}, { underline: styleObj[key] });
break;
case 'strike':
result['font'] = Object.assign(result['font'] || {}, { strike: styleObj[key] });
break;
case 'color':
result['font'] = Object.assign(result['font'] || {}, { color: { rgb: styleObj[key].slice(1) } });
break;
case 'bgcolor':
// result["fill"] = Object.assign((result["fill"] || {}), { bgColor: { rgb: styleObj[key].slice(1) },patternType:"solid" });
result['fill'] = Object.assign(result['fill'] || {}, { fgColor: { rgb: styleObj[key].slice(1) }, patternType: 'solid' });
break;
case 'border':
result['border'] = Object.assign(result['border'] || {}, {
top: styleObj[key]['top'] ? { style: styleObj[key]['top'][0], color: { rgb: styleObj[key]['top'][1].slice(1) } } : null,
bottom: styleObj[key]['bottom']
? { style: styleObj[key]['bottom'][0], color: { rgb: styleObj[key]['bottom'][1].slice(1) } }
: null,
left: styleObj[key]['left'] ? { style: styleObj[key]['left'][0], color: { rgb: styleObj[key]['left'][1].slice(1) } } : null,
right: styleObj[key]['right']
? { style: styleObj[key]['right'][0], color: { rgb: styleObj[key]['right'][1].slice(1) } }
: null
});
break;
case 'textwrap':
result['alignment'] = Object.assign(result['alignment'] || {}, { wrapText: styleObj[key] });
break;
default:;
}
});
return result;
}
function formatText(styleObj: Object) :string {
let format = '';
if (styleObj)
Object.keys(styleObj).map(key => {
if ('format' === key) {
switch (styleObj[key]) {
case 'scientific':
format = '0.00E+0';
break;
case 'percent':
format = '0.00%';
break;
case 'number':
format = '0.00';
break;
default:
}
}
});
return format;
}
const out = XLSX.utils.book_new();
sdata.forEach((xws)=> {
const ws = {};
const rowobj = xws.rows;
let minCoord: { r: number; c: number } | undefined = { r: 0, c: 0 },
maxCoord: { r: number; c: number } | undefined = { r: 0, c: 0 };
for (let ri = 0; ri < rowobj.len; ++ri) {
const row = rowobj[ri];
if (!row) continue;
Object.keys(row.cells).forEach((k)=> {
const idx = +k;
if (isNaN(idx)) return;
const lastRef = XLSX.utils.encode_cell({ r: ri, c: idx });
if (minCoord === undefined) {
minCoord = { r: ri, c: idx };
} else {
if (ri < minCoord.r) minCoord.r = ri;
if (idx < minCoord.c) minCoord.c = idx;
}
if (maxCoord === undefined) {
maxCoord = { r: ri, c: idx };
} else {
if (ri > maxCoord.r) maxCoord.r = ri;
if (idx > maxCoord.c) maxCoord.c = idx;
}
let cellText = row.cells[k].text,
type = 's';
if (!cellText) {
cellText = '';
// keep style and format
const styleIndex = row.cells[k].style
if(undefined === styleIndex || null === styleIndex){
type = 'z'; // type 'z' will not export style
}
if(undefined !== styleIndex && xws.styles[styleIndex]['format'] && 'scientific' === xws.styles[styleIndex]['format']){
cellText = 0;
type = 'n';
}
} else if (!isNaN(parseFloat(cellText))) {
cellText = parseFloat(cellText);
type = 'n';
} else if (cellText === 'true' || cellText === 'false') {
cellText = Boolean(cellText);
type = 'b';
}
ws[lastRef] = {
v: cellText,
t: type,
z: formatText(xws.styles[row.cells[k].style]),
s: transformStyle(xws.styles[row.cells[k].style])
};
if (keepFormulas && type === 's' && cellText[0] === '=') {
ws[lastRef].f = cellText.slice(1);
}
if (keepMerges && row.cells[k].merge !== undefined) {
if (ws['!merges'] === undefined) ws['!merges'] = [];
ws['!merges'].push({
s: {
r: ri,
c: idx
},
e: {
r: ri + row.cells[k].merge[0],
c: idx + row.cells[k].merge[1]
}
});
}
});
ws['!ref'] =
XLSX.utils.encode_cell({ r: minCoord.r, c: minCoord.c }) + ':' + XLSX.utils.encode_cell({ r: maxCoord.r, c: maxCoord.c });
}
XLSX.utils.book_append_sheet(out, ws, xws.name);
});
return out;
}
It miss a right click: insert row and column before|after
I used 'xlsx-js-style', which extends 'xlsx' and provides to export styles. The data structure of x-spreadsheet is different from the 'xlsx-js-style' input and I made a transformation.
Known bug:
- border mistake of merged cells.
xtos(sdata: any[], keepMerges = true, keepFormulas = true) { function transformStyle(styleObj: Object):Object { const result = {}; if (styleObj) Object.keys(styleObj).map(key => { switch (key) { case 'align': result['alignment'] = Object.assign(result['alignment'] || {}, { horizontal: styleObj[key] }); break; case 'valign': result['alignment'] = Object.assign(result['alignment'] || {}, { vertical: styleObj[key] }); break; case 'font': result['font'] = Object.assign(result['font'] || {}, styleObj[key]); if (result['font'] && result['font']['size']) result['font'] = Object.assign(result['font'] || {}, { sz: styleObj[key]['size'] }); break; case 'underline': result['font'] = Object.assign(result['font'] || {}, { underline: styleObj[key] }); break; case 'strike': result['font'] = Object.assign(result['font'] || {}, { strike: styleObj[key] }); break; case 'color': result['font'] = Object.assign(result['font'] || {}, { color: { rgb: styleObj[key].slice(1) } }); break; case 'bgcolor': // result["fill"] = Object.assign((result["fill"] || {}), { bgColor: { rgb: styleObj[key].slice(1) },patternType:"solid" }); result['fill'] = Object.assign(result['fill'] || {}, { fgColor: { rgb: styleObj[key].slice(1) }, patternType: 'solid' }); break; case 'border': result['border'] = Object.assign(result['border'] || {}, { top: styleObj[key]['top'] ? { style: styleObj[key]['top'][0], color: { rgb: styleObj[key]['top'][1].slice(1) } } : null, bottom: styleObj[key]['bottom'] ? { style: styleObj[key]['bottom'][0], color: { rgb: styleObj[key]['bottom'][1].slice(1) } } : null, left: styleObj[key]['left'] ? { style: styleObj[key]['left'][0], color: { rgb: styleObj[key]['left'][1].slice(1) } } : null, right: styleObj[key]['right'] ? { style: styleObj[key]['right'][0], color: { rgb: styleObj[key]['right'][1].slice(1) } } : null }); break; case 'textwrap': result['alignment'] = Object.assign(result['alignment'] || {}, { wrapText: styleObj[key] }); break; default:; } }); return result; } function formatText(styleObj: Object) :string { let format = ''; if (styleObj) Object.keys(styleObj).map(key => { if ('format' === key) { switch (styleObj[key]) { case 'scientific': format = '0.00E+0'; break; case 'percent': format = '0.00%'; break; case 'number': format = '0.00'; break; default: } } }); return format; } const out = XLSX.utils.book_new(); sdata.forEach((xws)=> { const ws = {}; const rowobj = xws.rows; let minCoord: { r: number; c: number } | undefined = { r: 0, c: 0 }, maxCoord: { r: number; c: number } | undefined = { r: 0, c: 0 }; for (let ri = 0; ri < rowobj.len; ++ri) { const row = rowobj[ri]; if (!row) continue; Object.keys(row.cells).forEach((k)=> { const idx = +k; if (isNaN(idx)) return; const lastRef = XLSX.utils.encode_cell({ r: ri, c: idx }); if (minCoord === undefined) { minCoord = { r: ri, c: idx }; } else { if (ri < minCoord.r) minCoord.r = ri; if (idx < minCoord.c) minCoord.c = idx; } if (maxCoord === undefined) { maxCoord = { r: ri, c: idx }; } else { if (ri > maxCoord.r) maxCoord.r = ri; if (idx > maxCoord.c) maxCoord.c = idx; } let cellText = row.cells[k].text, type = 's'; if (!cellText) { cellText = ''; // keep style and format const styleIndex = row.cells[k].style if(undefined === styleIndex || null === styleIndex){ type = 'z'; // type 'z' will not export style } if(undefined !== styleIndex && xws.styles[styleIndex]['format'] && 'scientific' === xws.styles[styleIndex]['format']){ cellText = 0; type = 'n'; } } else if (!isNaN(parseFloat(cellText))) { cellText = parseFloat(cellText); type = 'n'; } else if (cellText === 'true' || cellText === 'false') { cellText = Boolean(cellText); type = 'b'; } ws[lastRef] = { v: cellText, t: type, z: formatText(xws.styles[row.cells[k].style]), s: transformStyle(xws.styles[row.cells[k].style]) }; if (keepFormulas && type === 's' && cellText[0] === '=') { ws[lastRef].f = cellText.slice(1); } if (keepMerges && row.cells[k].merge !== undefined) { if (ws['!merges'] === undefined) ws['!merges'] = []; ws['!merges'].push({ s: { r: ri, c: idx }, e: { r: ri + row.cells[k].merge[0], c: idx + row.cells[k].merge[1] } }); } }); ws['!ref'] = XLSX.utils.encode_cell({ r: minCoord.r, c: minCoord.c }) + ':' + XLSX.utils.encode_cell({ r: maxCoord.r, c: maxCoord.c }); } XLSX.utils.book_append_sheet(out, ws, xws.name); }); return out; }
请问有对应的stox方法么 感谢
Introduction
Hi,
As I'm working on an x-spreadsheet integration, and would like to also allow xlsx import/export I started to work also with SheetJS. Using suggesting codes from SheetJS demo shows that some informations are lost during the import&export.
I observed the lost of :
Thus I started to work on improving import/export functions. Feel free to use those functions, make more tests and repost any bug-less versions, or optimize (either for browser support or better performances). I will try to keep track of updated versions here.
Import functions
Known bugs:
XLSX.utils.sheet_to_json
'trimming' empty columns)Version: 0.2 (basic semantic to keep track)
Note:
stox(wb)
orstox(wb, false, false)
will give the same result as original function.keepMerges=true
all merged cells in spreadsheet file will be transferred to x-spreadsheet instance.keepFormulas=true
all formulas in spreadsheet file will be written as is in their cells (instead of their interpreted value)Export functions
Known bugs:
Version: 0.2 (basic semantic to keep track)
Note:
xtos(sdata)
orxtos(sdata, false, false)
will give the same result as original function (Note: cell type are updated).keepMerges=true
all merged cells in x-spreadsheet instance will be transferred to spreadsheet file.keepFormulas=true
all formulas in x-spreadsheet instance will be written as is in their cellsIssues for reference
8
20
55
112
157
SheetJS/sheetjs#2165
Miscellaneous
Old functions (not used anymore) but kept in case it might be interesting for someone:
columnToLetter(column)
calls can be replaced byXLSX.utils.encode_col(column - 1)
coordinateToReference(row, col)
calls can be replaced byXLSX.utils.encode_cell({r: row - 1, c: col - 1})
1 Styles are obtained only with SheetJS Pro, as I don't own a license yet, I won't be able to make a version with styles import/export. ↩