resolve the issue corrupted xlsx after deleting formula of cell, reference #346

formula
xuri 6 years ago
parent e780e41e02
commit 0072bb7310
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7

@ -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:]...)
}
}
}
}

@ -305,6 +305,11 @@ func (f *File) SetCellFormula(sheet, axis, formula string) {
completeRow(xlsx, rows, cell) completeRow(xlsx, rows, cell)
completeCol(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 { if xlsx.SheetData.Row[xAxis].C[yAxis].F != nil {
xlsx.SheetData.Row[xAxis].C[yAxis].F.Content = formula xlsx.SheetData.Row[xAxis].C[yAxis].F.Content = formula
} else { } else {

@ -322,6 +322,10 @@ func (f *File) InsertCol(sheet, column string) {
// //
// xlsx.RemoveCol("Sheet1", "C") // 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) { func (f *File) RemoveCol(sheet, column string) {
xlsx := f.workSheetReader(sheet) xlsx := f.workSheetReader(sheet)
for r := range xlsx.SheetData.Row { for r := range xlsx.SheetData.Row {

@ -25,6 +25,7 @@ import (
type File struct { type File struct {
checked map[string]bool checked map[string]bool
sheetMap map[string]string sheetMap map[string]string
CalcChain *xlsxCalcChain
ContentTypes *xlsxTypes ContentTypes *xlsxTypes
Path string Path string
SharedStrings *xlsxSST SharedStrings *xlsxSST
@ -201,7 +202,8 @@ func (f *File) UpdateLinkedValue() {
// row: Index number of the row we're inserting/deleting before // row: Index number of the row we're inserting/deleting before
// offset: Number of rows/column to insert/delete negative values indicate deletion // 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) { func (f *File) adjustHelper(sheet string, column, row, offset int) {
xlsx := f.workSheetReader(sheet) xlsx := f.workSheetReader(sheet)

@ -342,8 +342,18 @@ func TestSetCellFormula(t *testing.T) {
xlsx.SetCellFormula("Sheet1", "C19", "SUM(Sheet2!D2,Sheet2!D9)") xlsx.SetCellFormula("Sheet1", "C19", "SUM(Sheet2!D2,Sheet2!D9)")
// Test set cell formula with illegal rows number. // Test set cell formula with illegal rows number.
xlsx.SetCellFormula("Sheet1", "C", "SUM(Sheet2!D2,Sheet2!D9)") 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) { func TestSetSheetBackground(t *testing.T) {

@ -92,6 +92,7 @@ func (f *File) WriteToBuffer() (*bytes.Buffer, error) {
f.workbookRelsWriter() f.workbookRelsWriter()
f.worksheetWriter() f.worksheetWriter()
f.styleSheetWriter() f.styleSheetWriter()
f.calcChainWriter()
for path, content := range f.XLSX { for path, content := range f.XLSX {
fi, err := zw.Create(path) fi, err := zw.Create(path)
if err != nil { if err != nil {

@ -343,6 +343,10 @@ func (f *File) GetRowOutlineLevel(sheet string, rowIndex int) uint8 {
// //
// xlsx.RemoveRow("Sheet1", 2) // 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) { func (f *File) RemoveRow(sheet string, row int) {
if row < 0 { if row < 0 {
return return
@ -375,15 +379,23 @@ func (f *File) InsertRow(sheet string, row int) {
// //
// xlsx.DuplicateRow("Sheet1", 2) // 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) { func (f *File) DuplicateRow(sheet string, row int) {
f.DuplicateRowTo(sheet, row, row+1) f.DuplicateRowTo(sheet, row, row+1)
} }
// DuplicateRowTo inserts a copy of specified row at specified row position // 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) // 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) { func (f *File) DuplicateRowTo(sheet string, row, row2 int) {
if row <= 0 || row2 <= 0 || row == row2 { if row <= 0 || row2 <= 0 || row == row2 {
return return

@ -900,7 +900,7 @@ func (p *PageLayoutPaperSize) getPageLayout(ps *xlsxPageSetUp) {
// 40 | German standard fanfold (8.5 in. by 12 in.) // 40 | German standard fanfold (8.5 in. by 12 in.)
// 41 | German legal fanfold (8.5 in. by 13 in.) // 41 | German legal fanfold (8.5 in. by 13 in.)
// 42 | ISO B4 (250 mm by 353 mm) // 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.) // 44 | Standard paper (9 in. by 11 in.)
// 45 | Standard paper (10 in. by 11 in.) // 45 | Standard paper (10 in. by 11 in.)
// 46 | Standard paper (15 in. by 11 in.) // 46 | Standard paper (15 in. by 11 in.)

Binary file not shown.

@ -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"`
}
Loading…
Cancel
Save