From 128dfdabb2b6e218a0d1bcd34d377933a93559a6 Mon Sep 17 00:00:00 2001 From: Jason Ko Date: Sun, 10 Mar 2024 01:56:27 +0900 Subject: [PATCH] Merged line table (#188) * fix merged line table issue * fix more complex merged table --------- Co-authored-by: muyoungko --- src/index.js | 24 +++++++++ test/crud-test.ts | 51 ++++++++++++++++++++ test/templates/test-tables-merged-line.xlsx | Bin 0 -> 10286 bytes 3 files changed, 75 insertions(+) create mode 100644 test/templates/test-tables-merged-line.xlsx diff --git a/src/index.js b/src/index.js index 312bfe6..9f5a938 100755 --- a/src/index.js +++ b/src/index.js @@ -1049,6 +1049,30 @@ class Workbook { newRow.append(newCell); } + // check merged and if yes, add merged cell with proper shape + let mergeCell = self.sheet.root.findall("mergeCells/mergeCell") + .find(c => self.splitRange(c.attrib.ref).start === cell.attrib.r) + let isMergeCell = mergeCell != null + + if(isMergeCell) { + let originalMergeRange = self.splitRange(mergeCell.attrib.ref), + originalMergeStart = self.splitRef(originalMergeRange.start), + originalMergeEnd = self.splitRef(originalMergeRange.end); + + for (let colnum = self.charToNum(originalMergeStart.col) + 1; colnum <= self.charToNum(originalMergeEnd.col); colnum++) { + const originalRow = self.sheet.root.find('sheetData')._children.find(f=>f.attrib.r == originalMergeStart.row) + let col = self.numToChar(colnum) + let originalCell = originalRow._children.find(f=>f.attrib.r.startsWith(col)) + + const addtionalCell = self.cloneElement(originalCell); + addtionalCell.attrib.r = self.joinRef({ + row: newRow.attrib.r, + col: self.numToChar(colnum) + }); + newRow.append(addtionalCell); + } + } + // expand named table range if necessary parentTables.forEach(function (namedTable) { var tableRoot = namedTable.root, autoFilter = tableRoot.find("autoFilter"), range = self.splitRange(tableRoot.attrib.ref); diff --git a/test/crud-test.ts b/test/crud-test.ts index 6bf68f0..b926837 100644 --- a/test/crud-test.ts +++ b/test/crud-test.ts @@ -602,6 +602,57 @@ describe("CRUD operations", function() { }); + it("moves rows down when filling tables with merged line cell", function(done) { + + fs.readFile(path.join(__dirname, "templates", "test-tables-merged-line.xlsx"), function(err, data) { + expect(err).toBeNull(); + + var t = new XlsxTemplate(data); + + t.substitute("Tables", { + score_first: {name: "Jason", score: 1}, + scores: [ + {name: "John", score: 100, extra:'O'}, + {name: "Bob", score: 110, extra:'O'}, + {name: "Jim", score: 120, extra:'O'} + ], + score_last: {name: "Fox", score: 99}, + + score2_first: {name: "Daddy", score: 1}, + scores2: [ + {name: "Son1", score: 100, extra:'O'}, + {name: "Son2", score: 110, extra:'O'}, + {name: "Son3", score: 120, extra:'O'}, + {name: "Son4", score: 130, extra:'O'} + ], + score2_last: {name: "Mom", score: 99}, + }); + + var newData = t.generate(); + + var sharedStrings = etree.parse(t.archive.file("xl/sharedStrings.xml").asText()).getroot(), + sheet1 = etree.parse(t.archive.file("xl/worksheets/sheet1.xml").asText()).getroot(); + + expect(sheet1.find("./sheetData/row[@r='8']/c[@r='B8']").attrib.s).toEqual("3"); + expect(sheet1.find("./sheetData/row[@r='8']/c[@r='B8']/v").text).toEqual("23"); + expect(sheet1.find("./sheetData/row[@r='8']/c[@r='C8']").attrib.s).toEqual("4"); + + expect(sheet1.find("./sheetData/row[@r='8']/c[@r='D8']").attrib.s).toEqual("5"); + expect(sheet1.find("./sheetData/row[@r='8']/c[@r='E8']").attrib.s).toEqual("6"); + expect(sheet1.find("./sheetData/row[@r='8']/c[@r='F8']").attrib.s).toEqual("7"); + + expect(sheet1.find("./sheetData/row[@r='18']/c[@r='B18']").attrib.s).toEqual("3"); + expect(sheet1.find("./sheetData/row[@r='18']/c[@r='C18']").attrib.s).toEqual("4"); + expect(sheet1.find("./sheetData/row[@r='18']/c[@r='D18']").attrib.s).toEqual("5"); + + // XXX: For debugging only + fs.writeFileSync("test/output/test-tables-merged-line-out.xlsx", newData, "binary"); + + done(); + }); + + }); + it("replaces hyperlinks in sheet", function(done) { fs.readFile(path.join(__dirname, "templates", "test-hyperlinks.xlsx"), function(err, data) { expect(err).toBeNull(); diff --git a/test/templates/test-tables-merged-line.xlsx b/test/templates/test-tables-merged-line.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..d37e318f8e894b7936a4dfaf8efd8d51cb482e5d GIT binary patch literal 10286 zcmeHNgLfY5)_-H$b{gBZlg5p0+qR8{jcwa$)SyA5#%gTqOMCBK=bWDN{RQ{VyVgAK ztTlVDnc2V1-y<�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 WX|Olz0szo&Kfkx!{*mnG)&Btk*9k8G literal 0 HcmV?d00001