From e6e09a03611efbfdd46346492a071b9c370e739b Mon Sep 17 00:00:00 2001 From: Skylar Ittner Date: Thu, 5 Feb 2026 03:32:14 -0700 Subject: [PATCH] Properly support currency fields --- scripts/createOdsFile.js | 60 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/scripts/createOdsFile.js b/scripts/createOdsFile.js index 8141360..01a63b4 100644 --- a/scripts/createOdsFile.js +++ b/scripts/createOdsFile.js @@ -31,7 +31,7 @@ const manifestXml = ` * @param {Map} sheetsData * @returns {Promise} */ -export async function createOdsFile(sheetsData) { +export async function createOdsFile(sheetsData, currencyData = null) { // Create a new zip writer const zipWriter = new ZipWriter(new BlobWriter('application/vnd.oasis.opendocument.spreadsheet')); @@ -49,7 +49,7 @@ export async function createOdsFile(sheetsData) { } ); - const contentXml = generateContentFileXMLString(sheetsData); + const contentXml = generateContentFileXMLString(sheetsData, currencyData); zipWriter.add("content.xml", new TextReader(contentXml), {level: 9}); zipWriter.add("styles.xml", new TextReader(stylesXml)); @@ -67,7 +67,7 @@ export async function createOdsFile(sheetsData) { * @param {Map} sheetsData * @returns {string} */ -function generateContentFileXMLString(sheetsData) { +function generateContentFileXMLString(sheetsData, currencyData) { const doc = createDocument('urn:oasis:names:tc:opendocument:xmlns:office:1.0', 'office:document-content'); const root = doc.documentElement; @@ -80,6 +80,40 @@ function generateContentFileXMLString(sheetsData) { root.setAttribute('office:version', '1.2'); const styleNode = doc.createElement("office:automatic-styles"); + + var currencyStyleName = "currencyStyle"; + if (currencyData != null) { + currencyStyleName = `currency${currencyData.currencyCode.toUpperCase()}`; + const numberStyle = doc.createElement("number:currency-style"); + numberStyle.setAttribute("style:name", currencyStyleName); + + const numberCurrencySymbolStyle = doc.createElement("number:currency-symbol"); + numberCurrencySymbolStyle.setAttribute("number:language", "en"); + numberCurrencySymbolStyle.setAttribute("number:country", currencyData.countryCode.toUpperCase()); + numberCurrencySymbolStyle.textContent = currencyData.currencySymbol; + numberStyle.appendChild(numberCurrencySymbolStyle); + + const numberCurrencyStyle = doc.createElement("number:number"); + numberCurrencyStyle.setAttribute("number:min-integer-digits", "1"); + numberCurrencyStyle.setAttribute("number:decimal-places", `${currencyData.decimalPlaces}`); + numberCurrencyStyle.setAttribute("number:min-decimal-places", `${currencyData.decimalPlaces}`); + numberCurrencyStyle.setAttribute("number:grouping", "true"); + numberStyle.appendChild(numberCurrencyStyle); + + styleNode.appendChild(numberStyle); + + const currencyCellStyleNode = doc.createElement("style:style"); + currencyCellStyleNode.setAttribute("style:name", "currencycell"); + currencyCellStyleNode.setAttribute("style:family", "table-cell"); + currencyCellStyleNode.setAttribute("style:data-style-name", currencyStyleName); + + const currencyCellTableCellProperties = doc.createElement("style:table-cell-properties"); + + currencyCellStyleNode.appendChild(currencyCellTableCellProperties); + + styleNode.appendChild(currencyCellStyleNode); + } + const boldCellStyleNode = doc.createElement("style:style"); boldCellStyleNode.setAttribute("style:name", "boldcell"); boldCellStyleNode.setAttribute("style:family", "table-cell"); @@ -88,6 +122,7 @@ function generateContentFileXMLString(sheetsData) { boldCellStyleNode.appendChild(boldCellTextPropsNode); styleNode.appendChild(boldCellStyleNode); + root.appendChild(styleNode); const bodyNode = doc.createElement('office:body'); @@ -105,10 +140,11 @@ function generateContentFileXMLString(sheetsData) { var columnsWidthChars = {}; for (let r = 0; r < sheetData.length; r++) { for (let c = 0; c < sheetData[r].length; c++) { + var len = ((sheetData[r][c].display ?? sheetData[r][c].value) + "").length; if (typeof columnsWidthChars[c] == "undefined") { - columnsWidthChars[c] = sheetData[r][c].value.length; + columnsWidthChars[c] = len; } - columnsWidthChars[c] = Math.max(columnsWidthChars[c], sheetData[r][c].value.length); + columnsWidthChars[c] = Math.max(columnsWidthChars[c], len); } } @@ -151,6 +187,14 @@ function generateContentFileXMLString(sheetsData) { cellNode.setAttribute('office:value', cell.value.toString()); cellNode.setAttribute('office:value-type', 'percentage'); break; + case 'currency': + cellNode.setAttribute('office:value', cell.value.toString()); + cellNode.setAttribute('office:value-type', 'currency'); + if (currencyData != null) { + cellNode.setAttribute("table:style-name", "currencycell"); + cellNode.setAttribute('office:currency', currencyData.currencyCode.toUpperCase()); + } + break; case 'date': cellNode.setAttribute('office:date-value', cell.value.toString()); break; @@ -166,7 +210,11 @@ function generateContentFileXMLString(sheetsData) { if (cellType !== 'string') { const textNode = doc.createElement('text:p'); - textNode.textContent = cell.value.toString(); + if (typeof cell.display != "undefined") { + textNode.textContent = cell.display.toString(); + } else { + textNode.textContent = cell.value.toString(); + } cellNode.appendChild(textNode); } }