import type { AsTableOption, ConditionalFormatting, ExcelTable, Formula, HeaderFooterOption, MergeRowConditionMap, ProtectionOptionKey, RowMap, StyleMapper, Buffer, SheetProcessResult, CellStrReference, ShapeRC, Data, ExcelTableReturnType, } from "../data-model/excel-table"; import { generateColumnName } from "../utils/generate-column-name"; import { styleGenerator } from "../utils/content-generator/styles"; import { contentTypeGenerator } from "../utils/content-generator/content-types"; import { appGenerator } from "../utils/content-generator/app"; import { generateCellRowCol } from "../utils/generate-formula-cell"; import { convertToHex } from "../utils/color"; import { commentConvertor, defaultCellCommentStyle, generateCommentTag, } from "../utils/comment"; import { generateMultiStyleByArray } from "../utils/multi-value"; import { cols as colsDef, formatMap as defaultFormatMap, } from "../data-model/const-data"; import { toDataURL2 } from "../utils/image"; import { getColRowBaseOnRefString } from "../utils/excel-util"; import { specialCharacterConverter } from "../utils/special-character"; import type JSZip from "jszip"; import { generateDropDown } from "../utils/drop-down-utils"; import { processDataValidation } from "../utils/data-validation.utils"; export async function generateExcel( data: ExcelTable ): ExcelTableReturnType { if (typeof data.creator == "string" && data.creator.trim().length <= 0) { throw 'length of "creator" most be bigger then 0'; } if ( typeof data.created == "string" && new Date(data.created).toString() == "Invalid Date" ) { throw '"created" is not valid date'; } if ( typeof data.modified == "string" && new Date(data.modified).toString() == "Invalid Date" ) { throw '"modified" is not valid date'; } let formatMap = defaultFormatMap; if (data.formatMap && typeof data.formatMap == "object") { formatMap = { ...formatMap, ...data.formatMap, }; } const isBackend = data.backend; const operatorMap: Record = { lt: "lessThan", gt: "greaterThan", between: "between", ct: "containsText", eq: "equal", }; let cols: string[] = [...colsDef]; if (data.numberOfColumn && data.numberOfColumn > 25) { cols = generateColumnName(cols, data.numberOfColumn); } const JSZipModule = (await import("jszip")).default; let zip = new JSZipModule(); const sheetsData = data.sheet ?? [ { headers: [], data: [], }, ]; const sheetLength = sheetsData.length; // xl const xlFolder: JSZip | null = zip.folder("xl"); let xl_media_Folder: JSZip | null | undefined = null; let xl_drawingsFolder: JSZip | null | undefined = null; let xl_drawings_relsFolder: JSZip | null | undefined = null; const inputStyle = { ...(data.styles ?? {}) }; if (data.addDefaultTitleStyle) { inputStyle["titleStyle"] = { alignment: { horizontal: "center", vertical: "center", }, }; } const styleKeys = Object.keys(inputStyle); const defaultCommentStyle = defaultCellCommentStyle; const addCF = data.activateConditionalFormatting ? data.activateConditionalFormatting : false; const headerFooterStyle: Record = {}; const cFMapIndex: Record = {}; let styleMapper: StyleMapper = styleKeys.reduce( (res: StyleMapper, cur) => { const styleObject = inputStyle[cur]; if ( styleObject?.type && (styleObject.type == "headerFooter" || styleObject.type == "HF") ) { let result = ""; let fontProcessLeft = "-"; let fontProcessRight = "Regular"; if (styleObject.fontFamily) { fontProcessLeft = styleObject.fontFamily; } if (styleObject.bold) { fontProcessRight = "Bold"; } if (styleObject.italic) { if (fontProcessRight == "Regular") { fontProcessRight = ""; } fontProcessRight += "Italic"; } if (fontProcessLeft != "-" || fontProcessRight != "Regular") { result = "&" + '"' + fontProcessLeft + "," + fontProcessRight + '"'; } if (styleObject.size) { result += "&" + styleObject.size; } if (styleObject.doubleUnderline) { result += "&E"; } else if (styleObject.underline) { result += "&U"; } if (styleObject.color) { const convertedColor = convertToHex(styleObject.color, isBackend); if (typeof convertedColor == "string" && convertedColor.length > 0) { result += "&K" + convertedColor.toUpperCase(); } } headerFooterStyle[cur] = result; return res; } if ( addCF && typeof styleObject?.type === "string" && styleObject.type && (styleObject.type == "conditionalFormatting" || styleObject.type.toUpperCase() == "CF") ) { cFMapIndex[cur] = res.conditionalFormatting.count; let color = convertToHex(styleObject.color, isBackend); let bgColor = convertToHex(styleObject.backgroundColor, isBackend); res.conditionalFormatting.value += ' '; res.conditionalFormatting.count++; return res; } const indexes = { fillIndex: 0, fontIndex: 0, borderIndex: 0, formatIndex: 0, }; if (styleObject?.backgroundColor) { let fgConvertor = convertToHex(styleObject.backgroundColor, isBackend); indexes.fillIndex = res.fill.count; res.fill.count++; res.fill.value = res.fill.value + "" + '' + (fgConvertor ? '' : "") + "" + ""; } if ( styleObject?.color || styleObject?.fontFamily || styleObject?.size || styleObject?.bold || styleObject?.italic || styleObject?.underline || styleObject?.doubleUnderline ) { const colors = convertToHex(styleObject.color, isBackend); indexes.fontIndex = res.font.count; res.font.count++; res.font.value = res.font.value + "" + (styleObject.bold ? "" : "") + (styleObject.italic ? "" : "") + (styleObject.underline || styleObject.doubleUnderline ? "" : "") + (styleObject.size ? '' : "") + (colors ? '' : "") + (styleObject.fontFamily ? '' : "") + ""; res.commentSyntax.value[cur] = "" + (styleObject.bold ? "" : "") + (styleObject.italic ? "" : "") + (styleObject.underline || styleObject.doubleUnderline ? "" : "") + '' + (colors ? '' : "") + '' + ""; } let endPart = "/>"; if (styleObject?.alignment) { let directionHasBeenSet = false; endPart = ' applyAlignment="1">' + " { if (alignmentOptionName === "rtl") { alignmentOptionName = "readingOrder"; alignmentOptionValue = 2; } else if (alignmentOptionName === "ltr") { alignmentOptionName = "readingOrder"; alignmentOptionValue = 1; } if (alignmentOptionName === "readingOrder") { directionHasBeenSet = true; } if (directionHasBeenSet) { return al; } return ( al + " " + alignmentOptionName + '="' + alignmentOptionValue + '" ' ); }, "", ) + " />" + ""; } const borderObj = styleObject?.border; let borderStr = ""; if (typeof borderObj === "object") { if (borderObj.left || borderObj.full) { borderStr += '' + '' + ""; } if (borderObj.right || borderObj.full) { borderStr += '' + '' + ""; } if (borderObj.top || borderObj.full) { borderStr += '' + '' + ""; } if (borderObj.bottom || borderObj.full) { borderStr += '' + '' + ""; } indexes.borderIndex = res.border.count; res.border.count++; res.border.value += "" + borderStr + "" + ""; } if (styleObject?.format) { const format = formatMap[styleObject.format]; if (format) { indexes.formatIndex = format.key; if ("value" in format) { res.format.count++; res.format.value += format.value; } } } res.cell.value = res.cell.value + ' 0 ? ' applyBorder="1" ' : "") + (indexes.fillIndex > 0 ? ' applyFill="1" ' : "") + (indexes.fontIndex >= 0 ? ' applyFont="1" ' : "") + (indexes.formatIndex > 0 ? ' applyNumberFormat="1" ' : "") + endPart; res.styleIndexMap[cur] = res.cell.count; res.cell.count++; return res; }, { conditionalFormatting: { count: addCF ? 1 : 0, value: ' ', }, styleIndexMap: {}, commentSyntax: { value: {}, }, format: { count: 0, value: "", }, border: { count: 1, value: "", }, fill: { count: 2, value: "", }, font: { count: 2, value: "", }, cell: { count: 2, value: "", }, }, ); xlFolder?.file("styles.xml", styleGenerator(styleMapper, addCF)); let sheetContentType = ''; let sharedString = ""; let sharedStringIndex = 0; let workbookString = ""; let workbookRelString = ""; let sharedStringMap: { [key: string]: string; } = {}; const mapData: { [key: string]: SheetProcessResult; } = {}; let sheetNameApp = ""; let indexId = 4; let selectedAdded = false; let activeTabIndex = -1; let arrTypes: string[] = []; let imageCounter = 1; const formCtrlMap = { checkbox: '\n' + '', }; let shapeIdCounter = 1024; const shapeMap = { checkbox: `
***text***
0, 2, 0, 2, 0, 86, 1, 0 False False Center
`, }; const shapeTypeMap = { checkbox: ` `, }; let checkboxForm: string[] = []; let calcChainValue = ""; let needCalcChain = false; let xl_tableFolder: JSZip | null | undefined = null; for (let index = 0; index < sheetLength; index++) { const sheetData = sheetsData[index]; if (!sheetData) { continue; } const sheetDataId = index + 1; let rowMap: RowMap = {}; let sheetDimensions: CellStrReference = { start: "", end: "", }; const asTable = sheetData.asTable; let sheetDataTableColumns = ""; let rowCount = sheetData.shiftTop && sheetData.shiftTop >= 0 ? sheetData.shiftTop + 1 : 1; let sheetDataString = ""; let sheetSizeString = ""; let sheetSortFilter = ""; let splitOption = ""; let sheetViewProperties = ""; let viewType: string = ""; let hasCheckbox = false; let checkboxDrawingContent = ""; let checkboxShape = ""; let formRel = ""; let checkboxSheetContent = ""; let mergesCellArray: string[] = Object.assign([], sheetData.merges); let formulaSheetObj: Formula = Object.assign({}, sheetData.formula); let conditionalFormatting: ConditionalFormatting[] = Object.assign( [], sheetData.conditionalFormatting, ); let hasComment = false; let commentAuthor: string[] = []; let commentString = ""; let shapeCommentRowCol: ShapeRC[] = []; let objKey: string[] = []; let headerFormula: number[] = []; let headerConditionalFormatting: number[] = []; let mergeRowConditionMap: MergeRowConditionMap = {}; let sheetHeaderFooter = ""; let isPortrait = false; let sheetBreakLine = ""; if (sheetData.zoomScale) { sheetViewProperties += (sheetData.zoomScale.startAt ? ' topLeftCell="' + sheetData.zoomScale.startAt + '" ' : "") + ' zoomScale="' + sheetData.zoomScale.scale + '" zoomScaleNormal="' + sheetData.zoomScale.scale + '" '; } if (sheetData.rtl) { sheetViewProperties += ' rightToLeft="1" '; } if (sheetData.pageBreak) { const pageBreak = sheetData.pageBreak; if (pageBreak.row && Array.isArray(pageBreak.row)) { viewType = "pageBreakPreview"; const rowLength = pageBreak.row.length; sheetBreakLine += '' + pageBreak.row.reduce( (result, current) => result + '', "", ) + ""; } if (pageBreak.column && Array.isArray(pageBreak.column)) { viewType = "pageBreakPreview"; const columnLength = pageBreak.column.length; sheetBreakLine += '' + pageBreak.column.reduce( (result, current) => result + '', "", ) + ""; } } let sheetMargin = ""; if (sheetData.pageOption) { const pageOption = sheetData.pageOption; if (pageOption.isPortrait) { isPortrait = true; } if (pageOption.margin) { const margin = pageOption.margin; let result = { left: 0.7, right: 0.7, top: 0.75, bottom: 0.75, header: 0.3, footer: 0.3, }; Object.keys(result).forEach((marginKey) => { if (typeof margin[marginKey as keyof object] == "number") { result[marginKey as keyof object] = margin[marginKey as keyof object]; } }); sheetMargin = ''; } let typeKeeper = ""; let odd = ""; let even = ""; let first = ""; const keyKey = ["header", "footer"]; keyKey.forEach((keyObj) => { const endTag = keyObj.charAt(0).toUpperCase() + keyObj.substring(1); if (pageOption[keyObj as keyof object]) { const element = pageOption[keyObj as keyof object]; if (typeof element == "object") { Object.keys(element).forEach((typeHF) => { if (typeKeeper.indexOf(typeHF) < 0) { typeKeeper += typeHF; } const typeObj = element[typeHF]; let node = ""; Object.keys(typeObj) .reduce((resultKey, currentKey) => { if (currentKey == "l") { resultKey.splice(0, 0, currentKey); } else if (currentKey == "c") { resultKey.splice(1, 0, currentKey); } else if (currentKey == "r") { resultKey.splice(2, 0, currentKey); } return resultKey; }, [] as string[]) .forEach((direction) => { const dirObj = typeObj[direction]; node += "&" + direction.toUpperCase(); if (dirObj.styleId && headerFooterStyle[dirObj.styleId]) { node += headerFooterStyle[dirObj.styleId]; } if (dirObj.text) { node += dirObj.text; } }); node = "<" + typeHF + endTag + ">" + node + ""; if (typeHF == "odd") { odd += node; } else if (typeHF == "even") { even += node; } else if (typeHF == "first") { first += node; } else { throw "type error"; } }); } } }); sheetHeaderFooter = odd + even + first; if (sheetHeaderFooter.length > 0) { isPortrait = true; const oddEvenFlag = typeKeeper.length == "oddeven".length || typeKeeper.length == "oddevenfirst".length ? ' differentOddEven="1"' : ""; const firstFlag = typeKeeper.indexOf("first") >= 0 ? ' differentFirst="1"' : ""; sheetHeaderFooter = "" + sheetHeaderFooter + ""; } } if (sheetData.viewOption) { let splitState = ""; const viewOption = sheetData.viewOption; if (viewOption.type) { viewType = viewOption.type; } if (viewOption.hideRuler) { sheetViewProperties += ' showRuler="0" '; } if (viewOption.hideGrid) { sheetViewProperties += ' showGridLines="0" '; } if (viewOption.hideHeadlines) { sheetViewProperties += ' showRowColHeaders="0" '; } let split = viewOption.splitOption; if (typeof split == "undefined") { isPortrait = false; if (typeof viewOption.frozenOption == "object") { const frozen = viewOption.frozenOption; splitState = ' state="frozen" '; if (frozen.type == "R" || frozen.type == "ROW") { let fIndex; if (typeof frozen.index == "object") { fIndex = frozen.index.r; } else { fIndex = frozen.index; } split = { startAt: { b: "A" + (fIndex + 1), }, type: "H", split: fIndex, }; } else if (frozen.type == "C" || frozen.type == "COLUMN") { let fIndex; if (typeof frozen.index == "object") { fIndex = frozen.index.c; } else { fIndex = frozen.index; } if (fIndex > cols.length - 1) { cols = generateColumnName(cols, fIndex); } //TODO split = { type: "V", startAt: { r: cols[fIndex]! + 1, }, split: fIndex, }; } else if (frozen.type == "B" || frozen.type == "BOTH") { let two = ""; let splitO; if (typeof frozen.index === "number") { splitO = frozen.index as number; if (!cols[splitO]) { cols = generateColumnName(cols, splitO); } two = cols[splitO]! + (frozen.index + 1); } else { splitO = { y: frozen.index.r, x: frozen.index.c, }; if (!cols[frozen.index.c]) { cols = generateColumnName(cols, frozen.index.c as number); } two = cols[frozen.index.c]! + (frozen.index.r + 1); } split = { startAt: { two, }, type: "B", split: splitO, }; } } } if (split) { if (split.type == "H" || split.type == "HORIZONTAL") { let ref; if (split.startAt) { ref = split.startAt.b; if (split.startAt.t) { sheetViewProperties += ' topLeftCell="' + split.startAt.t + '"'; } } if (!ref) { ref = "A1"; } splitOption = '"; } else if (split.type == "V" || split.type == "VERTICAL") { let ref; if (split.startAt) { ref = split.startAt.r; if (split.startAt.l) { sheetViewProperties += ' topLeftCell="' + split.startAt.l + '"'; } } if (!ref) { ref = "A1"; } splitOption = '"; } else { let ref; if (split.startAt) { ref = split.startAt.two; if (split.startAt.one) { sheetViewProperties += ' topLeftCell="' + split.startAt.one + '"'; } } if (!ref) { ref = "A1"; } splitOption = '"; } } } if (isPortrait) { viewType = "pageLayout"; } if (sheetData.checkbox) { hasCheckbox = true; const strFormDef = formCtrlMap["checkbox"]; sheetData.checkbox.forEach((v, i) => { let formCtlStr = strFormDef; if (v.link) { let linkAddress = getColRowBaseOnRefString(v.link, cols); formCtlStr = formCtlStr.replace( "**fmlaLink**", 'fmlaLink="$' + cols[linkAddress.col] + "$" + (linkAddress.row + 1) + '"', ); } else { formCtlStr = formCtlStr.replace("**fmlaLink**", ""); } if (v.mixed) { formCtlStr = formCtlStr.replace("**value**", 'checked="Mixed"'); } else { if (v.checked) { formCtlStr = formCtlStr.replace("**value**", 'checked="Checked"'); } else { formCtlStr = formCtlStr.replace("**value**", ""); } } if (v.threeD) { formCtlStr.replace('noThreeD="1"', ""); } checkboxForm.push(formCtlStr); shapeIdCounter++; let shapeId = index + "" + shapeIdCounter++; const sId = "_x0000_s" + shapeId; checkboxShape += shapeMap["checkbox"] .replace("***id***", sId) .replace("***text***", v.text); let from = v.startStr; let to = v.endStr; let resultVal = { start: { col: 0, row: 0, }, end: { col: 1, row: 1, }, }; if (v.col && v.row) { resultVal = { start: { col: v.col, row: v.row - 1, }, end: { col: v.col, row: v.row, }, }; } if (typeof from == "string" && from.length >= 2) { let p = getColRowBaseOnRefString(from, cols); resultVal.start = { ...p, }; resultVal.end = { col: p.col + 1, row: p.row + 1, }; } if (typeof to == "string" && to.length >= 2) { let p = getColRowBaseOnRefString(to, cols); p.row += 1; p.col += 1; resultVal.end = { ...p, }; } checkboxSheetContent += '' + resultVal.start.col + "19050" + resultVal.start.row + "19050" + resultVal.end.col + "819150" + resultVal.end.row + "0"; formRel += ''; checkboxDrawingContent += '' + resultVal.start.col + "19050" + resultVal.start.row + "19050" + resultVal.end.col + "819150" + resultVal.end.row + '0' + '' + '' + '' + '' + v.text + ""; }); } let backgroundImagePromise; if (sheetData.backgroundImage) { if (xl_media_Folder == null) { xl_media_Folder = xlFolder?.folder("media"); } const urlImg = sheetData.backgroundImage; backgroundImagePromise = new Promise(async (resolve, reject) => { let indexImageType = urlImg.lastIndexOf("."); let type; if (indexImageType > 0) { type = urlImg.substring(indexImageType + 1).toLowerCase(); if (type.length > 4) { if (type.indexOf("gif") >= 0) { type = "gif"; } else if (type.indexOf("jpg") >= 0) { type = "jpg"; } else if (type.indexOf("jpeg") >= 0) { type = "jpeg"; } else { type = "png"; } } } else { type = "png"; } const ref = imageCounter++; const name = "image" + ref + "." + type; const image = await toDataURL2(urlImg, name, isBackend, data.fetch); if (!image) { reject("image not load"); } arrTypes.push(type); resolve({ name, type, image, ref, }); }); } let imagePromise; if (sheetData.images) { if (xl_media_Folder == null) { xl_media_Folder = xlFolder?.folder("media"); } imagePromise = Promise.all([ ...sheetData.images.map(async (v, i) => { let indexImageType = v.url.lastIndexOf("."); let type; if (indexImageType > 0) { type = v.url.substring(indexImageType + 1).toLowerCase(); if (type.length > 4) { if (type.indexOf("gif") >= 0) { type = "gif"; } else if (type.indexOf("jpg") >= 0) { type = "jpg"; } else if (type.indexOf("jpeg") >= 0) { type = "jpeg"; } else { type = "png"; } } } else { type = "png"; } arrTypes.push(type); const name = "image" + imageCounter++ + "." + type; return { type, image: await toDataURL2(v.url, name, isBackend, data.fetch), obj: v, i, name, }; }), ]); } if (Array.isArray(sheetData.headers) && sheetData.headers.length) { const colsLength = sheetData.headers.length; let titleRow = ""; if (sheetData.title) { const title = sheetData.title; const commentTitle = title.comment; const top = title.shiftTop && title.shiftTop >= 0 ? title.shiftTop : 0; const sL = sheetData.shiftLeft && sheetData.shiftLeft >= 0 ? sheetData.shiftLeft : 0; const left = title.shiftLeft && title.shiftLeft + sL >= 0 ? title.shiftLeft + sL : sL; const consommeRow = title.consommeRow ? title.consommeRow - 1 : 1; const consommeCol = title.consommeCol ? title.consommeCol : colsLength; const height = consommeRow == 0 && typeof title.height == "number" ? ' ht="' + title.height + '" customHeight="1" ' : ""; const tStyle = title.styleId ? title.styleId : "titleStyle"; const refString = cols[left] + "" + (rowCount + top); mergesCellArray.push( refString + ":" + cols[left + consommeCol - 1] + (rowCount + consommeRow + top), ); if (typeof commentTitle != "undefined") { hasComment = true; const commentObj = commentConvertor( commentTitle, styleMapper.commentSyntax.value, defaultCommentStyle, ); let authorId = commentAuthor.length; if (commentObj.hasAuthor && typeof commentObj.author != "undefined") { let auth = commentObj.author.toString(); const index = commentAuthor.indexOf(auth); if (index < 0) { commentAuthor.push(auth); } else { authorId = index; } } shapeCommentRowCol.push({ row: rowCount + top - 1, col: left, }); commentString += generateCommentTag( refString, commentObj.commentStr, commentObj.commentStyle, authorId, ); } if (typeof title.text == "string") { rowMap[rowCount + top] = { startTag: '', details: '' + sharedStringIndex + "", endTag: "", }; titleRow += ''; titleRow += '' + sharedStringIndex + ""; titleRow += ""; sharedStringIndex++; sharedStringMap[title.text] = title.text; if (title.multiStyleValue && Array.isArray(title.multiStyleValue)) { sharedString += generateMultiStyleByArray( title.multiStyleValue, styleMapper.commentSyntax.value, tStyle, ); } else { sharedString += "" + specialCharacterConverter(title.text) + ""; } } rowCount += top + consommeRow + 1; } let headerStyleKey = sheetData.headerStyleKey ? sheetData.headerStyleKey : null; let shiftCount = 0; if (typeof sheetData.shiftLeft == "number" && sheetData.shiftLeft >= 0) { shiftCount = sheetData.shiftLeft; } if (asTable) { sheetDataTableColumns += ''; if (!xl_tableFolder) { xl_tableFolder = xlFolder?.folder("tables"); } } sheetDimensions.start = cols[shiftCount] + "" + rowCount; sheetDimensions.end = cols[shiftCount + sheetData.headers.length - 1] + "" + (rowCount + sheetData.data.length); sheetData.headers.forEach((v, innerIndex) => { if (asTable) { sheetDataTableColumns += ''; } if (shiftCount) { innerIndex += shiftCount; } if (v.formula) { headerFormula.push(innerIndex); } if (v.conditionalFormatting && addCF) { headerConditionalFormatting.push(innerIndex); } objKey.push(v.label); if ( sheetData.mergeRowDataCondition && typeof sheetData.mergeRowDataCondition == "function" ) { let result = sheetData.mergeRowDataCondition( v, null, innerIndex, true, ); if (result === true) { if (!cols[innerIndex]) { cols = generateColumnName(cols, innerIndex as number); } mergeRowConditionMap[cols[innerIndex]!] = { inProgress: true, start: rowCount, }; } } if ( sheetData.styleCellCondition && typeof sheetData.styleCellCondition == "function" ) { headerStyleKey = sheetData.styleCellCondition( v, v, rowCount, innerIndex, true, styleKeys, ) || headerStyleKey; } if (v.size && v.size > 0) { sheetSizeString += ''; } if (sheetData.withoutHeader) { return; } const refString = cols[innerIndex] + "" + rowCount; if (typeof sheetData.commentCondition == "function") { const checkCommentCondition = sheetData.commentCondition( v, null, v.label, rowCount, innerIndex, true, ); if ( typeof checkCommentCondition == "string" || (typeof checkCommentCondition == "object" && checkCommentCondition != null) ) { v.comment = checkCommentCondition; } } if (v.comment) { hasComment = true; const commentObj = commentConvertor( v.comment, styleMapper.commentSyntax.value, defaultCommentStyle, ); let authorId = commentAuthor.length; if (commentObj.hasAuthor && typeof commentObj.author != "undefined") { let auth = commentObj.author.toString(); const index = commentAuthor.indexOf(auth); if (index < 0) { commentAuthor.push(auth); } else { authorId = index; } } shapeCommentRowCol.push({ row: rowCount - 1, col: innerIndex, }); commentString += generateCommentTag( refString, commentObj.commentStr, commentObj.commentStyle, authorId, ); } const formula = formulaSheetObj && formulaSheetObj[refString]; if (formula) { const f = generateCellRowCol( refString, formula, sheetDataId, styleMapper.styleIndexMap, ); if (f.needCalcChain) { needCalcChain = true; calcChainValue += f.chainCell; } sheetDataString += f.cell; delete formulaSheetObj![refString]; } else { sheetDataString += '' + sharedStringIndex + ""; if (typeof sheetData.multiStyleCondition == "function") { const multi = sheetData.multiStyleCondition( v, null, v.label, rowCount, innerIndex, true, ); if (multi) { v.multiStyleValue = multi; } } if (v.multiStyleValue && Array.isArray(v.multiStyleValue)) { sharedString += generateMultiStyleByArray( v.multiStyleValue, styleMapper.commentSyntax.value, headerStyleKey ? headerStyleKey : "", ); } else { sharedString += "" + specialCharacterConverter(v.text) + ""; } sharedStringMap[v.text] = v.text; sharedStringIndex++; } }); if (asTable) { sheetDataTableColumns += ""; } if (!sheetData.withoutHeader) { const rowTag = ' { return ( res + " " + curr + '="' + sheetData.headerRowOption![curr as keyof object] + '" ' ); }, " ") : "") + ">"; rowMap[rowCount] = { startTag: rowTag, endTag: "", details: sheetDataString, }; sheetDataString = titleRow + rowTag + sheetDataString + ""; rowCount++; } else { sheetDataString += titleRow; } if (Array.isArray(sheetData.data)) { const keyOutline: keyof Data = ( sheetData.mapSheetDataOption && sheetData.mapSheetDataOption.outlineLevel ? sheetData.mapSheetDataOption.outlineLevel : "outlineLevel" ) as keyof Data; const keyHidden: keyof Data = ( sheetData.mapSheetDataOption && sheetData.mapSheetDataOption.hidden ? sheetData.mapSheetDataOption.hidden : "hidden" ) as keyof Data; const keyHeight: keyof Data = ( sheetData.mapSheetDataOption && sheetData.mapSheetDataOption.height ? sheetData.mapSheetDataOption.height : "height" ) as keyof Data; const rowLength = sheetData.data.length; sheetData.data.forEach((mData, innerIndex) => { //TODO if (mData["mergeType" as keyof T]) { for ( let iIndex = 0; iIndex < (mData["mergeType" as keyof T] as []).length; iIndex++ ) { const mergeType = mData["mergeType" as keyof object][iIndex]; const mergeStart = mData["mergeStart" as keyof object][iIndex]; const mergeValue = mData["mergeValue" as keyof object][index]; let mergeStr = ""; if (mergeType == "both") { mergeStr = cols[mergeStart] + "" + rowCount + ":" + cols[mergeStart + mergeValue[1]] + "" + (rowCount + mergeValue[0]); } else { if (mergeType == "col") { mergeStr = cols[mergeStart] + "" + rowCount + ":" + cols[mergeStart + mergeValue[0]] + "" + rowCount; } else { mergeStr = cols[mergeStart] + "" + rowCount + ":" + cols[mergeStart] + "" + (rowCount + mergeValue[0]); } } mergesCellArray.push(mergeStr); } } const rowStyle = mData.rowStyle; const rowTagStart = '", details: rowDataString, }; rowCount++; sheetDataString += ""; }); if (sheetData.sortAndFilter) { if (sheetData.sortAndFilter.mode == "all") { sheetSortFilter += ''; } else { if ( typeof sheetData.sortAndFilter.ref == "string" && sheetData.sortAndFilter.ref.length > 0 ) { sheetSortFilter += ''; } } } } if (headerFormula.length > 0) { headerFormula.forEach((v) => { const shiftLeftValue = sheetData.shiftLeft ? sheetData.shiftLeft : 0; const header = sheetData.headers[v - shiftLeftValue]; if (!header?.formula) { return; } const columnRef = cols[v]; formulaSheetObj[columnRef + "" + rowCount] = { start: sheetData.withoutHeader ? columnRef + "1" : columnRef + "2", end: columnRef + "" + (rowCount - 1), type: header.formula.type, ...(header.formula.styleId ? { styleId: header.formula.styleId } : {}), }; }); } if (headerConditionalFormatting.length > 0 && addCF) { headerConditionalFormatting.forEach((v) => { const header = sheetData.headers[v]; if (!header?.conditionalFormatting) { return; } conditionalFormatting.push({ ...header.conditionalFormatting, start: sheetData.withoutHeader ? cols[v] + "1" : cols[v] + "2", end: cols[v] + "" + (rowCount - 1), }); }); } if (formulaSheetObj) { const remindFormulaKey = Object.keys(formulaSheetObj).sort((a, b) => a > b ? 1 : -1, ); if (remindFormulaKey.length) { let rF: { [row: number]: string; } = {}; remindFormulaKey.forEach((v) => { if (!formulaSheetObj[v]) { return; } const f = generateCellRowCol( v, formulaSheetObj[v], sheetDataId, styleMapper.styleIndexMap, ); if (f.needCalcChain) { needCalcChain = true; calcChainValue += f.chainCell; } if (!rF[f.row]) { rF[f.row] = f.cell; } else { rF[f.row] += f.cell; } }); Object.entries(rF) .sort((a, b) => (+a[0] > +b[0] ? 1 : -1)) .forEach(([v, l]) => { const val = v as keyof object; let rowDataMap = rowMap[val]; if (rowDataMap) { const body = rowDataMap.startTag + rowDataMap.details + l + rowDataMap.endTag; let reg = new RegExp( rowDataMap.startTag + "[\\n\\s\\S]*?", ); sheetDataString = sheetDataString.replace(reg, body); } else { sheetDataString += '' + l + ""; rowMap[val] = { startTag: '', endTag: "", details: l, }; } }); } } } if (index > 0) { sheetContentType += "'; } const shName = sheetData.name ? sheetData.name : "sheet" + (index + 1); const shState = sheetData.state ? sheetData.state : "visible"; workbookString += ''; workbookRelString += ''; sheetNameApp += "" + ("sheet" + (index + 1)) + ""; if (sheetData.selected) { selectedAdded = true; activeTabIndex = index; } const filterMode = sheetData.sortAndFilter ? 'filterMode="1"' : ""; let backgroundImageRef = -1; if (backgroundImagePromise) { await backgroundImagePromise.then((res) => { let result = res as any; backgroundImageRef = result.ref; xl_media_Folder?.file(result.name, result.image); }); } let hasImages = false; let drawersContent = ""; let drawersRels: string = ""; if (imagePromise) { hasImages = true; await imagePromise.then((res) => { let drawerStr = ""; res.forEach((val, i) => { const index = i + 1; let v = val.image; const name = val.name; let from = val.obj.from; let to = val.obj.to; let margin = val.obj.margin; let type = val.obj.type; let extent = val.obj.extent; if (typeof extent == "undefined") { extent = { cx: 200000, cy: 200000, }; } let result: { start: { col: number; row: number; mL?: number; mT?: number; }; end: { col: number; row: number; mR?: number; mB?: number; }; } = { start: { col: 0, row: 0, mL: 0, mT: 0, }, end: { col: 1, row: 1, mR: 0, mB: 0, }, }; if (typeof from == "string" && from.length >= 2) { let p = getColRowBaseOnRefString(from, cols); result.start = { ...p, }; result.end = { col: p.col + 1, row: p.row + 1, }; } if (typeof to == "string" && to.length >= 2) { let p = getColRowBaseOnRefString(to, cols); p.row += 1; p.col += 1; result.end = { ...p, }; } result.end.mR = 0; result.end.mB = 0; result.start.mL = 0; result.start.mT = 0; if (margin) { result.end.mR = margin.right ?? margin.all ?? 0; result.end.mB = margin.bottom ?? margin.all ?? 0; result.start.mL = margin.left ?? margin.all ?? 0; result.start.mT = margin.top ?? margin.all ?? 0; } if (type == "one") { drawersContent += "" + "" + "" + result.start.col + "" + "" + result.start.mT + "" + "" + result.start.row + "" + "" + result.start.mL + "" + "" + '' + "" + "" + '' + "" + '' + "" + "" + '' + "" + "" + "" + "" + "" + "" + '' + "" + "" + "" + "" + "" + "" + ""; } else { drawersContent += '' + "" + "" + result.start.col + "" + "" + result.start.mT + "" + "" + result.start.row + "" + "" + result.start.mL + "" + "" + "" + "" + result.end.col + "" + "" + result.end.mB + "" + "" + result.end.row + "" + "" + result.end.mR + "" + "" + "" + "" + '' + "" + '' + "" + "" + '' + "" + "" + "" + "" + "" + "" + '' + "" + "" + "" + "" + "" + "" + ""; } xl_media_Folder?.file(name, v!); drawerStr += ''; }); drawersRels = '\n' + '' + drawerStr + ""; }); } mergesCellArray = [...new Set(mergesCellArray)]; let cFDataString: string = ""; let priorityCounter = 1; if (conditionalFormatting.length > 0 && addCF) { cFDataString = conditionalFormatting.reduce((cf, cu) => { if (cu.type == "cells") { if (cu.operator == "ct") { return ( cf + '' + 'NOT(ISERROR(SEARCH("' + cu.value + '",' + cu.start + ")))" ); } if ( typeof cu.operator == "undefined" || typeof operatorMap[cu.operator] == "undefined" ) { return cf; } return ( cf + '' + (Array.isArray(cu.value) ? cu.value.reduce((rC, cr) => { return rC + "" + cr.value + ""; }, "") : "" + cu.value + "") + "" ); } else if (cu.type == "top") { return ( cf + '" ); } else if (cu.type == "iconSet") { let percentValue = ""; if (typeof cu.operator == "undefined") { return cf; } if (cu.operator.indexOf("5") == 0) { percentValue = ''; } else if (cu.operator.indexOf("4") == 0) { percentValue = ''; } else { percentValue = ''; } return ( cf + '' + percentValue + "" ); } else if (cu.type == "colorScale") { return ( cf + '' + (cu.operator == "percentile" ? '' : "") + '' + (cu.colors && Array.isArray(cu.colors) ? cu.colors.reduce((reColors, colorCu) => { return ( reColors + '' ); }, "") : '') + "" ); } else if (cu.type == "dataBar") { return ( cf + '' + (cu.colors && Array.isArray(cu.colors) ? cu.colors.reduce((reColors, colorCu) => { return ( reColors + '' ); }, "") : '') + "" ); } else { return cf; } }, ""); } if ((hasCheckbox || hasComment || hasImages) && xl_drawingsFolder == null) { xl_drawingsFolder = xlFolder?.folder("drawings"); } if (hasImages && xl_drawings_relsFolder == null) { xl_drawings_relsFolder = xl_drawingsFolder?.folder("_rels"); } mapData["sheet" + (index + 1)] = { indexId: indexId + 1, key: "sheet" + (index + 1), sheetName: shName, sheetDataTableColumns, backgroundImageRef, sheetDimensions, asTable: asTable ? asTable : false, sheetDataString, sheetDropDown: generateDropDown(sheetData.dropDowns), sheetBreakLine, viewType, hasComment, drawersContent, cFDataString, sheetMargin, sheetHeaderFooter, isPortrait, drawersRels, hasImages, hasCheckbox, formRel, checkboxDrawingContent, checkboxForm, checkboxSheetContent, checkboxShape, commentString, sheetValidation: sheetData.dataValidations ?? [], commentAuthor, shapeCommentRowCol, splitOption, sheetViewProperties, sheetSizeString: sheetSizeString.length > 0 ? "" + sheetSizeString + "" : "", protectionOption: sheetData.protectionOption ? Object.keys(sheetData.protectionOption).reduce((res, cu) => { return ( res + " " + cu + '="' + sheetData.protectionOption![cu as ProtectionOptionKey] + '" ' ); }, "" : "", merges: mergesCellArray.length > 0 ? mergesCellArray.reduce( (mResult, currRef) => { return mResult + ' '; }, '', ) + " " : "", selectedView: !!sheetData.selected, sheetSortFilter, tabColor: sheetData.tabColor ? '" + '' + "" : "" + '' + "", }; indexId++; } if (needCalcChain) { indexId++; workbookRelString += ''; xlFolder?.file( "calcChain.xml", '\n' + calcChainValue + "", ); } let sheetKeys = Object.keys(mapData); // in _rels let relsFolder = zip.folder("_rels"); relsFolder?.file( ".rels", '\n' + '' + ' ' + ' ' + ' ' + "", ); let docPropsFolder = zip.folder("docProps"); docPropsFolder?.file( "core.xml", '\n' + '' + (data.creator ? "" + data.creator + "" : "") + (data.created ? '' + data.created + "" : "") + (data.modified ? '' + data.modified + "" : "") + "", ); docPropsFolder?.file("app.xml", appGenerator(sheetLength, sheetNameApp)); //xl xlFolder?.file( "workbook.xml", '\n' + '' + " " + (selectedAdded || data.hidden ? "' : "") + " " + " " + workbookString + " " + " " + " " + "", ); xlFolder?.file( "sharedStrings.xml", '\n' + '' + " " + sharedString + "", ); //xl/_rels const xl__relsFolder = xlFolder?.folder("_rels"); xl__relsFolder?.file( "workbook.xml.rels", '\n' + '' + ' ' + ' ' + ' ' + " " + workbookRelString + " " + "", ); //xl/theme let xl_themeFolder = xlFolder?.folder("theme"); xl_themeFolder?.file( "theme1.xml", '\n' + '', ); // xl/worksheets let xl_worksheetsFolder = xlFolder?.folder("worksheets"); let commentId: number[] = []; let tableRef: string[] = []; let sheetDrawings: string[] = []; sheetKeys.forEach((sheetProcessResult, iCo) => { const sh = mapData[sheetProcessResult]; if (!sh) { return; } let sheetRelContentStr = ""; let sheetRelSeenFlag = { form: false, drawing: false, vmlDrwing: false, comment: false, table: false, sheetDrawingsPushed: false, }; const sheetDataTableColumns = sh.sheetDataTableColumns; if (sheetDataTableColumns.length > 0) { tableRef.push("table" + (iCo + 1) + ".xml"); const asTableOption: AsTableOption = sh.asTable; const dimensions = <{ start: string; end: string }>sh.sheetDimensions; xl_tableFolder?.file( "table" + (iCo + 1) + ".xml", '\n' + '' + sheetDataTableColumns + '
', ); sheetRelContentStr += ''; } const drawerName = "drawing" + (sheetDrawings.length + 1) + ".xml"; if (sh.hasImages) { sheetDrawings.push(drawerName); sheetRelSeenFlag.sheetDrawingsPushed = true; xl_drawings_relsFolder?.file( drawerName + ".rels", sh.drawersRels.toString(), ); sheetRelSeenFlag.drawing = true; sheetRelContentStr += ''; } if (sh.hasCheckbox) { if (!sheetRelSeenFlag.sheetDrawingsPushed) { sheetDrawings.push(drawerName); } sheetRelContentStr += '' + (sheetRelSeenFlag.drawing ? "" : ''); sheetRelSeenFlag.drawing = true; sheetRelSeenFlag.vmlDrwing = true; sheetRelContentStr += sh.formRel; } if (sh.hasCheckbox || sh.hasImages) { xl_drawingsFolder?.file( drawerName, '\n' + '' + (sh.hasImages ? sh.drawersContent : "") + (sh.hasCheckbox ? sh.checkboxDrawingContent : "") + "", ); } if (sh.hasComment) { commentId.push(iCo + 1); let aurt = sh.commentAuthor; xlFolder?.file( "comments" + (iCo + 1) + ".xml", '\n' + '' + "" + (Array.isArray(aurt) && aurt.length > 0 ? aurt.reduce( (res, currr) => res + "" + currr + "", "", ) : "") + "" + "" + sh.commentString + "" + "", ); sheetRelContentStr += '' + (sheetRelSeenFlag.vmlDrwing ? "" : ''); } if (sh.hasComment || sh.hasCheckbox) { xl_drawingsFolder?.file( "vmlDrawing" + (iCo + 1) + ".vml", '' + "" + (sh.hasCheckbox ? shapeTypeMap["checkbox"] + sh.checkboxShape : "") + (sh.hasComment ? ' ' + ' ' + ' ' + " " + (sh.shapeCommentRowCol as ShapeRC[]).reduce((res, curr) => { return ( res + '' + ' ' + ' ' + ' ' + " " + "
" + "
" + ' ' + " " + " " + " " + " 1, 15, 1, 10, 5, 15, 4, 4" + " False" + " " + curr.row + "" + " " + curr.col + "" + " " + "
" ); }, "") : "") + "
", ); } if (sh.backgroundImageRef > 0) { sheetRelContentStr += ''; } if ( sh.hasImages || sh.hasComment || sh.hasCheckbox || sheetDataTableColumns.length > 0 || sh.backgroundImageRef > 0 ) { const xl_worksheets_relsFolder = xl_worksheetsFolder?.folder("_rels"); xl_worksheets_relsFolder?.file( "sheet" + (iCo + 1) + ".xml.rels", '\n' + ' ' + sheetRelContentStr + "", ); } let sheetViews = ""; if (sh.selectedView || sh.splitOption) { sheetViews = 'sh.viewType).length > 0 ? ' view="' + sh.viewType + '"' : "") + ' workbookViewId="0">' + sh.splitOption + (sh.selectedView ? '' : "") + ""; } else { sheetViews = 'sh.viewType).length > 0 ? ' view="' + sh.viewType + '"' : "") + "/>"; } xl_worksheetsFolder?.file( sh.key + ".xml", '\n' + '' + sh.tabColor + sheetViews + '' + sh.sheetSizeString + "" + sh.sheetDataString + "" + sh.sheetDropDown + sh.protectionOption + sh.sheetSortFilter + sh.merges + sh.cFDataString + (sh.hasImages || sh.hasCheckbox ? '' : "") + (sh.hasComment || sh.hasCheckbox ? '' : "") + (sh.hasCheckbox ? '' + sh.checkboxSheetContent + "" : "") + (Array.isArray(sh.sheetValidation) && sh.sheetValidation.length ? processDataValidation(sh.sheetValidation) : "") + sh.sheetMargin + (sh.isPortrait || (sh.sheetBreakLine).length > 0 ? '' : "") + sh.sheetBreakLine + sh.sheetHeaderFooter + (sh.backgroundImageRef > 0 ? '' : "") + (sheetDataTableColumns.length > 0 ? ' ' : "") + "", ); }); if (checkboxForm.length > 0) { let xlCtrlFolder = xlFolder?.folder("ctrlProps"); checkboxForm.forEach((v, index) => { xlCtrlFolder?.file("ctrlProp" + (index + 1) + ".xml", v); }); } zip.file( "[Content_Types].xml", contentTypeGenerator( sheetContentType, commentId, [...new Set(arrTypes)], sheetDrawings, checkboxForm, needCalcChain, tableRef, ), ); if (isBackend) { return zip .generateAsync({ type: data.generateType ? data.generateType : "nodebuffer", ...(data.useCompression ? { compression: "DEFLATE", compressionOptions: { level: 9, }, } : {}), }) .then((content) => { return content as string | number[] | Buffer; }); } else { if (data.notSave) { return zip .generateAsync({ type: "blob", ...(data.useCompression ? { compression: "DEFLATE", compressionOptions: { level: 9, }, } : {}), }) .then((content) => { return content.slice( 0, content.size, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ); }); } else { zip .generateAsync({ type: "blob", ...(data.useCompression ? { compression: "DEFLATE", compressionOptions: { level: 9, }, } : {}), }) .then(function (content) { import("file-saver").then((module) => { const { saveAs } = module; saveAs( content, (data.fileName ? data.fileName : "tableRecord") + ".xlsx", ); }); }); } } }