UNPKG

mr-excel

Version:

A versatile JavaScript library for effortlessly generating .xlsx files from input objects. Seamlessly create Excel spreadsheets with data, formatting, formulas, and more.

1,514 lines (1,501 loc) 99.5 kB
import { type AlignmentOptionKey, type AsTableOption, type ConditionalFormatting, type ExcelTable, type Formula, type HeaderFooterOption, type MergeRowConditionMap, type ProtectionOptionKey, type RowMap, type StyleMapper, type Buffer, } 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 { applyConfig } from "../utils/store"; import type JSZip from "jszip"; import { generateDropDown } from "../utils/drop-down-utils"; export async function generateExcel( data: ExcelTable, styleKey: string = "" ): Promise<string | number[] | Blob | Buffer | undefined> { if (typeof styleKey == "string" && styleKey.length > 0) { data = applyConfig(styleKey, data); } 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<string, string> = { 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 module = await import("jszip"); const JSZip1 = module.default; let zip = new JSZip1(); if (!data.sheet) { data.sheet = [ { headers: [], data: [], }, ]; } const sheetLength = data.sheet.length; // xl let xlFolder = 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; if (!data.styles) { data.styles = {}; } if (data.addDefaultTitleStyle) { data.styles["titleStyle"] = { alignment: { horizontal: "center", vertical: "center", }, }; } const styleKeys = Object.keys(data.styles); const defaultCommentStyle = defaultCellCommentStyle; const addCF = data.activateConditionalFormatting ? data.activateConditionalFormatting : false; const headerFooterStyle: Record<string, string> = {}; const cFMapIndex: Record<string, number> = {}; let styleMapper: StyleMapper = styleKeys.reduce( (res: StyleMapper, cur, index) => { const styleObject = data.styles![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 = "&amp;" + '"' + fontProcessLeft + "," + fontProcessRight + '"'; } if (styleObject.size) { result += "&amp;" + styleObject.size; } if (styleObject.doubleUnderline) { result += "&amp;E"; } else if (styleObject.underline) { result += "&amp;U"; } if (styleObject.color) { const convertedColor = convertToHex(styleObject.color, isBackend); if (typeof convertedColor == "string" && convertedColor.length > 0) { result += "&amp;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 += '<dxf><font><color rgb="' + color + '"/></font><fill> <patternFill> <bgColor rgb="' + bgColor + '"/></patternFill></fill></dxf>'; 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 + "<fill>" + '<patternFill patternType="solid">' + (fgConvertor ? '<fgColor rgb="' + fgConvertor.replace("#", "") + '" />' : "") + "</patternFill>" + "</fill>"; } 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 + "<font>" + (styleObject.bold ? "<b/>" : "") + (styleObject.italic ? "<i />" : "") + (styleObject.underline || styleObject.doubleUnderline ? "<u " + (styleObject.doubleUnderline ? ' val="double" ' : "") + "/>" : "") + (styleObject.size ? '<sz val="' + styleObject.size + '" />' : "") + (colors ? '<color rgb="' + colors.replace("#", "") + '" />' : "") + (styleObject.fontFamily ? '<name val="' + styleObject.fontFamily + '" />' : "") + "</font>"; res.commentSyntax.value[cur] = "<rPr>" + (styleObject.bold ? "<b/>" : "") + (styleObject.italic ? "<i/>" : "") + (styleObject.underline || styleObject.doubleUnderline ? "<u " + (styleObject.doubleUnderline ? 'val="double" ' : "") + "/>" : "") + '<sz val="' + (styleObject.size ? styleObject.size : "9") + '" />' + (colors ? '<color rgb="' + colors.replace("#", "") + '" />' : "") + '<rFont val="' + (styleObject.fontFamily ? styleObject.fontFamily : "Tahoma") + '" />' + "</rPr>"; } let endPart = "/>"; if (styleObject.alignment) { if (styleObject.alignment.rtl) { styleObject.alignment["readingOrder"] = 2; } delete styleObject.alignment.rtl; if (styleObject.alignment.ltr) { styleObject.alignment["readingOrder"] = 1; } delete styleObject.alignment.ltr; endPart = ' applyAlignment="1">' + "<alignment " + Object.keys(styleObject.alignment).reduce((al, alignmentOption) => { return ( al + " " + alignmentOption + '="' + styleObject.alignment![alignmentOption! as AlignmentOptionKey] + '" ' ); }, "") + " />" + "</xf>"; } const borderObj = styleObject.border; let borderStr = ""; if (typeof borderObj == "object") { if (borderObj.left || borderObj.full) { borderStr += '<left style="' + (borderObj.left || borderObj.full)!.style + '">' + '<color rgb="' + convertToHex( (borderObj.left || borderObj.full)!.color, isBackend )!.replace("#", "") + '" />' + "</left>"; } if (borderObj.right || borderObj.full) { borderStr += '<right style="' + (borderObj.right || borderObj.full)!.style + '">' + '<color rgb="' + convertToHex( (borderObj.right || borderObj.full)!.color, isBackend )!.replace("#", "") + '" />' + "</right>"; } if (borderObj.top || borderObj.full) { borderStr += '<top style="' + (borderObj.top || borderObj.full)!.style + '">' + '<color rgb="' + convertToHex( (borderObj.top || borderObj.full)!.color, isBackend )!.replace("#", "") + '" />' + "</top>"; } if (borderObj.bottom || borderObj.full) { borderStr += '<bottom style="' + (borderObj.bottom || borderObj.full)!.style + '">' + '<color rgb="' + convertToHex( (borderObj.bottom || borderObj.full)!.color, isBackend )!.replace("#", "") + '" />' + "</bottom>"; } indexes.borderIndex = res.border.count; res.border.count++; res.border.value += "<border>" + borderStr + "<diagonal />" + "</border>"; } 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 + '<xf numFmtId="' + indexes.formatIndex + '"' + ' fontId="' + indexes.fontIndex + '" fillId="' + indexes.fillIndex + '" borderId="' + indexes.borderIndex + '" xfId="0"' + (indexes.borderIndex > 0 ? ' applyBorder="1" ' : "") + (indexes.fillIndex > 0 ? ' applyFill="1" ' : "") + (indexes.fontIndex >= 0 ? ' applyFont="1" ' : "") + (indexes.formatIndex > 0 ? ' applyNumberFormat="1" ' : "") + endPart; data.styles![cur].index = res.cell.count; res.cell.count++; return res; }, { conditionalFormatting: { count: addCF ? 1 : 0, value: '<dxf><font><color rgb="FF9C0006"/></font><fill> <patternFill> <bgColor rgb="FFFFC7CE"/></patternFill></fill></dxf>', }, 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 = '<Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" PartName="/xl/worksheets/sheet1.xml" />'; let sharedString = ""; let sharedStringIndex = 0; let workbookString = ""; let workbookRelString = ""; let sharedStringMap: { [key: string]: string; } = {}; const mapData: { [k: string]: { [k2: string]: string | number | boolean | object; }; } = {}; let sheetNameApp = ""; let indexId = 4; let selectedAdded = false; let activeTabIndex = -1; let arrTypes: string[] = []; let imageCounter = 1; interface ShapeRC { row: string | number; col: string | number; } const formCtrlMap = { checkbox: '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' + '<formControlPr xmlns="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" objectType="CheckBox" **value** **fmlaLink** lockText="1" noThreeD="1"/>', }; let shapeIdCounter = 1024; const shapeMap = { checkbox: `<v:shape id="***id***" type="#_x0000_t201" style='position:absolute; margin-left:1.5pt;margin-top:1.5pt;width:63pt;height:16.5pt;z-index:1; mso-wrap-style:tight' filled="f" fillcolor="window [65]" stroked="f" strokecolor="windowText [64]" o:insetmode="auto"> <v:path shadowok="t" strokeok="t" fillok="t"/> <o:lock v:ext="edit" rotation="t"/> <v:textbox style='mso-direction-alt:auto' o:singleclick="f"> <div style='text-align:left'><font face="Segoe UI" size="160" color="auto">***text***</font></div> </v:textbox> <x:ClientData ObjectType="Checkbox"> <x:SizeWithCells/> <x:Anchor> 0, 2, 0, 2, 0, 86, 1, 0</x:Anchor> <x:AutoFill>False</x:AutoFill> <x:AutoLine>False</x:AutoLine> <x:TextVAlign>Center</x:TextVAlign> <x:NoThreeD/> </x:ClientData> </v:shape>`, }; const shapeTypeMap = { checkbox: `<v:shapetype id="_x0000_t201" coordsize="21600,21600" o:spt="201" path="m,l,21600r21600,l21600,xe"> <v:stroke joinstyle="miter"/> <v:path shadowok="f" o:extrusionok="f" strokeok="f" fillok="f" o:connecttype="rect"/> <o:lock v:ext="edit" shapetype="t"/> </v:shapetype>`, }; let checkboxForm: string[] = []; let calcChainValue = ""; let needCalcChain = false; let xl_tableFolder: JSZip | null | undefined = null; for (let index = 0; index < sheetLength; index++) { const sheetData = data.sheet[index]; const sheetDataId = index + 1; let rowMap: RowMap = {}; let sheetDimensions = { 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.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 += '<rowBreaks count="' + rowLength + '" manualBreakCount="' + rowLength + '">' + pageBreak.row.reduce( (result, current) => result + '<brk id="' + current + '" max="16383" man="1"/>', "" ) + "</rowBreaks>"; } if (pageBreak.column && Array.isArray(pageBreak.column)) { viewType = "pageBreakPreview"; const columnLength = pageBreak.column.length; sheetBreakLine += '<colBreaks count="' + columnLength + '" manualBreakCount="' + columnLength + '">' + pageBreak.column.reduce( (result, current) => result + '<brk id="' + current + '" max="16383" man="1"/>', "" ) + "</colBreaks>"; } } 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 = '<pageMargins left="' + result["left"] + '" right="' + result["right"] + '" top="' + result["top"] + '" bottom="' + result["bottom"] + '" header="' + result["header"] + '" footer="' + result["footer"] + '"/>'; } 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 = <HeaderFooterOption>typeObj[direction]; node += "&amp;" + direction.toUpperCase(); if (dirObj.styleId && headerFooterStyle[dirObj.styleId]) { node += headerFooterStyle[dirObj.styleId]; } if (dirObj.text) { node += dirObj.text; } }); node = "<" + typeHF + endTag + ">" + node + "</" + typeHF + endTag + ">"; 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 = "<headerFooter" + oddEvenFlag + firstFlag + ">" + sheetHeaderFooter + "</headerFooter>"; } } 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); } 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; two = cols[frozen.index] + (frozen.index + 1); } else { splitO = { y: frozen.index.r, x: frozen.index.c, }; 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 = '<pane ySplit="' + ((typeof split.split == "object" && split.split.y) || split.split) + '" topLeftCell="' + ref + '" activePane="bottomLeft"' + splitState + "/>"; } 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 = '<pane xSplit="' + ((typeof split.split == "object" && split.split.x) || split.split) + '" topLeftCell="' + ref + '" activePane="topLeft"' + splitState + "/>"; } else { let ref; if (split.startAt) { ref = split.startAt.two; if (split.startAt.one) { sheetViewProperties += ' topLeftCell="' + split.startAt.one + '"'; } } if (!ref) { ref = "A1"; } splitOption = '<pane xSplit="' + ((typeof split.split == "object" && split.split.x) || split.split) + '" ySplit="' + ((typeof split.split == "object" && split.split.y) || split.split) + '" topLeftCell="' + ref + '" activePane="bottomLeft"' + splitState + "/>"; } } } 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 += '<mc:AlternateContent xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"><mc:Choice Requires="x14"><control shapeId="' + shapeId + '" r:id="rId' + (7 + i) + '" name="' + v.text + '"><controlPr defaultSize="0" autoFill="0" autoLine="0" autoPict="0"><anchor moveWithCells="1"><from><xdr:col>' + resultVal.start.col + "</xdr:col><xdr:colOff>19050</xdr:colOff><xdr:row>" + resultVal.start.row + "</xdr:row><xdr:rowOff>19050</xdr:rowOff></from><to><xdr:col>" + resultVal.end.col + "</xdr:col><xdr:colOff>819150</xdr:colOff><xdr:row>" + resultVal.end.row + "</xdr:row><xdr:rowOff>0</xdr:rowOff></to></anchor></controlPr></control></mc:Choice></mc:AlternateContent>"; formRel += '<Relationship Id="rId' + (7 + i) + '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp" ' + 'Target="../ctrlProps/ctrlProp' + checkboxForm.length + '.xml" />'; checkboxDrawingContent += '<mc:AlternateContent xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"><mc:Choice xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" Requires="a14"><xdr:twoCellAnchor editAs="oneCell"><xdr:from><xdr:col>' + resultVal.start.col + "</xdr:col><xdr:colOff>19050</xdr:colOff><xdr:row>" + resultVal.start.row + "</xdr:row><xdr:rowOff>19050</xdr:rowOff></xdr:from><xdr:to><xdr:col>" + resultVal.end.col + "</xdr:col><xdr:colOff>819150</xdr:colOff><xdr:row>" + resultVal.end.row + '</xdr:row><xdr:rowOff>0</xdr:rowOff></xdr:to><xdr:sp macro="" textlink=""><xdr:nvSpPr><xdr:cNvPr id="' + shapeId + '" name="' + v.text + '" hidden="1"><a:extLst><a:ext uri=""><a14:compatExt spid="' + sId + '"/></a:ext><a:ext uri=""><a16:creationId xmlns:a16="http://schemas.microsoft.com/office/drawing/2014/main" id=""/></a:ext>' + '</a:extLst></xdr:cNvPr><xdr:cNvSpPr/></xdr:nvSpPr><xdr:spPr bwMode="auto"><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/></a:xfrm>' + '<a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/><a:ln><a:noFill/></a:ln></xdr:spPr><xdr:txBody>' + '<a:bodyPr vertOverflow="clip" wrap="square" lIns="27432" tIns="18288" rIns="0" bIns="18288" anchor="ctr" upright="1"/>' + '<a:lstStyle/><a:p><a:pPr algn="l" rtl="0"><a:defRPr sz="1000"/></a:pPr><a:r><a:rPr lang="en-US" sz="800" b="0" i="0" u="none" strike="noStrike" baseline="0"><a:solidFill>' + '<a:srgbClr val="000000"/></a:solidFill><a:latin typeface="Segoe UI"/><a:cs typeface="Segoe UI"/></a:rPr><a:t>' + v.text + "</a:t></a:r></a:p></xdr:txBody></xdr:sp><xdr:clientData/></xdr:twoCellAnchor></mc:Choice><mc:Fallback/></mc:AlternateContent>"; }); } 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: '<row r="' + (rowCount + top) + '" ' + height + ' spans="1:' + Math.max(left + consommeCol - 1, 1) + '">', details: '<c r="' + refString + '" ' + (data.styles[tStyle] ? ' s="' + data.styles[tStyle].index + '" ' : "") + ' t="s"><v>' + sharedStringIndex + "</v></c>", endTag: "</row>", }; titleRow += '<row r="' + (rowCount + top) + '" ' + height + ' spans="1:' + Math.max(left + consommeCol - 1, 1) + '">'; titleRow += '<c r="' + refString + '" ' + (data.styles[tStyle] ? ' s="' + data.styles[tStyle].index + '" ' : "") + ' t="s"><v>' + sharedStringIndex + "</v></c>"; titleRow += "</row>"; sharedStringIndex++; sharedStringMap[title.text] = title.text; if (title.multiStyleValue && Array.isArray(title.multiStyleValue)) { sharedString += generateMultiStyleByArray( title.multiStyleValue, styleMapper.commentSyntax.value, tStyle ); } else { sharedString += "<si><t>" + specialCharacterConverter(title.text) + "</t></si>"; } } 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 += '<tableColumns count="' + sheetData.headers.length + '">'; 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 += '<tableColumn id="' + (innerIndex + 1) + '" name="' + v.text + '"/>'; } 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) { 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 += '<col min="' + (innerIndex + 1) + '" max="' + (innerIndex + 1) + '" width="' + v.size + '" customWidth="1" />'; } 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, data.styles ); if (f.needCalcChain) { needCalcChain = true; calcChainValue += f.chainCell; } sheetDataString += f.cell; delete formulaSheetObj![refString]; } else { sheetDataString += '<c r="' + cols[innerIndex] + rowCount + '" ' + (headerStyleKey && data.styles && data.styles[headerStyleKey] ? ' s="' + data.styles[headerStyleKey].index + '" ' : "") + " " + 't="s"><v>' + sharedStringIndex + "</v></c>"; 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 += "<si><t>" + specialCharacterConverter(v.text) + "</t></si>"; } sharedStringMap[v.text] = v.text; sharedStringIndex++; } }); if (asTable) { sheetDataTableColumns += "</tableColumns>"; } if (!sheetData.withoutHeader) { const rowTag = '<row r="' + rowCount + '" spans="1:' + Math.max(colsLength, 1) + '" ' + (sheetData.headerHeight ? 'ht="' + sheetData.headerHeight + '" customHeight="1"' : "") + (sheetData.headerRowOption ? Object.keys(sheetData.headerRowOption).reduce((res, curr) => { return ( res + " " + curr + '="' + sheetData.headerRowOption![curr as keyof object] + '" ' ); }, " ") : "") + ">"; rowMap[rowCount] = { startTag: rowTag, endTag: "</row>", details: sheetDataString, }; sheetDataString = titleRow + rowTag + sheetDataString + "</row>"; rowCount++; } else { sheetDataString += titleRow; } if (Array.isArray(sheetData.data)) { const keyOutline = sheetData.mapSheetDataOption && sheetData.mapSheetDataOption.outlineLevel ? sheetData.mapSheetDataOption.outlineLevel : "outlineLevel"; const keyHidden = sheetData.mapSheetDataOption && sheetData.mapSheetDataOption.hidden ? sheetData.mapSheetDataOption.hidden : "hidden"; const keyHeight = sheetData.mapSheetDataOption && sheetData.mapSheetDataOption.height ? sheetData.mapSheetDataOption.height : "height"; const rowLength = sheetData.data.length; sheetData.data.forEach((mData, innerIndex) => { if (mData.mergeType) { for (let iIndex = 0; iIndex < mData.mergeType.length; iIndex++) { const mergeType = mData.mergeType[iIndex]; const mergeStart = mData.mergeStart[iIndex]; const mergeValue = mData.mergeValue[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 = '<row r="' + rowCount + '" spans="1:' + Math.max(colsLength, 1) + '" ' + (keyHeight in mData ? 'ht="' + mData[keyHeight] + '" customHeight="1"' : "") + (keyOutline in mData ? ' outlineLevel="' + mData[keyOutline] + '"' : "") + (keyHidden in mData ? ' hidden="' + mData[keyHidden] + '"' : "") + " >"; sheetDataString += rowTagStart; let rowDataString = ""; objKey.forEach((key, keyIndex) => { if (shiftCount) { keyIndex += shiftCount; } const cellValue = mData[key] * 1; let dataEl = sheetData.convertStringToNumber && !isNaN(cellValue) ? cellValue : mData[key]; if (typeof dataEl === "boolean") { dataEl = dataEl + ""; } let cellStyle = rowStyle; if ( sheetData.styleCellCondition && typeof sheetData.styleCellCondition == "function" ) { cellStyle = sheetData.styleCellCondition( dataEl, mData, rowCount, keyIndex, false, styleKeys ) || rowStyle; } if ( sheetData.mergeRowDataCondition && typeof sheetData.mergeRowDataCondition == "function" ) { let result = sheetData.mergeRowDataCondition( dataEl, key, keyIndex, false ); const columnKey = cols[keyIndex]; let item = mergeRowConditionMap[columnKey]; if (result === true) { if (!item || (item && !item.inProgress)) { mergeRowConditionMap[columnKey] = { inProgress: true, start: rowCount, }; } } else { if (item && item.inProgress) { mergesCellArray.push( columnKey + item.start + ":" + columnKey + (rowCount - 1) ); mergeRowConditionMap[columnKey] = { inProgress: false, start: -1, }; } } } if (typeof dataEl == "undefined") { dataEl = ""; } const refString = cols[keyIndex] + "" + rowCount; if (typeof sheetData.commentCondition == "function") { const checkCommentCondition = sheetData.commentCondition( dataEl, mData, key, rowCount, keyIndex, false ); if ( typeof checkCommentCondition == "string" || (typeof checkCommentCondition == "object" && checkCommentCondition != null) ) { if (typeof mData.comment !== "object") { mData.comment = {}; } mData.comment[key] = checkCommentCondition; } } if (typeof mData.comment == "object" && key in mData.comment) { const cellComment = mData.comment[key]; hasComment = true; const commentObj = commentConvertor( cellComment, styleMapper.commentSyntax.value, defaultCommentStyle ); if ( commentObj.hasAuthor && typeof commentObj.author != "undefined" ) { commentAuthor.push(commentObj.author.toString()); } shapeCommentRowCol.push({ row: rowCount - 1, col: keyIndex, }); let authorId = commentAuthor.length; if ( commentObj.hasAuthor && typeof commentObj.author != "undefined" ) { let auth = commentObj.author.toSt