Selaa lähdekoodia

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

xuri 6 vuotta sitten
vanhempi
commit
0072bb7310
10 muutettua tiedostoa jossa 121 lisäystä ja 4 poistoa
  1. 55 0
      calcchain.go
  2. 5 0
      cell.go
  3. 4 0
      col.go
  4. 3 1
      excelize.go
  5. 11 1
      excelize_test.go
  6. 1 0
      file.go
  7. 13 1
      rows.go
  8. 1 1
      sheet.go
  9. BIN
      test/CalcChain.xlsx
  10. 28 0
      xmlCalcChain.go

+ 55 - 0
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:]...)
+			}
+		}
+	}
+}

+ 5 - 0
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 {

+ 4 - 0
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 {

+ 3 - 1
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)

+ 11 - 1
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) {

+ 1 - 0
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 {

+ 13 - 1
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

+ 1 - 1
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.)

BIN
test/CalcChain.xlsx


+ 28 - 0
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"`
+}