diff --git a/calcchain.go b/calcchain.go new file mode 100644 index 0000000..285a3e9 --- /dev/null +++ b/calcchain.go @@ -0,0 +1,55 @@ +// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of +// this source code is governed by a BSD-style license that can be found in +// the LICENSE file. +// +// Package excelize providing a set of functions that allow you to write to +// and read from XLSX files. Support reads and writes XLSX file generated by +// Microsoft Excelâ„¢ 2007 and later. Support save file without losing original +// charts of XLSX. This library needs Go version 1.8 or later. + +package excelize + +import "encoding/xml" + +// calcChainReader provides a function to get the pointer to the structure +// after deserialization of xl/calcChain.xml. +func (f *File) calcChainReader() *xlsxCalcChain { + if f.CalcChain == nil { + var c xlsxCalcChain + _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML("xl/calcChain.xml")), &c) + f.CalcChain = &c + } + return f.CalcChain +} + +// calcChainWriter provides a function to save xl/calcChain.xml after +// serialize structure. +func (f *File) calcChainWriter() { + if f.CalcChain != nil { + output, _ := xml.Marshal(f.CalcChain) + f.saveFileList("xl/calcChain.xml", output) + } +} + +// deleteCalcChain provides a function to remove cell reference on the +// calculation chain. +func (f *File) deleteCalcChain(axis string) { + calc := f.calcChainReader() + if calc != nil { + for i, c := range calc.C { + if c.R == axis { + calc.C = append(calc.C[:i], calc.C[i+1:]...) + } + } + } + if len(calc.C) == 0 { + f.CalcChain = nil + delete(f.XLSX, "xl/calcChain.xml") + content := f.contentTypesReader() + for k, v := range content.Overrides { + if v.PartName == "/xl/calcChain.xml" { + content.Overrides = append(content.Overrides[:k], content.Overrides[k+1:]...) + } + } + } +} diff --git a/cell.go b/cell.go index afe8635..3cf880a 100644 --- a/cell.go +++ b/cell.go @@ -305,6 +305,11 @@ func (f *File) SetCellFormula(sheet, axis, formula string) { completeRow(xlsx, rows, cell) completeCol(xlsx, rows, cell) + if formula == "" { + xlsx.SheetData.Row[xAxis].C[yAxis].F = nil + f.deleteCalcChain(axis) + return + } if xlsx.SheetData.Row[xAxis].C[yAxis].F != nil { xlsx.SheetData.Row[xAxis].C[yAxis].F.Content = formula } else { diff --git a/col.go b/col.go index af2c321..1130c3a 100644 --- a/col.go +++ b/col.go @@ -322,6 +322,10 @@ func (f *File) InsertCol(sheet, column string) { // // xlsx.RemoveCol("Sheet1", "C") // +// Use this method with caution, which will affect changes in references such +// as formulas, charts, and so on. If there is any referenced value of the +// worksheet, it will cause a file error when you open it. The excelize only +// partially updates these references currently. func (f *File) RemoveCol(sheet, column string) { xlsx := f.workSheetReader(sheet) for r := range xlsx.SheetData.Row { diff --git a/excelize.go b/excelize.go index 32f0451..32aa431 100644 --- a/excelize.go +++ b/excelize.go @@ -25,6 +25,7 @@ import ( type File struct { checked map[string]bool sheetMap map[string]string + CalcChain *xlsxCalcChain ContentTypes *xlsxTypes Path string SharedStrings *xlsxSST @@ -201,7 +202,8 @@ func (f *File) UpdateLinkedValue() { // row: Index number of the row we're inserting/deleting before // offset: Number of rows/column to insert/delete negative values indicate deletion // -// TODO: adjustPageBreaks, adjustComments, adjustDataValidations, adjustProtectedCells +// TODO: adjustCalcChain, adjustPageBreaks, adjustComments, +// adjustDataValidations, adjustProtectedCells // func (f *File) adjustHelper(sheet string, column, row, offset int) { xlsx := f.workSheetReader(sheet) diff --git a/excelize_test.go b/excelize_test.go index ebbfcf7..d621b87 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -342,8 +342,18 @@ func TestSetCellFormula(t *testing.T) { xlsx.SetCellFormula("Sheet1", "C19", "SUM(Sheet2!D2,Sheet2!D9)") // Test set cell formula with illegal rows number. xlsx.SetCellFormula("Sheet1", "C", "SUM(Sheet2!D2,Sheet2!D9)") + assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestSetCellFormula1.xlsx"))) - assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestSetCellFormula.xlsx"))) + xlsx, err = OpenFile(filepath.Join("test", "CalcChain.xlsx")) + if !assert.NoError(t, err) { + t.FailNow() + } + // Test remove cell formula. + xlsx.SetCellFormula("Sheet1", "A1", "") + assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestSetCellFormula2.xlsx"))) + // Test remove all cell formula. + xlsx.SetCellFormula("Sheet1", "B1", "") + assert.NoError(t, xlsx.SaveAs(filepath.Join("test", "TestSetCellFormula3.xlsx"))) } func TestSetSheetBackground(t *testing.T) { diff --git a/file.go b/file.go index 3e49803..66b46c5 100644 --- a/file.go +++ b/file.go @@ -92,6 +92,7 @@ func (f *File) WriteToBuffer() (*bytes.Buffer, error) { f.workbookRelsWriter() f.worksheetWriter() f.styleSheetWriter() + f.calcChainWriter() for path, content := range f.XLSX { fi, err := zw.Create(path) if err != nil { diff --git a/rows.go b/rows.go index 3984216..aebc979 100644 --- a/rows.go +++ b/rows.go @@ -343,6 +343,10 @@ func (f *File) GetRowOutlineLevel(sheet string, rowIndex int) uint8 { // // xlsx.RemoveRow("Sheet1", 2) // +// Use this method with caution, which will affect changes in references such +// as formulas, charts, and so on. If there is any referenced value of the +// worksheet, it will cause a file error when you open it. The excelize only +// partially updates these references currently. func (f *File) RemoveRow(sheet string, row int) { if row < 0 { return @@ -375,15 +379,23 @@ func (f *File) InsertRow(sheet string, row int) { // // xlsx.DuplicateRow("Sheet1", 2) // +// Use this method with caution, which will affect changes in references such +// as formulas, charts, and so on. If there is any referenced value of the +// worksheet, it will cause a file error when you open it. The excelize only +// partially updates these references currently. func (f *File) DuplicateRow(sheet string, row int) { f.DuplicateRowTo(sheet, row, row+1) } // DuplicateRowTo inserts a copy of specified row at specified row position -// movig down exists rows aftet target position +// moving down exists rows after target position // // xlsx.DuplicateRowTo("Sheet1", 2, 7) // +// Use this method with caution, which will affect changes in references such +// as formulas, charts, and so on. If there is any referenced value of the +// worksheet, it will cause a file error when you open it. The excelize only +// partially updates these references currently. func (f *File) DuplicateRowTo(sheet string, row, row2 int) { if row <= 0 || row2 <= 0 || row == row2 { return diff --git a/sheet.go b/sheet.go index cb1c08e..efbd466 100644 --- a/sheet.go +++ b/sheet.go @@ -900,7 +900,7 @@ func (p *PageLayoutPaperSize) getPageLayout(ps *xlsxPageSetUp) { // 40 | German standard fanfold (8.5 in. by 12 in.) // 41 | German legal fanfold (8.5 in. by 13 in.) // 42 | ISO B4 (250 mm by 353 mm) -// 43 | Japanese double postcard (200 mm by 148 mm) +// 43 | Japanese postcard (100 mm by 148 mm) // 44 | Standard paper (9 in. by 11 in.) // 45 | Standard paper (10 in. by 11 in.) // 46 | Standard paper (15 in. by 11 in.) diff --git a/test/CalcChain.xlsx b/test/CalcChain.xlsx new file mode 100755 index 0000000..8558f82 Binary files /dev/null and b/test/CalcChain.xlsx differ diff --git a/xmlCalcChain.go b/xmlCalcChain.go new file mode 100644 index 0000000..9c916bf --- /dev/null +++ b/xmlCalcChain.go @@ -0,0 +1,28 @@ +// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of +// this source code is governed by a BSD-style license that can be found in +// the LICENSE file. +// +// Package excelize providing a set of functions that allow you to write to +// and read from XLSX files. Support reads and writes XLSX file generated by +// Microsoft Excelâ„¢ 2007 and later. Support save file without losing original +// charts of XLSX. This library needs Go version 1.8 or later. + +package excelize + +import "encoding/xml" + +// xlsxCalcChain directly maps the calcChain element. This element represents the root of the calculation chain. +type xlsxCalcChain struct { + XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main calcChain"` + C []xlsxCalcChainC `xml:"c"` +} + +// xlsxCalcChainC directly maps the c element. +type xlsxCalcChainC struct { + R string `xml:"r,attr"` + I int `xml:"i,attr"` + L bool `xml:"l,attr,omitempty"` + S bool `xml:"s,attr,omitempty"` + T bool `xml:"t,attr,omitempty"` + A bool `xml:"a,attr,omitempty"` +}