summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxuri <xuri.me@gmail.com>2021-07-05 00:03:56 +0800
committerxuri <xuri.me@gmail.com>2021-07-05 00:03:56 +0800
commit544ef18a8cb9949fcb8833c6d2816783c90f3318 (patch)
tree88bb3eaa9d92522d3b5c4eeb052210c26bc4c99f
parent0e02329bedf6648259fd219642bb907bdb07fd21 (diff)
- Support concurrency iterate rows and columns
- Rename exported field `File.XLSX` to `File.Pkg` - Exported error message
-rw-r--r--adjust_test.go18
-rw-r--r--calcchain.go2
-rw-r--r--calcchain_test.go2
-rw-r--r--cell.go12
-rw-r--r--cell_test.go42
-rw-r--r--chart.go14
-rw-r--r--chart_test.go14
-rw-r--r--col.go7
-rw-r--r--col_test.go18
-rw-r--r--comment.go22
-rw-r--r--comment_test.go6
-rw-r--r--crypt.go11
-rw-r--r--docProps_test.go8
-rw-r--r--drawing.go2
-rw-r--r--drawing_test.go6
-rw-r--r--errors.go34
-rw-r--r--excelize.go67
-rw-r--r--excelize_test.go34
-rw-r--r--file.go48
-rw-r--r--file_test.go15
-rw-r--r--lib.go6
-rw-r--r--merge_test.go28
-rw-r--r--picture.go45
-rw-r--r--picture_test.go13
-rw-r--r--pivotTable.go23
-rw-r--r--pivotTable_test.go4
-rw-r--r--rows.go7
-rw-r--r--rows_test.go14
-rw-r--r--sheet.go55
-rw-r--r--sheet_test.go14
-rw-r--r--sheetpr_test.go8
-rw-r--r--sparkline.go2
-rw-r--r--sparkline_test.go4
-rw-r--r--stream.go6
-rw-r--r--stream_test.go8
-rw-r--r--styles.go7
-rw-r--r--styles_test.go6
-rw-r--r--table.go7
38 files changed, 377 insertions, 262 deletions
diff --git a/adjust_test.go b/adjust_test.go
index 0d63ed6..c4af38b 100644
--- a/adjust_test.go
+++ b/adjust_test.go
@@ -73,20 +73,10 @@ func TestAdjustAutoFilter(t *testing.T) {
func TestAdjustHelper(t *testing.T) {
f := NewFile()
f.NewSheet("Sheet2")
- f.Sheet["xl/worksheets/sheet1.xml"] = &xlsxWorksheet{
- MergeCells: &xlsxMergeCells{
- Cells: []*xlsxMergeCell{
- {
- Ref: "A:B1",
- },
- },
- },
- }
- f.Sheet["xl/worksheets/sheet2.xml"] = &xlsxWorksheet{
- AutoFilter: &xlsxAutoFilter{
- Ref: "A1:B",
- },
- }
+ f.Sheet.Store("xl/worksheets/sheet1.xml", &xlsxWorksheet{
+ MergeCells: &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:B1"}}}})
+ f.Sheet.Store("xl/worksheets/sheet2.xml", &xlsxWorksheet{
+ AutoFilter: &xlsxAutoFilter{Ref: "A1:B"}})
// testing adjustHelper with illegal cell coordinates.
assert.EqualError(t, f.adjustHelper("Sheet1", rows, 0, 0), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
assert.EqualError(t, f.adjustHelper("Sheet2", rows, 0, 0), `cannot convert cell "B" to coordinates: invalid cell name "B"`)
diff --git a/calcchain.go b/calcchain.go
index ea3080f..1b99a04 100644
--- a/calcchain.go
+++ b/calcchain.go
@@ -54,7 +54,7 @@ func (f *File) deleteCalcChain(index int, axis string) {
}
if len(calc.C) == 0 {
f.CalcChain = nil
- delete(f.XLSX, "xl/calcChain.xml")
+ f.Pkg.Delete("xl/calcChain.xml")
content := f.contentTypesReader()
for k, v := range content.Overrides {
if v.PartName == "/xl/calcChain.xml" {
diff --git a/calcchain_test.go b/calcchain_test.go
index 842dde1..4956f60 100644
--- a/calcchain_test.go
+++ b/calcchain_test.go
@@ -5,7 +5,7 @@ import "testing"
func TestCalcChainReader(t *testing.T) {
f := NewFile()
f.CalcChain = nil
- f.XLSX["xl/calcChain.xml"] = MacintoshCyrillicCharset
+ f.Pkg.Store("xl/calcChain.xml", MacintoshCyrillicCharset)
f.calcChainReader()
}
diff --git a/cell.go b/cell.go
index 4dec093..1d08c8a 100644
--- a/cell.go
+++ b/cell.go
@@ -13,7 +13,6 @@ package excelize
import (
"encoding/xml"
- "errors"
"fmt"
"reflect"
"strconv"
@@ -187,6 +186,8 @@ func (f *File) SetCellInt(sheet, axis string, value int) error {
if err != nil {
return err
}
+ ws.Lock()
+ defer ws.Unlock()
cellData.S = f.prepareCellStyle(ws, col, cellData.S)
cellData.T, cellData.V = setCellInt(value)
return err
@@ -262,6 +263,8 @@ func (f *File) SetCellStr(sheet, axis, value string) error {
if err != nil {
return err
}
+ ws.Lock()
+ defer ws.Unlock()
cellData.S = f.prepareCellStyle(ws, col, cellData.S)
cellData.T, cellData.V = f.setCellString(value)
return err
@@ -742,7 +745,7 @@ func (f *File) SetSheetRow(sheet, axis string, slice interface{}) error {
// Make sure 'slice' is a Ptr to Slice
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Slice {
- return errors.New("pointer to slice expected")
+ return ErrParameterInvalid
}
v = v.Elem()
@@ -762,8 +765,6 @@ func (f *File) SetSheetRow(sheet, axis string, slice interface{}) error {
// getCellInfo does common preparation for all SetCell* methods.
func (f *File) prepareCell(ws *xlsxWorksheet, sheet, cell string) (*xlsxC, int, int, error) {
- ws.Lock()
- defer ws.Unlock()
var err error
cell, err = f.mergeCellsParser(ws, cell)
if err != nil {
@@ -775,7 +776,8 @@ func (f *File) prepareCell(ws *xlsxWorksheet, sheet, cell string) (*xlsxC, int,
}
prepareSheetXML(ws, col, row)
-
+ ws.Lock()
+ defer ws.Unlock()
return &ws.SheetData.Row[row-1].C[col-1], col, row, err
}
diff --git a/cell_test.go b/cell_test.go
index 2282e55..e289983 100644
--- a/cell_test.go
+++ b/cell_test.go
@@ -30,6 +30,20 @@ func TestConcurrency(t *testing.T) {
assert.Equal(t, "", name)
assert.Nil(t, raw)
assert.NoError(t, err)
+ // Concurrency iterate rows
+ rows, err := f.Rows("Sheet1")
+ assert.NoError(t, err)
+ for rows.Next() {
+ _, err := rows.Columns()
+ assert.NoError(t, err)
+ }
+ // Concurrency iterate columns
+ cols, err := f.Cols("Sheet1")
+ assert.NoError(t, err)
+ for rows.Next() {
+ _, err := cols.Rows()
+ assert.NoError(t, err)
+ }
wg.Done()
}(i, t)
@@ -149,8 +163,8 @@ func TestGetCellValue(t *testing.T) {
// Test get cell value without r attribute of the row.
f := NewFile()
sheetData := `<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>%s</sheetData></worksheet>`
- delete(f.Sheet, "xl/worksheets/sheet1.xml")
- f.XLSX["xl/worksheets/sheet1.xml"] = []byte(fmt.Sprintf(sheetData, `<row r="3"><c t="str"><v>A3</v></c></row><row><c t="str"><v>A4</v></c><c t="str"><v>B4</v></c></row><row r="7"><c t="str"><v>A7</v></c><c t="str"><v>B7</v></c></row><row><c t="str"><v>A8</v></c><c t="str"><v>B8</v></c></row>`))
+ f.Sheet.Delete("xl/worksheets/sheet1.xml")
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `<row r="3"><c t="str"><v>A3</v></c></row><row><c t="str"><v>A4</v></c><c t="str"><v>B4</v></c></row><row r="7"><c t="str"><v>A7</v></c><c t="str"><v>B7</v></c></row><row><c t="str"><v>A8</v></c><c t="str"><v>B8</v></c></row>`)))
f.checked = nil
cells := []string{"A3", "A4", "B4", "A7", "B7"}
rows, err := f.GetRows("Sheet1")
@@ -164,20 +178,20 @@ func TestGetCellValue(t *testing.T) {
cols, err := f.GetCols("Sheet1")
assert.Equal(t, [][]string{{"", "", "A3", "A4", "", "", "A7", "A8"}, {"", "", "", "B4", "", "", "B7", "B8"}}, cols)
assert.NoError(t, err)
- delete(f.Sheet, "xl/worksheets/sheet1.xml")
- f.XLSX["xl/worksheets/sheet1.xml"] = []byte(fmt.Sprintf(sheetData, `<row r="2"><c r="A2" t="str"><v>A2</v></c></row><row r="2"><c r="B2" t="str"><v>B2</v></c></row>`))
+ f.Sheet.Delete("xl/worksheets/sheet1.xml")
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `<row r="2"><c r="A2" t="str"><v>A2</v></c></row><row r="2"><c r="B2" t="str"><v>B2</v></c></row>`)))
f.checked = nil
cell, err := f.GetCellValue("Sheet1", "A2")
assert.Equal(t, "A2", cell)
assert.NoError(t, err)
- delete(f.Sheet, "xl/worksheets/sheet1.xml")
- f.XLSX["xl/worksheets/sheet1.xml"] = []byte(fmt.Sprintf(sheetData, `<row r="2"><c r="A2" t="str"><v>A2</v></c></row><row r="2"><c r="B2" t="str"><v>B2</v></c></row>`))
+ f.Sheet.Delete("xl/worksheets/sheet1.xml")
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `<row r="2"><c r="A2" t="str"><v>A2</v></c></row><row r="2"><c r="B2" t="str"><v>B2</v></c></row>`)))
f.checked = nil
rows, err = f.GetRows("Sheet1")
assert.Equal(t, [][]string{nil, {"A2", "B2"}}, rows)
assert.NoError(t, err)
- delete(f.Sheet, "xl/worksheets/sheet1.xml")
- f.XLSX["xl/worksheets/sheet1.xml"] = []byte(fmt.Sprintf(sheetData, `<row r="1"><c r="A1" t="str"><v>A1</v></c></row><row r="1"><c r="B1" t="str"><v>B1</v></c></row>`))
+ f.Sheet.Delete("xl/worksheets/sheet1.xml")
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `<row r="1"><c r="A1" t="str"><v>A1</v></c></row><row r="1"><c r="B1" t="str"><v>B1</v></c></row>`)))
f.checked = nil
rows, err = f.GetRows("Sheet1")
assert.Equal(t, [][]string{{"A1", "B1"}}, rows)
@@ -264,17 +278,23 @@ func TestGetCellRichText(t *testing.T) {
assert.True(t, reflect.DeepEqual(runsSource[1].Font, runs[1].Font), "should get the same font")
// Test get cell rich text when string item index overflow
- f.Sheet["xl/worksheets/sheet1.xml"].SheetData.Row[0].C[0].V = "2"
+ ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).SheetData.Row[0].C[0].V = "2"
runs, err = f.GetCellRichText("Sheet1", "A1")
assert.NoError(t, err)
assert.Equal(t, 0, len(runs))
// Test get cell rich text when string item index is negative
- f.Sheet["xl/worksheets/sheet1.xml"].SheetData.Row[0].C[0].V = "-1"
+ ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).SheetData.Row[0].C[0].V = "-1"
runs, err = f.GetCellRichText("Sheet1", "A1")
assert.NoError(t, err)
assert.Equal(t, 0, len(runs))
// Test get cell rich text on invalid string item index
- f.Sheet["xl/worksheets/sheet1.xml"].SheetData.Row[0].C[0].V = "x"
+ ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).SheetData.Row[0].C[0].V = "x"
_, err = f.GetCellRichText("Sheet1", "A1")
assert.EqualError(t, err, "strconv.Atoi: parsing \"x\": invalid syntax")
// Test set cell rich text on not exists worksheet
diff --git a/chart.go b/chart.go
index 9bc4b20..52fd543 100644
--- a/chart.go
+++ b/chart.go
@@ -14,7 +14,6 @@ package excelize
import (
"encoding/json"
"encoding/xml"
- "errors"
"fmt"
"strconv"
"strings"
@@ -945,7 +944,7 @@ func (f *File) AddChartSheet(sheet, format string, combo ...string) error {
sheetID++
path := "xl/chartsheets/sheet" + strconv.Itoa(sheetID) + ".xml"
f.sheetMap[trimSheetName(sheet)] = path
- f.Sheet[path] = nil
+ f.Sheet.Store(path, nil)
drawingID := f.countDrawings() + 1
chartID := f.countCharts() + 1
drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
@@ -981,12 +980,12 @@ func (f *File) getFormatChart(format string, combo []string) (*formatChart, []*f
return formatSet, comboCharts, err
}
if _, ok := chartValAxNumFmtFormatCode[comboChart.Type]; !ok {
- return formatSet, comboCharts, errors.New("unsupported chart type " + comboChart.Type)
+ return formatSet, comboCharts, newUnsupportChartType(comboChart.Type)
}
comboCharts = append(comboCharts, comboChart)
}
if _, ok := chartValAxNumFmtFormatCode[formatSet.Type]; !ok {
- return formatSet, comboCharts, errors.New("unsupported chart type " + formatSet.Type)
+ return formatSet, comboCharts, newUnsupportChartType(formatSet.Type)
}
return formatSet, comboCharts, err
}
@@ -1015,11 +1014,12 @@ func (f *File) DeleteChart(sheet, cell string) (err error) {
// folder xl/charts.
func (f *File) countCharts() int {
count := 0
- for k := range f.XLSX {
- if strings.Contains(k, "xl/charts/chart") {
+ f.Pkg.Range(func(k, v interface{}) bool {
+ if strings.Contains(k.(string), "xl/charts/chart") {
count++
}
- }
+ return true
+ })
return count
}
diff --git a/chart_test.go b/chart_test.go
index 657230b..8957b93 100644
--- a/chart_test.go
+++ b/chart_test.go
@@ -65,10 +65,10 @@ func TestChartSize(t *testing.T) {
anchor decodeTwoCellAnchor
)
- content, ok := newFile.XLSX["xl/drawings/drawing1.xml"]
+ content, ok := newFile.Pkg.Load("xl/drawings/drawing1.xml")
assert.True(t, ok, "Can't open the chart")
- err = xml.Unmarshal([]byte(content), &workdir)
+ err = xml.Unmarshal(content.([]byte), &workdir)
if !assert.NoError(t, err) {
t.FailNow()
}
@@ -340,11 +340,15 @@ func TestChartWithLogarithmicBase(t *testing.T) {
type xmlChartContent []byte
xmlCharts := make([]xmlChartContent, expectedChartsCount)
expectedChartsLogBase := []float64{0, 10.5, 0, 2, 0, 1000}
- var ok bool
-
+ var (
+ drawingML interface{}
+ ok bool
+ )
for i := 0; i < expectedChartsCount; i++ {
chartPath := fmt.Sprintf("xl/charts/chart%d.xml", i+1)
- xmlCharts[i], ok = newFile.XLSX[chartPath]
+ if drawingML, ok = newFile.Pkg.Load(chartPath); ok {
+ xmlCharts[i] = drawingML.([]byte)
+ }
assert.True(t, ok, "Can't open the %s", chartPath)
err = xml.Unmarshal([]byte(xmlCharts[i]), &chartSpaces[i])
diff --git a/col.go b/col.go
index 2fd90b2..91ca3da 100644
--- a/col.go
+++ b/col.go
@@ -199,8 +199,11 @@ func (f *File) Cols(sheet string) (*Cols, error) {
if !ok {
return nil, ErrSheetNotExist{sheet}
}
- if f.Sheet[name] != nil {
- output, _ := xml.Marshal(f.Sheet[name])
+ if ws, ok := f.Sheet.Load(name); ok && ws != nil {
+ worksheet := ws.(*xlsxWorksheet)
+ worksheet.Lock()
+ defer worksheet.Unlock()
+ output, _ := xml.Marshal(worksheet)
f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
}
var colIterator columnXMLIterator
diff --git a/col_test.go b/col_test.go
index 6ab5e57..8159a11 100644
--- a/col_test.go
+++ b/col_test.go
@@ -48,11 +48,11 @@ func TestCols(t *testing.T) {
_, err = f.Rows("Sheet1")
assert.NoError(t, err)
- f.Sheet["xl/worksheets/sheet1.xml"] = &xlsxWorksheet{
+ f.Sheet.Store("xl/worksheets/sheet1.xml", &xlsxWorksheet{
Dimension: &xlsxDimension{
Ref: "C2:C4",
},
- }
+ })
_, err = f.Rows("Sheet1")
assert.NoError(t, err)
}
@@ -110,15 +110,15 @@ func TestGetColsError(t *testing.T) {
assert.EqualError(t, err, "sheet SheetN is not exist")
f = NewFile()
- delete(f.Sheet, "xl/worksheets/sheet1.xml")
- f.XLSX["xl/worksheets/sheet1.xml"] = []byte(`<worksheet><sheetData><row r="A"><c r="2" t="str"><v>B</v></c></row></sheetData></worksheet>`)
+ f.Sheet.Delete("xl/worksheets/sheet1.xml")
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<worksheet><sheetData><row r="A"><c r="2" t="str"><v>B</v></c></row></sheetData></worksheet>`))
f.checked = nil
_, err = f.GetCols("Sheet1")
assert.EqualError(t, err, `strconv.Atoi: parsing "A": invalid syntax`)
f = NewFile()
- delete(f.Sheet, "xl/worksheets/sheet1.xml")
- f.XLSX["xl/worksheets/sheet1.xml"] = []byte(`<worksheet><sheetData><row r="2"><c r="A" t="str"><v>B</v></c></row></sheetData></worksheet>`)
+ f.Sheet.Delete("xl/worksheets/sheet1.xml")
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<worksheet><sheetData><row r="2"><c r="A" t="str"><v>B</v></c></row></sheetData></worksheet>`))
f.checked = nil
_, err = f.GetCols("Sheet1")
assert.EqualError(t, err, `cannot convert cell "A" to coordinates: invalid cell name "A"`)
@@ -142,14 +142,14 @@ func TestColsRows(t *testing.T) {
assert.NoError(t, err)
assert.NoError(t, f.SetCellValue("Sheet1", "A1", 1))
- f.Sheet["xl/worksheets/sheet1.xml"] = &xlsxWorksheet{
+ f.Sheet.Store("xl/worksheets/sheet1.xml", &xlsxWorksheet{
Dimension: &xlsxDimension{
Ref: "A1:A1",
},
- }
+ })
f = NewFile()
- f.XLSX["xl/worksheets/sheet1.xml"] = nil
+ f.Pkg.Store("xl/worksheets/sheet1.xml", nil)
_, err = f.Cols("Sheet1")
if !assert.NoError(t, err) {
t.FailNow()
diff --git a/comment.go b/comment.go
index b05f308..306826d 100644
--- a/comment.go
+++ b/comment.go
@@ -299,11 +299,12 @@ func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
// the folder xl.
func (f *File) countComments() int {
c1, c2 := 0, 0
- for k := range f.XLSX {
- if strings.Contains(k, "xl/comments") {
+ f.Pkg.Range(func(k, v interface{}) bool {
+ if strings.Contains(k.(string), "xl/comments") {
c1++
}
- }
+ return true
+ })
for rel := range f.Comments {
if strings.Contains(rel, "xl/comments") {
c2++
@@ -321,10 +322,10 @@ func (f *File) decodeVMLDrawingReader(path string) *decodeVmlDrawing {
var err error
if f.DecodeVMLDrawing[path] == nil {
- c, ok := f.XLSX[path]
- if ok {
+ c, ok := f.Pkg.Load(path)
+ if ok && c != nil {
f.DecodeVMLDrawing[path] = new(decodeVmlDrawing)
- if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(c))).
+ if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(c.([]byte)))).
Decode(f.DecodeVMLDrawing[path]); err != nil && err != io.EOF {
log.Printf("xml decode error: %s", err)
}
@@ -339,7 +340,7 @@ func (f *File) vmlDrawingWriter() {
for path, vml := range f.VMLDrawing {
if vml != nil {
v, _ := xml.Marshal(vml)
- f.XLSX[path] = v
+ f.Pkg.Store(path, v)
}
}
}
@@ -348,12 +349,11 @@ func (f *File) vmlDrawingWriter() {
// after deserialization of xl/comments%d.xml.
func (f *File) commentsReader(path string) *xlsxComments {
var err error
-
if f.Comments[path] == nil {
- content, ok := f.XLSX[path]
- if ok {
+ content, ok := f.Pkg.Load(path)
+ if ok && content != nil {
f.Comments[path] = new(xlsxComments)
- if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content))).
+ if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).
Decode(f.Comments[path]); err != nil && err != io.EOF {
log.Printf("xml decode error: %s", err)
}
diff --git a/comment_test.go b/comment_test.go
index 19b705f..f1b60dc 100644
--- a/comment_test.go
+++ b/comment_test.go
@@ -36,7 +36,7 @@ func TestAddComments(t *testing.T) {
}
f.Comments["xl/comments2.xml"] = nil
- f.XLSX["xl/comments2.xml"] = []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><comments xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><authors><author>Excelize: </author></authors><commentList><comment ref="B7" authorId="0"><text><t>Excelize: </t></text></comment></commentList></comments>`)
+ f.Pkg.Store("xl/comments2.xml", []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><comments xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><authors><author>Excelize: </author></authors><commentList><comment ref="B7" authorId="0"><text><t>Excelize: </t></text></comment></commentList></comments>`))
comments := f.GetComments()
assert.EqualValues(t, 2, len(comments["Sheet1"]))
assert.EqualValues(t, 1, len(comments["Sheet2"]))
@@ -46,14 +46,14 @@ func TestAddComments(t *testing.T) {
func TestDecodeVMLDrawingReader(t *testing.T) {
f := NewFile()
path := "xl/drawings/vmlDrawing1.xml"
- f.XLSX[path] = MacintoshCyrillicCharset
+ f.Pkg.Store(path, MacintoshCyrillicCharset)
f.decodeVMLDrawingReader(path)
}
func TestCommentsReader(t *testing.T) {
f := NewFile()
path := "xl/comments1.xml"
- f.XLSX[path] = MacintoshCyrillicCharset
+ f.Pkg.Store(path, MacintoshCyrillicCharset)
f.commentsReader(path)
}
diff --git a/crypt.go b/crypt.go
index 88abd0e..a0096c9 100644
--- a/crypt.go
+++ b/crypt.go
@@ -21,7 +21,6 @@ import (
"encoding/base64"
"encoding/binary"
"encoding/xml"
- "errors"
"hash"
"math/rand"
"reflect"
@@ -145,7 +144,7 @@ func Decrypt(raw []byte, opt *Options) (packageBuf []byte, err error) {
case "standard":
return standardDecrypt(encryptionInfoBuf, encryptedPackageBuf, opt)
default:
- err = errors.New("unsupport encryption mechanism")
+ err = ErrUnsupportEncryptMechanism
}
return
}
@@ -265,7 +264,7 @@ func Encrypt(raw []byte, opt *Options) (packageBuf []byte, err error) {
}
// TODO: Create a new CFB.
_, _ = encryptedPackage, encryptionInfoBuffer
- err = errors.New("not support encryption currently")
+ err = ErrEncrypt
return
}
@@ -293,7 +292,7 @@ func extractPart(doc *mscfb.Reader) (encryptionInfoBuf, encryptedPackageBuf []by
// encryptionMechanism parse password-protected documents created mechanism.
func encryptionMechanism(buffer []byte) (mechanism string, err error) {
if len(buffer) < 4 {
- err = errors.New("unknown encryption mechanism")
+ err = ErrUnknownEncryptMechanism
return
}
versionMajor, versionMinor := binary.LittleEndian.Uint16(buffer[0:2]), binary.LittleEndian.Uint16(buffer[2:4])
@@ -306,7 +305,7 @@ func encryptionMechanism(buffer []byte) (mechanism string, err error) {
} else if (versionMajor == 3 || versionMajor == 4) && versionMinor == 3 {
mechanism = "extensible"
}
- err = errors.New("unsupport encryption mechanism")
+ err = ErrUnsupportEncryptMechanism
return
}
@@ -470,7 +469,6 @@ func convertPasswdToKey(passwd string, blockKey []byte, encryption Encryption) (
if len(key) < keyBytes {
tmp := make([]byte, 0x36)
key = append(key, tmp...)
- key = tmp
} else if len(key) > keyBytes {
key = key[:keyBytes]
}
@@ -599,7 +597,6 @@ func createIV(blockKey interface{}, encryption Encryption) ([]byte, error) {
if len(iv) < encryptedKey.BlockSize {
tmp := make([]byte, 0x36)
iv = append(iv, tmp...)
- iv = tmp
} else if len(iv) > encryptedKey.BlockSize {
iv = iv[0:encryptedKey.BlockSize]
}
diff --git a/docProps_test.go b/docProps_test.go
index 0cb6f71..40ae2dc 100644
--- a/docProps_test.go
+++ b/docProps_test.go
@@ -42,12 +42,12 @@ func TestSetDocProps(t *testing.T) {
Version: "1.0.0",
}))
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetDocProps.xlsx")))
- f.XLSX["docProps/core.xml"] = nil
+ f.Pkg.Store("docProps/core.xml", nil)
assert.NoError(t, f.SetDocProps(&DocProperties{}))
// Test unsupported charset
f = NewFile()
- f.XLSX["docProps/core.xml"] = MacintoshCyrillicCharset
+ f.Pkg.Store("docProps/core.xml", MacintoshCyrillicCharset)
assert.EqualError(t, f.SetDocProps(&DocProperties{}), "xml decode error: XML syntax error on line 1: invalid UTF-8")
}
@@ -59,13 +59,13 @@ func TestGetDocProps(t *testing.T) {
props, err := f.GetDocProps()
assert.NoError(t, err)
assert.Equal(t, props.Creator, "Microsoft Office User")
- f.XLSX["docProps/core.xml"] = nil
+ f.Pkg.Store("docProps/core.xml", nil)
_, err = f.GetDocProps()
assert.NoError(t, err)
// Test unsupported charset
f = NewFile()
- f.XLSX["docProps/core.xml"] = MacintoshCyrillicCharset
+ f.Pkg.Store("docProps/core.xml", MacintoshCyrillicCharset)
_, err = f.GetDocProps()
assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
}
diff --git a/drawing.go b/drawing.go
index 58e2669..181bb43 100644
--- a/drawing.go
+++ b/drawing.go
@@ -1151,7 +1151,7 @@ func (f *File) drawingParser(path string) (*xlsxWsDr, int) {
content := xlsxWsDr{}
content.A = NameSpaceDrawingML.Value
content.Xdr = NameSpaceDrawingMLSpreadSheet.Value
- if _, ok = f.XLSX[path]; ok { // Append Model
+ if _, ok = f.Pkg.Load(path); ok { // Append Model
decodeWsDr := decodeWsDr{}
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))).
Decode(&decodeWsDr); err != nil && err != io.EOF {
diff --git a/drawing_test.go b/drawing_test.go
index 3c0b619..f2413cf 100644
--- a/drawing_test.go
+++ b/drawing_test.go
@@ -19,10 +19,10 @@ import (
func TestDrawingParser(t *testing.T) {
f := File{
Drawings: sync.Map{},
- XLSX: map[string][]byte{
- "charset": MacintoshCyrillicCharset,
- "wsDr": []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"><xdr:oneCellAnchor><xdr:graphicFrame/></xdr:oneCellAnchor></xdr:wsDr>`)},
+ Pkg: sync.Map{},
}
+ f.Pkg.Store("charset", MacintoshCyrillicCharset)
+ f.Pkg.Store("wsDr", []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"><xdr:oneCellAnchor><xdr:graphicFrame/></xdr:oneCellAnchor></xdr:wsDr>`))
// Test with one cell anchor
f.drawingParser("wsDr")
// Test with unsupported charset
diff --git a/errors.go b/errors.go
index a0c61c8..4931198 100644
--- a/errors.go
+++ b/errors.go
@@ -32,6 +32,10 @@ func newInvalidExcelDateError(dateValue float64) error {
return fmt.Errorf("invalid date value %f, negative values are not supported supported", dateValue)
}
+func newUnsupportChartType(chartType string) error {
+ return fmt.Errorf("unsupported chart type %s", chartType)
+}
+
var (
// ErrStreamSetColWidth defined the error message on set column width in
// stream writing mode.
@@ -71,4 +75,34 @@ var (
// ErrMaxFileNameLength defined the error message on receive the file name
// length overflow.
ErrMaxFileNameLength = errors.New("file name length exceeds maximum limit")
+ // ErrEncrypt defined the error message on encryption spreadsheet.
+ ErrEncrypt = errors.New("not support encryption currently")
+ // ErrUnknownEncryptMechanism defined the error message on unsupport
+ // encryption mechanism.
+ ErrUnknownEncryptMechanism = errors.New("unknown encryption mechanism")
+ // ErrUnsupportEncryptMechanism defined the error message on unsupport
+ // encryption mechanism.
+ ErrUnsupportEncryptMechanism = errors.New("unsupport encryption mechanism")
+ // ErrParameterRequired defined the error message on receive the empty
+ // parameter.
+ ErrParameterRequired = errors.New("parameter is required")
+ // ErrParameterInvalid defined the error message on receive the invalid
+ // parameter.
+ ErrParameterInvalid = errors.New("parameter is invalid")
+ // ErrDefinedNameScope defined the error message on not found defined name
+ // in the given scope.
+ ErrDefinedNameScope = errors.New("no defined name on the scope")
+ // ErrDefinedNameduplicate defined the error message on the same name
+ // already exists on the scope.
+ ErrDefinedNameduplicate = errors.New("the same name already exists on the scope")
+ // ErrFontLength defined the error message on the length of the font
+ // family name overflow.
+ ErrFontLength = errors.New("the length of the font family name must be smaller than or equal to 31")
+ // ErrFontSize defined the error message on the size of the font is invalid.
+ ErrFontSize = errors.New("font size must be between 1 and 409 points")
+ // ErrSheetIdx defined the error message on receive the invalid worksheet
+ // index.
+ ErrSheetIdx = errors.New("invalid worksheet index")
+ // ErrGroupSheets defined the error message on group sheets.
+ ErrGroupSheets = errors.New("group worksheet must contain an active worksheet")
)
diff --git a/excelize.go b/excelize.go
index 66cfd00..bdb7120 100644
--- a/excelize.go
+++ b/excelize.go
@@ -43,7 +43,7 @@ type File struct {
Path string
SharedStrings *xlsxSST
sharedStringsMap map[string]int
- Sheet map[string]*xlsxWorksheet
+ Sheet sync.Map // map[string]*xlsxWorksheet
SheetCount int
Styles *xlsxStyleSheet
Theme *xlsxTheme
@@ -51,7 +51,7 @@ type File struct {
VMLDrawing map[string]*vmlDrawing
WorkBook *xlsxWorkbook
Relationships sync.Map
- XLSX map[string][]byte
+ Pkg sync.Map
CharsetReader charsetTranscoderFn
}
@@ -95,7 +95,7 @@ func newFile() *File {
Comments: make(map[string]*xlsxComments),
Drawings: sync.Map{},
sharedStringsMap: make(map[string]int),
- Sheet: make(map[string]*xlsxWorksheet),
+ Sheet: sync.Map{},
DecodeVMLDrawing: make(map[string]*decodeVmlDrawing),
VMLDrawing: make(map[string]*vmlDrawing),
Relationships: sync.Map{},
@@ -129,7 +129,10 @@ func OpenReader(r io.Reader, opt ...Options) (*File, error) {
if err != nil {
return nil, err
}
- f.SheetCount, f.XLSX = sheetCount, file
+ f.SheetCount = sheetCount
+ for k, v := range file {
+ f.Pkg.Store(k, v)
+ }
f.CalcChain = f.calcChainReader()
f.sheetMap = f.getSheetMap()
f.Styles = f.stylesReader()
@@ -172,40 +175,40 @@ func (f *File) workSheetReader(sheet string) (ws *xlsxWorksheet, err error) {
name string
ok bool
)
-
if name, ok = f.sheetMap[trimSheetName(sheet)]; !ok {
err = fmt.Errorf("sheet %s is not exist", sheet)
return
}
- if ws = f.Sheet[name]; f.Sheet[name] == nil {
- if strings.HasPrefix(name, "xl/chartsheets") {
- err = fmt.Errorf("sheet %s is chart sheet", sheet)
- return
- }
- ws = new(xlsxWorksheet)
- if _, ok := f.xmlAttr[name]; !ok {
- d := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(name))))
- f.xmlAttr[name] = append(f.xmlAttr[name], getRootElement(d)...)
- }
- if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(name)))).
- Decode(ws); err != nil && err != io.EOF {
- err = fmt.Errorf("xml decode error: %s", err)
+ if worksheet, ok := f.Sheet.Load(name); ok && worksheet != nil {
+ ws = worksheet.(*xlsxWorksheet)
+ return
+ }
+ if strings.HasPrefix(name, "xl/chartsheets") {
+ err = fmt.Errorf("sheet %s is chart sheet", sheet)
+ return
+ }
+ ws = new(xlsxWorksheet)
+ if _, ok := f.xmlAttr[name]; !ok {
+ d := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(name))))
+ f.xmlAttr[name] = append(f.xmlAttr[name], getRootElement(d)...)
+ }
+ if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(name)))).
+ Decode(ws); err != nil && err != io.EOF {
+ err = fmt.Errorf("xml decode error: %s", err)
+ return
+ }
+ err = nil
+ if f.checked == nil {
+ f.checked = make(map[string]bool)
+ }
+ if ok = f.checked[name]; !ok {
+ checkSheet(ws)
+ if err = checkRow(ws); err != nil {
return
}
- err = nil
- if f.checked == nil {
- f.checked = make(map[string]bool)
- }
- if ok = f.checked[name]; !ok {
- checkSheet(ws)
- if err = checkRow(ws); err != nil {
- return
- }
- f.checked[name] = true
- }
- f.Sheet[name] = ws
+ f.checked[name] = true
}
-
+ f.Sheet.Store(name, ws)
return
}
@@ -375,7 +378,7 @@ func (f *File) AddVBAProject(bin string) error {
})
}
file, _ := ioutil.ReadFile(bin)
- f.XLSX["xl/vbaProject.bin"] = file
+ f.Pkg.Store("xl/vbaProject.bin", file)
return err
}
diff --git a/excelize_test.go b/excelize_test.go
index e3cfa53..22e39d1 100644
--- a/excelize_test.go
+++ b/excelize_test.go
@@ -343,13 +343,17 @@ func TestSetCellHyperLink(t *testing.T) {
f = NewFile()
_, err = f.workSheetReader("Sheet1")
assert.NoError(t, err)
- f.Sheet["xl/worksheets/sheet1.xml"].Hyperlinks = &xlsxHyperlinks{Hyperlink: make([]xlsxHyperlink, 65530)}
+ ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).Hyperlinks = &xlsxHyperlinks{Hyperlink: make([]xlsxHyperlink, 65530)}
assert.EqualError(t, f.SetCellHyperLink("Sheet1", "A65531", "https://github.com/360EntSecGroup-Skylar/excelize", "External"), ErrTotalSheetHyperlinks.Error())
f = NewFile()
_, err = f.workSheetReader("Sheet1")
assert.NoError(t, err)
- f.Sheet["xl/worksheets/sheet1.xml"].MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
+ ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
err = f.SetCellHyperLink("Sheet1", "A1", "https://github.com/360EntSecGroup-Skylar/excelize", "External")
assert.EqualError(t, err, `cannot convert cell "A" to coordinates: invalid cell name "A"`)
}
@@ -376,7 +380,9 @@ func TestGetCellHyperLink(t *testing.T) {
f = NewFile()
_, err = f.workSheetReader("Sheet1")
assert.NoError(t, err)
- f.Sheet["xl/worksheets/sheet1.xml"].Hyperlinks = &xlsxHyperlinks{
+ ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).Hyperlinks = &xlsxHyperlinks{
Hyperlink: []xlsxHyperlink{{Ref: "A1"}},
}
link, target, err = f.GetCellHyperLink("Sheet1", "A1")
@@ -384,7 +390,9 @@ func TestGetCellHyperLink(t *testing.T) {
assert.Equal(t, link, true)
assert.Equal(t, target, "")
- f.Sheet["xl/worksheets/sheet1.xml"].MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
+ ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
link, target, err = f.GetCellHyperLink("Sheet1", "A1")
assert.EqualError(t, err, `cannot convert cell "A" to coordinates: invalid cell name "A"`)
assert.Equal(t, link, false)
@@ -1112,8 +1120,8 @@ func TestSetSheetRow(t *testing.T) {
assert.EqualError(t, f.SetSheetRow("Sheet1", "", &[]interface{}{"cell", nil, 2}),
`cannot convert cell "" to coordinates: invalid cell name ""`)
- assert.EqualError(t, f.SetSheetRow("Sheet1", "B27", []interface{}{}), `pointer to slice expected`)
- assert.EqualError(t, f.SetSheetRow("Sheet1", "B27", &f), `pointer to slice expected`)
+ assert.EqualError(t, f.SetSheetRow("Sheet1", "B27", []interface{}{}), ErrParameterInvalid.Error())
+ assert.EqualError(t, f.SetSheetRow("Sheet1", "B27", &f), ErrParameterInvalid.Error())
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetSheetRow.xlsx")))
}
@@ -1198,7 +1206,7 @@ func TestContentTypesReader(t *testing.T) {
// Test unsupported charset.
f := NewFile()
f.ContentTypes = nil
- f.XLSX["[Content_Types].xml"] = MacintoshCyrillicCharset
+ f.Pkg.Store("[Content_Types].xml", MacintoshCyrillicCharset)
f.contentTypesReader()
}
@@ -1206,22 +1214,22 @@ func TestWorkbookReader(t *testing.T) {
// Test unsupported charset.
f := NewFile()
f.WorkBook = nil
- f.XLSX["xl/workbook.xml"] = MacintoshCyrillicCharset
+ f.Pkg.Store("xl/workbook.xml", MacintoshCyrillicCharset)
f.workbookReader()
}
func TestWorkSheetReader(t *testing.T) {
// Test unsupported charset.
f := NewFile()
- delete(f.Sheet, "xl/worksheets/sheet1.xml")
- f.XLSX["xl/worksheets/sheet1.xml"] = MacintoshCyrillicCharset
+ f.Sheet.Delete("xl/worksheets/sheet1.xml")
+ f.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset)
_, err := f.workSheetReader("Sheet1")
assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
// Test on no checked worksheet.
f = NewFile()
- delete(f.Sheet, "xl/worksheets/sheet1.xml")
- f.XLSX["xl/worksheets/sheet1.xml"] = []byte(`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData/></worksheet>`)
+ f.Sheet.Delete("xl/worksheets/sheet1.xml")
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData/></worksheet>`))
f.checked = nil
_, err = f.workSheetReader("Sheet1")
assert.NoError(t, err)
@@ -1232,7 +1240,7 @@ func TestRelsReader(t *testing.T) {
f := NewFile()
rels := "xl/_rels/workbook.xml.rels"
f.Relationships.Store(rels, nil)
- f.XLSX[rels] = MacintoshCyrillicCharset
+ f.Pkg.Store(rels, MacintoshCyrillicCharset)
f.relsReader(rels)
}
diff --git a/file.go b/file.go
index fa73ec8..1786e27 100644
--- a/file.go
+++ b/file.go
@@ -26,18 +26,17 @@ import (
// f := NewFile()
//
func NewFile() *File {
- file := make(map[string][]byte)
- file["_rels/.rels"] = []byte(XMLHeader + templateRels)
- file["docProps/app.xml"] = []byte(XMLHeader + templateDocpropsApp)
- file["docProps/core.xml"] = []byte(XMLHeader + templateDocpropsCore)
- file["xl/_rels/workbook.xml.rels"] = []byte(XMLHeader + templateWorkbookRels)
- file["xl/theme/theme1.xml"] = []byte(XMLHeader + templateTheme)
- file["xl/worksheets/sheet1.xml"] = []byte(XMLHeader + templateSheet)
- file["xl/styles.xml"] = []byte(XMLHeader + templateStyles)
- file["xl/workbook.xml"] = []byte(XMLHeader + templateWorkbook)
- file["[Content_Types].xml"] = []byte(XMLHeader + templateContentTypes)
f := newFile()
- f.SheetCount, f.XLSX = 1, file
+ f.Pkg.Store("_rels/.rels", []byte(XMLHeader+templateRels))
+ f.Pkg.Store("docProps/app.xml", []byte(XMLHeader+templateDocpropsApp))
+ f.Pkg.Store("docProps/core.xml", []byte(XMLHeader+templateDocpropsCore))
+ f.Pkg.Store("xl/_rels/workbook.xml.rels", []byte(XMLHeader+templateWorkbookRels))
+ f.Pkg.Store("xl/theme/theme1.xml", []byte(XMLHeader+templateTheme))
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(XMLHeader+templateSheet))
+ f.Pkg.Store("xl/styles.xml", []byte(XMLHeader+templateStyles))
+ f.Pkg.Store("xl/workbook.xml", []byte(XMLHeader+templateWorkbook))
+ f.Pkg.Store("[Content_Types].xml", []byte(XMLHeader+templateContentTypes))
+ f.SheetCount = 1
f.CalcChain = f.calcChainReader()
f.Comments = make(map[string]*xlsxComments)
f.ContentTypes = f.contentTypesReader()
@@ -48,8 +47,9 @@ func NewFile() *File {
f.WorkBook = f.workbookReader()
f.Relationships = sync.Map{}
f.Relationships.Store("xl/_rels/workbook.xml.rels", f.relsReader("xl/_rels/workbook.xml.rels"))
- f.Sheet["xl/worksheets/sheet1.xml"], _ = f.workSheetReader("Sheet1")
f.sheetMap["Sheet1"] = "xl/worksheets/sheet1.xml"
+ ws, _ := f.workSheetReader("Sheet1")
+ f.Sheet.Store("xl/worksheets/sheet1.xml", ws)
f.Theme = f.themeReader()
return f
}
@@ -165,20 +165,22 @@ func (f *File) writeToZip(zw *zip.Writer) error {
}
stream.rawData.Close()
}
-
- for path, content := range f.XLSX {
- if _, ok := f.streams[path]; ok {
- continue
- }
- fi, err := zw.Create(path)
+ var err error
+ f.Pkg.Range(func(path, content interface{}) bool {
if err != nil {
- return err
+ return false
}
- _, err = fi.Write(content)
+ if _, ok := f.streams[path.(string)]; ok {
+ return true
+ }
+ var fi io.Writer
+ fi, err = zw.Create(path.(string))
if err != nil {
- return err
+ return false
}
- }
+ _, err = fi.Write(content.([]byte))
+ return true
+ })
- return nil
+ return err
}
diff --git a/file_test.go b/file_test.go
index dbbf75a..d86ce53 100644
--- a/file_test.go
+++ b/file_test.go
@@ -5,6 +5,7 @@ import (
"bytes"
"os"
"strings"
+ "sync"
"testing"
"github.com/stretchr/testify/assert"
@@ -38,19 +39,19 @@ func TestWriteTo(t *testing.T) {
{
f := File{}
buf := bytes.Buffer{}
- f.XLSX = make(map[string][]byte)
- f.XLSX["/d/"] = []byte("s")
+ f.Pkg = sync.Map{}
+ f.Pkg.Store("/d/", []byte("s"))
_, err := f.WriteTo(bufio.NewWriter(&buf))
assert.EqualError(t, err, "zip: write to directory")
- delete(f.XLSX, "/d/")
+ f.Pkg.Delete("/d/")
}
// Test file path overflow
{
f := File{}
buf := bytes.Buffer{}
- f.XLSX = make(map[string][]byte)
+ f.Pkg = sync.Map{}
const maxUint16 = 1<<16 - 1
- f.XLSX[strings.Repeat("s", maxUint16+1)] = nil
+ f.Pkg.Store(strings.Repeat("s", maxUint16+1), nil)
_, err := f.WriteTo(bufio.NewWriter(&buf))
assert.EqualError(t, err, "zip: FileHeader.Name too long")
}
@@ -58,8 +59,8 @@ func TestWriteTo(t *testing.T) {
{
f := File{}
buf := bytes.Buffer{}
- f.XLSX = make(map[string][]byte)
- f.XLSX["s"] = nil
+ f.Pkg = sync.Map{}
+ f.Pkg.Store("s", nil)
f.streams = make(map[string]*StreamWriter)
file, _ := os.Open("123")
f.streams["s"] = &StreamWriter{rawData: bufferedWriter{tmp: file}}
diff --git a/lib.go b/lib.go
index 5c9bbf6..00a67d9 100644
--- a/lib.go
+++ b/lib.go
@@ -51,8 +51,8 @@ func ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
// readXML provides a function to read XML content as string.
func (f *File) readXML(name string) []byte {
- if content, ok := f.XLSX[name]; ok {
- return content
+ if content, _ := f.Pkg.Load(name); content != nil {
+ return content.([]byte)
}
if content, ok := f.streams[name]; ok {
return content.rawData.buf.Bytes()
@@ -66,7 +66,7 @@ func (f *File) saveFileList(name string, content []byte) {
newContent := make([]byte, 0, len(XMLHeader)+len(content))
newContent = append(newContent, []byte(XMLHeader)...)
newContent = append(newContent, content...)
- f.XLSX[name] = newContent
+ f.Pkg.Store(name, newContent)
}
// Read file content as string in a archive file.
diff --git a/merge_test.go b/merge_test.go
index afe75aa..cf460dd 100644
--- a/merge_test.go
+++ b/merge_test.go
@@ -71,13 +71,19 @@ func TestMergeCell(t *testing.T) {
f = NewFile()
assert.NoError(t, f.MergeCell("Sheet1", "A2", "B3"))
- f.Sheet["xl/worksheets/sheet1.xml"].MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{nil, nil}}
+ ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{nil, nil}}
assert.NoError(t, f.MergeCell("Sheet1", "A2", "B3"))
- f.Sheet["xl/worksheets/sheet1.xml"].MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A1"}}}
+ ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A1"}}}
assert.EqualError(t, f.MergeCell("Sheet1", "A2", "B3"), `invalid area "A1"`)
- f.Sheet["xl/worksheets/sheet1.xml"].MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
+ ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
assert.EqualError(t, f.MergeCell("Sheet1", "A2", "B3"), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
}
@@ -154,16 +160,24 @@ func TestUnmergeCell(t *testing.T) {
// Test unmerged area on not exists worksheet.
assert.EqualError(t, f.UnmergeCell("SheetN", "A1", "A1"), "sheet SheetN is not exist")
- f.Sheet["xl/worksheets/sheet1.xml"].MergeCells = nil
+ ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).MergeCells = nil
assert.NoError(t, f.UnmergeCell("Sheet1", "H7", "B15"))
- f.Sheet["xl/worksheets/sheet1.xml"].MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{nil, nil}}
+ ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{nil, nil}}
assert.NoError(t, f.UnmergeCell("Sheet1", "H15", "B7"))
- f.Sheet["xl/worksheets/sheet1.xml"].MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A1"}}}
+ ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A1"}}}
assert.EqualError(t, f.UnmergeCell("Sheet1", "A2", "B3"), `invalid area "A1"`)
- f.Sheet["xl/worksheets/sheet1.xml"].MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
+ ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
assert.EqualError(t, f.UnmergeCell("Sheet1", "A2", "B3"), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
}
diff --git a/picture.go b/picture.go
index 09283c8..58fa909 100644
--- a/picture.go
+++ b/picture.go
@@ -223,11 +223,12 @@ func (f *File) addSheetPicture(sheet string, rID int) {
// folder xl/drawings.
func (f *File) countDrawings() int {
c1, c2 := 0, 0
- for k := range f.XLSX {
- if strings.Contains(k, "xl/drawings/drawing") {
+ f.Pkg.Range(func(k, v interface{}) bool {
+ if strings.Contains(k.(string), "xl/drawings/drawing") {
c1++
}
- }
+ return true
+ })
f.Drawings.Range(func(rel, value interface{}) bool {
if strings.Contains(rel.(string), "xl/drawings/drawing") {
c2++
@@ -305,11 +306,12 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, he
// folder xl/media/image.
func (f *File) countMedia() int {
count := 0
- for k := range f.XLSX {
- if strings.Contains(k, "xl/media/image") {
+ f.Pkg.Range(func(k, v interface{}) bool {
+ if strings.Contains(k.(string), "xl/media/image") {
count++
}
- }
+ return true
+ })
return count
}
@@ -318,16 +320,22 @@ func (f *File) countMedia() int {
// and drawings that use it will reference the same image.
func (f *File) addMedia(file []byte, ext string) string {
count := f.countMedia()
- for name, existing := range f.XLSX {
- if !strings.HasPrefix(name, "xl/media/image") {
- continue
+ var name string
+ f.Pkg.Range(func(k, existing interface{}) bool {
+ if !strings.HasPrefix(k.(string), "xl/media/image") {
+ return true
}
- if bytes.Equal(file, existing) {
- return name
+ if bytes.Equal(file, existing.([]byte)) {
+ name = k.(string)
+ return false
}
+ return true
+ })
+ if name != "" {
+ return name
}
media := "xl/media/image" + strconv.Itoa(count+1) + ext
- f.XLSX[media] = file
+ f.Pkg.Store(media, file)
return media
}
@@ -468,8 +476,7 @@ func (f *File) GetPicture(sheet, cell string) (string, []byte, error) {
}
target := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)
drawingXML := strings.Replace(target, "..", "xl", -1)
- _, ok := f.XLSX[drawingXML]
- if !ok {
+ if _, ok := f.Pkg.Load(drawingXML); !ok {
return "", nil, err
}
drawingRelationships := strings.Replace(
@@ -532,7 +539,10 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
if deTwoCellAnchor.From.Col == col && deTwoCellAnchor.From.Row == row {
drawRel = f.getDrawingRelationships(drawingRelationships, deTwoCellAnchor.Pic.BlipFill.Blip.Embed)
if _, ok = supportImageTypes[filepath.Ext(drawRel.Target)]; ok {
- ret, buf = filepath.Base(drawRel.Target), f.XLSX[strings.Replace(drawRel.Target, "..", "xl", -1)]
+ ret = filepath.Base(drawRel.Target)
+ if buffer, _ := f.Pkg.Load(strings.Replace(drawRel.Target, "..", "xl", -1)); buffer != nil {
+ buf = buffer.([]byte)
+ }
return
}
}
@@ -556,7 +566,10 @@ func (f *File) getPictureFromWsDr(row, col int, drawingRelationships string, wsD
if drawRel = f.getDrawingRelationships(drawingRelationships,
anchor.Pic.BlipFill.Blip.Embed); drawRel != nil {
if _, ok = supportImageTypes[filepath.Ext(drawRel.Target)]; ok {
- ret, buf = filepath.Base(drawRel.Target), f.XLSX[strings.Replace(drawRel.Target, "..", "xl", -1)]
+ ret = filepath.Base(drawRel.Target)
+ if buffer, _ := f.Pkg.Load(strings.Replace(drawRel.Target, "..", "xl", -1)); buffer != nil {
+ buf = buffer.([]byte)
+ }
return
}
}
diff --git a/picture_test.go b/picture_test.go
index be917b8..69873eb 100644
--- a/picture_test.go
+++ b/picture_test.go
@@ -155,7 +155,7 @@ func TestGetPicture(t *testing.T) {
assert.Empty(t, raw)
f, err = prepareTestBook1()
assert.NoError(t, err)
- f.XLSX["xl/drawings/drawing1.xml"] = MacintoshCyrillicCharset
+ f.Pkg.Store("xl/drawings/drawing1.xml", MacintoshCyrillicCharset)
_, _, err = f.getPicture(20, 5, "xl/drawings/drawing1.xml", "xl/drawings/_rels/drawing2.xml.rels")
assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
}
@@ -173,11 +173,12 @@ func TestAddPictureFromBytes(t *testing.T) {
assert.NoError(t, f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", 1), "", "logo", ".png", imgFile))
assert.NoError(t, f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", 50), "", "logo", ".png", imgFile))
imageCount := 0
- for fileName := range f.XLSX {
- if strings.Contains(fileName, "media/image") {
+ f.Pkg.Range(func(fileName, v interface{}) bool {
+ if strings.Contains(fileName.(string), "media/image") {
imageCount++
}
- }
+ return true
+ })
assert.Equal(t, 1, imageCount, "Duplicate image should only be stored once.")
assert.EqualError(t, f.AddPictureFromBytes("SheetN", fmt.Sprint("A", 1), "", "logo", ".png", imgFile), "sheet SheetN is not exist")
}
@@ -205,6 +206,8 @@ func TestDrawingResize(t *testing.T) {
// Test calculate drawing resize with invalid coordinates.
_, _, _, _, err = f.drawingResize("Sheet1", "", 1, 1, nil)
assert.EqualError(t, err, `cannot convert cell "" to coordinates: invalid cell name ""`)
- f.Sheet["xl/worksheets/sheet1.xml"].MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
+ ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}}
assert.EqualError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.jpg"), `{"autofit": true}`), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
}
diff --git a/pivotTable.go b/pivotTable.go
index 3d93260..05ac783 100644
--- a/pivotTable.go
+++ b/pivotTable.go
@@ -13,7 +13,6 @@ package excelize
import (
"encoding/xml"
- "errors"
"fmt"
"strconv"
"strings"
@@ -163,7 +162,7 @@ func (f *File) AddPivotTable(opt *PivotTableOption) error {
// properties.
func (f *File) parseFormatPivotTableSet(opt *PivotTableOption) (*xlsxWorksheet, string, error) {
if opt == nil {
- return nil, "", errors.New("parameter is required")
+ return nil, "", ErrParameterRequired
}
pivotTableSheetName, _, err := f.adjustRange(opt.PivotTableRange)
if err != nil {
@@ -192,11 +191,11 @@ func (f *File) parseFormatPivotTableSet(opt *PivotTableOption) (*xlsxWorksheet,
// adjustRange adjust range, for example: adjust Sheet1!$E$31:$A$1 to Sheet1!$A$1:$E$31
func (f *File) adjustRange(rangeStr string) (string, []int, error) {
if len(rangeStr) < 1 {
- return "", []int{}, errors.New("parameter is required")
+ return "", []int{}, ErrParameterRequired
}
rng := strings.Split(rangeStr, "!")
if len(rng) != 2 {
- return "", []int{}, errors.New("parameter is invalid")
+ return "", []int{}, ErrParameterInvalid
}
trimRng := strings.Replace(rng[1], "$", "", -1)
coordinates, err := f.areaRefToCoordinates(trimRng)
@@ -205,7 +204,7 @@ func (f *File) adjustRange(rangeStr string) (string, []int, error) {
}
x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
if x1 == x2 && y1 == y2 {
- return rng[0], []int{}, errors.New("parameter is invalid")
+ return rng[0], []int{}, ErrParameterInvalid
}
// Correct the coordinate area, such correct C1:B3 to B1:C3.
@@ -600,11 +599,12 @@ func (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opt *PivotTableOptio
// the folder xl/pivotTables.
func (f *File) countPivotTables() int {
count := 0
- for k := range f.XLSX {
- if strings.Contains(k, "xl/pivotTables/pivotTable") {
+ f.Pkg.Range(func(k, v interface{}) bool {
+ if strings.Contains(k.(string), "xl/pivotTables/pivotTable") {
count++
}
- }
+ return true
+ })
return count
}
@@ -612,11 +612,12 @@ func (f *File) countPivotTables() int {
// the folder xl/pivotCache.
func (f *File) countPivotCache() int {
count := 0
- for k := range f.XLSX {
- if strings.Contains(k, "xl/pivotCache/pivotCacheDefinition") {
+ f.Pkg.Range(func(k, v interface{}) bool {
+ if strings.Contains(k.(string), "xl/pivotCache/pivotCacheDefinition") {
count++
}
- }
+ return true
+ })
return count
}
diff --git a/pivotTable_test.go b/pivotTable_test.go
index 7098b3a..e746d8d 100644
--- a/pivotTable_test.go
+++ b/pivotTable_test.go
@@ -137,12 +137,12 @@ func TestAddPivotTable(t *testing.T) {
ShowLastColumn: true,
}))
// Create pivot table with many data, many rows, many cols and defined name
- f.SetDefinedName(&DefinedName{
+ assert.NoError(t, f.SetDefinedName(&DefinedName{
Name: "dataRange",
RefersTo: "Sheet1!$A$1:$E$31",
Comment: "Pivot Table Data Range",
Scope: "Sheet2",
- })
+ }))
assert.NoError(t, f.AddPivotTable(&PivotTableOption{
DataRange: "dataRange",
PivotTableRange: "Sheet2!$A$57:$AJ$91",
diff --git a/rows.go b/rows.go
index 6360f4e..229b12d 100644
--- a/rows.go
+++ b/rows.go
@@ -195,9 +195,12 @@ func (f *File) Rows(sheet string) (*Rows, error) {
if !ok {
return nil, ErrSheetNotExist{sheet}
}
- if f.Sheet[name] != nil {
+ if ws, ok := f.Sheet.Load(name); ok && ws != nil {
+ worksheet := ws.(*xlsxWorksheet)
+ worksheet.Lock()
+ defer worksheet.Unlock()
// flush data
- output, _ := xml.Marshal(f.Sheet[name])
+ output, _ := xml.Marshal(worksheet)
f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
}
var (
diff --git a/rows_test.go b/rows_test.go
index 585fe59..069b668 100644
--- a/rows_test.go
+++ b/rows_test.go
@@ -43,11 +43,13 @@ func TestRows(t *testing.T) {
}
f = NewFile()
- f.XLSX["xl/worksheets/sheet1.xml"] = []byte(`<worksheet><sheetData><row r="1"><c r="A1" t="s"><v>1</v></c></row><row r="A"><c r="2" t="str"><v>B</v></c></row></sheetData></worksheet>`)
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<worksheet><sheetData><row r="1"><c r="A1" t="s"><v>1</v></c></row><row r="A"><c r="2" t="str"><v>B</v></c></row></sheetData></worksheet>`))
+ f.Sheet.Delete("xl/worksheets/sheet1.xml")
+ delete(f.checked, "xl/worksheets/sheet1.xml")
_, err = f.Rows("Sheet1")
assert.EqualError(t, err, `strconv.Atoi: parsing "A": invalid syntax`)
- f.XLSX["xl/worksheets/sheet1.xml"] = nil
+ f.Pkg.Store("xl/worksheets/sheet1.xml", nil)
_, err = f.Rows("Sheet1")
assert.NoError(t, err)
}
@@ -187,7 +189,7 @@ func TestColumns(t *testing.T) {
func TestSharedStringsReader(t *testing.T) {
f := NewFile()
- f.XLSX["xl/sharedStrings.xml"] = MacintoshCyrillicCharset
+ f.Pkg.Store("xl/sharedStrings.xml", MacintoshCyrillicCharset)
f.sharedStringsReader()
si := xlsxSI{}
assert.EqualValues(t, "", si.String())
@@ -874,12 +876,14 @@ func TestErrSheetNotExistError(t *testing.T) {
func TestCheckRow(t *testing.T) {
f := NewFile()
- f.XLSX["xl/worksheets/sheet1.xml"] = []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" ><sheetData><row r="2"><c><v>1</v></c><c r="F2"><v>2</v></c><c><v>3</v></c><c><v>4</v></c><c r="M2"><v>5</v></c></row></sheetData></worksheet>`)
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" ><sheetData><row r="2"><c><v>1</v></c><c r="F2"><v>2</v></c><c><v>3</v></c><c><v>4</v></c><c r="M2"><v>5</v></c></row></sheetData></worksheet>`))
_, err := f.GetRows("Sheet1")
assert.NoError(t, err)
assert.NoError(t, f.SetCellValue("Sheet1", "A1", false))
f = NewFile()
- f.XLSX["xl/worksheets/sheet1.xml"] = []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" ><sheetData><row r="2"><c><v>1</v></c><c r="-"><v>2</v></c><c><v>3</v></c><c><v>4</v></c><c r="M2"><v>5</v></c></row></sheetData></worksheet>`)
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" ><sheetData><row r="2"><c><v>1</v></c><c r="-"><v>2</v></c><c><v>3</v></c><c><v>4</v></c><c r="M2"><v>5</v></c></row></sheetData></worksheet>`))
+ f.Sheet.Delete("xl/worksheets/sheet1.xml")
+ delete(f.checked, "xl/worksheets/sheet1.xml")
assert.EqualError(t, f.SetCellValue("Sheet1", "A1", false), `cannot convert cell "-" to coordinates: invalid cell name "-"`)
}
diff --git a/sheet.go b/sheet.go
index 420235c..2b91495 100644
--- a/sheet.go
+++ b/sheet.go
@@ -15,7 +15,6 @@ import (
"bytes"
"encoding/json"
"encoding/xml"
- "errors"
"fmt"
"io"
"io/ioutil"
@@ -152,25 +151,27 @@ func (f *File) workSheetWriter() {
var arr []byte
buffer := bytes.NewBuffer(arr)
encoder := xml.NewEncoder(buffer)
- for p, sheet := range f.Sheet {
- if sheet != nil {
+ f.Sheet.Range(func(p, ws interface{}) bool {
+ if ws != nil {
+ sheet := ws.(*xlsxWorksheet)
for k, v := range sheet.SheetData.Row {
- f.Sheet[p].SheetData.Row[k].C = trimCell(v.C)
+ sheet.SheetData.Row[k].C = trimCell(v.C)
}
if sheet.SheetPr != nil || sheet.Drawing != nil || sheet.Hyperlinks != nil || sheet.Picture != nil || sheet.TableParts != nil {
- f.addNameSpaces(p, SourceRelationship)
+ f.addNameSpaces(p.(string), SourceRelationship)
}
// reusing buffer
_ = encoder.Encode(sheet)
- f.saveFileList(p, replaceRelationshipsBytes(f.replaceNameSpaceBytes(p, buffer.Bytes())))
- ok := f.checked[p]
+ f.saveFileList(p.(string), replaceRelationshipsBytes(f.replaceNameSpaceBytes(p.(string), buffer.Bytes())))
+ ok := f.checked[p.(string)]
if ok {
- delete(f.Sheet, p)
- f.checked[p] = false
+ f.Sheet.Delete(p.(string))
+ f.checked[p.(string)] = false
}
buffer.Reset()
}
- }
+ return true
+ })
}
// trimCell provides a function to trim blank cells which created by fillColumns.
@@ -213,7 +214,7 @@ func (f *File) setSheet(index int, name string) {
}
path := "xl/worksheets/sheet" + strconv.Itoa(index) + ".xml"
f.sheetMap[trimSheetName(name)] = path
- f.Sheet[path] = &ws
+ f.Sheet.Store(path, &ws)
f.xmlAttr[path] = []xml.Attr{NameSpaceSpreadSheet}
}
@@ -448,7 +449,7 @@ func (f *File) getSheetMap() map[string]string {
if strings.HasPrefix(rel.Target, "/") {
path = filepath.ToSlash(strings.TrimPrefix(strings.Replace(filepath.Clean(rel.Target), "\\", "/", -1), "/"))
}
- if _, ok := f.XLSX[path]; ok {
+ if _, ok := f.Pkg.Load(path); ok {
maps[v.Name] = path
}
}
@@ -524,10 +525,10 @@ func (f *File) DeleteSheet(name string) {
f.deleteSheetFromContentTypes(target)
f.deleteCalcChain(sheet.SheetID, "")
delete(f.sheetMap, sheetName)
- delete(f.XLSX, sheetXML)
- delete(f.XLSX, rels)
+ f.Pkg.Delete(sheetXML)
+ f.Pkg.Delete(rels)
f.Relationships.Delete(rels)
- delete(f.Sheet, sheetXML)
+ f.Sheet.Delete(sheetXML)
delete(f.xmlAttr, sheetXML)
f.SheetCount--
}
@@ -573,7 +574,7 @@ func (f *File) deleteSheetFromContentTypes(target string) {
//
func (f *File) CopySheet(from, to int) error {
if from < 0 || to < 0 || from == to || f.GetSheetName(from) == "" || f.GetSheetName(to) == "" {
- return errors.New("invalid worksheet index")
+ return ErrSheetIdx
}
return f.copySheet(from, to)
}
@@ -595,12 +596,11 @@ func (f *File) copySheet(from, to int) error {
worksheet.Drawing = nil
worksheet.TableParts = nil
worksheet.PageSetUp = nil
- f.Sheet[path] = worksheet
+ f.Sheet.Store(path, worksheet)
toRels := "xl/worksheets/_rels/sheet" + toSheetID + ".xml.rels"
fromRels := "xl/worksheets/_rels/sheet" + strconv.Itoa(f.getSheetID(fromSheet)) + ".xml.rels"
- _, ok := f.XLSX[fromRels]
- if ok {
- f.XLSX[toRels] = f.XLSX[fromRels]
+ if rels, ok := f.Pkg.Load(fromRels); ok && rels != nil {
+ f.Pkg.Store(toRels, rels.([]byte))
}
fromSheetXMLPath := f.sheetMap[trimSheetName(fromSheet)]
fromSheetAttr := f.xmlAttr[fromSheetXMLPath]
@@ -824,9 +824,9 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) {
if !ok {
return result, ErrSheetNotExist{sheet}
}
- if f.Sheet[name] != nil {
+ if ws, ok := f.Sheet.Load(name); ok && ws != nil {
// flush data
- output, _ := xml.Marshal(f.Sheet[name])
+ output, _ := xml.Marshal(ws.(*xlsxWorksheet))
f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
}
return f.searchSheet(name, value, regSearch)
@@ -1483,7 +1483,7 @@ func (f *File) SetDefinedName(definedName *DefinedName) error {
scope = f.GetSheetName(*dn.LocalSheetID)
}
if scope == definedName.Scope && dn.Name == definedName.Name {
- return errors.New("the same name already exists on the scope")
+ return ErrDefinedNameduplicate
}
}
wb.DefinedNames.DefinedName = append(wb.DefinedNames.DefinedName, d)
@@ -1518,7 +1518,7 @@ func (f *File) DeleteDefinedName(definedName *DefinedName) error {
}
}
}
- return errors.New("no defined name on the scope")
+ return ErrDefinedNameScope
}
// GetDefinedName provides a function to get the defined names of the workbook
@@ -1558,7 +1558,7 @@ func (f *File) GroupSheets(sheets []string) error {
}
}
if !inActiveSheet {
- return errors.New("group worksheet must contain an active worksheet")
+ return ErrGroupSheets
}
// check worksheet exists
wss := []*xlsxWorksheet{}
@@ -1714,8 +1714,7 @@ func (f *File) relsReader(path string) *xlsxRelationships {
var err error
rels, _ := f.Relationships.Load(path)
if rels == nil {
- _, ok := f.XLSX[path]
- if ok {
+ if _, ok := f.Pkg.Load(path); ok {
c := xlsxRelationships{}
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))).
Decode(&c); err != nil && err != io.EOF {
@@ -1734,6 +1733,8 @@ func (f *File) relsReader(path string) *xlsxRelationships {
// row to accept data. Missing rows are backfilled and given their row number
// Uses the last populated row as a hint for the size of the next row to add
func prepareSheetXML(ws *xlsxWorksheet, col int, row int) {
+ ws.Lock()
+ defer ws.Unlock()
rowCount := len(ws.SheetData.Row)
sizeHint := 0
var ht float64
diff --git a/sheet_test.go b/sheet_test.go
index a721472..268abdc 100644
--- a/sheet_test.go
+++ b/sheet_test.go
@@ -347,9 +347,13 @@ func TestSetActiveSheet(t *testing.T) {
f.WorkBook.BookViews = nil
f.SetActiveSheet(1)
f.WorkBook.BookViews = &xlsxBookViews{WorkBookView: []xlsxWorkBookView{}}
- f.Sheet["xl/worksheets/sheet1.xml"].SheetViews = &xlsxSheetViews{SheetView: []xlsxSheetView{}}
+ ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).SheetViews = &xlsxSheetViews{SheetView: []xlsxSheetView{}}
f.SetActiveSheet(1)
- f.Sheet["xl/worksheets/sheet1.xml"].SheetViews = nil
+ ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).SheetViews = nil
f.SetActiveSheet(1)
f = NewFile()
f.SetActiveSheet(-1)
@@ -365,14 +369,14 @@ func TestSetSheetName(t *testing.T) {
func TestGetWorkbookPath(t *testing.T) {
f := NewFile()
- delete(f.XLSX, "_rels/.rels")
+ f.Pkg.Delete("_rels/.rels")
assert.Equal(t, "", f.getWorkbookPath())
}
func TestGetWorkbookRelsPath(t *testing.T) {
f := NewFile()
- delete(f.XLSX, "xl/_rels/.rels")
- f.XLSX["_rels/.rels"] = []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument" Target="/workbook.xml"/></Relationships>`)
+ f.Pkg.Delete("xl/_rels/.rels")
+ f.Pkg.Store("_rels/.rels", []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument" Target="/workbook.xml"/></Relationships>`))
assert.Equal(t, "_rels/workbook.xml.rels", f.getWorkbookRelsPath())
}
diff --git a/sheetpr_test.go b/sheetpr_test.go
index 42e2e0d..53532e9 100644
--- a/sheetpr_test.go
+++ b/sheetpr_test.go
@@ -443,7 +443,9 @@ func TestSheetFormatPrOptions(t *testing.T) {
func TestSetSheetFormatPr(t *testing.T) {
f := NewFile()
assert.NoError(t, f.GetSheetFormatPr("Sheet1"))
- f.Sheet["xl/worksheets/sheet1.xml"].SheetFormatPr = nil
+ ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).SheetFormatPr = nil
assert.NoError(t, f.SetSheetFormatPr("Sheet1", BaseColWidth(1.0)))
// Test set formatting properties on not exists worksheet.
assert.EqualError(t, f.SetSheetFormatPr("SheetN"), "sheet SheetN is not exist")
@@ -452,7 +454,9 @@ func TestSetSheetFormatPr(t *testing.T) {
func TestGetSheetFormatPr(t *testing.T) {
f := NewFile()
assert.NoError(t, f.GetSheetFormatPr("Sheet1"))
- f.Sheet["xl/worksheets/sheet1.xml"].SheetFormatPr = nil
+ ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).SheetFormatPr = nil
var (
baseColWidth BaseColWidth
defaultColWidth DefaultColWidth
diff --git a/sparkline.go b/sparkline.go
index 150c0ea..5326c60 100644
--- a/sparkline.go
+++ b/sparkline.go
@@ -467,7 +467,7 @@ func (f *File) parseFormatAddSparklineSet(sheet string, opt *SparklineOption) (*
return ws, err
}
if opt == nil {
- return ws, errors.New("parameter is required")
+ return ws, ErrParameterRequired
}
if len(opt.Location) < 1 {
return ws, errors.New("parameter 'Location' is required")
diff --git a/sparkline_test.go b/sparkline_test.go
index eac9824..0777ee1 100644
--- a/sparkline_test.go
+++ b/sparkline_test.go
@@ -253,7 +253,9 @@ func TestAddSparkline(t *testing.T) {
Style: -1,
}), `parameter 'Style' must betweent 0-35`)
- f.Sheet["xl/worksheets/sheet1.xml"].ExtLst.Ext = `<extLst>
+ ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).ExtLst.Ext = `<extLst>
<ext x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}">
<x14:sparklineGroups
xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
diff --git a/stream.go b/stream.go
index 054dd8d..4a77b56 100644
--- a/stream.go
+++ b/stream.go
@@ -301,7 +301,7 @@ func (sw *StreamWriter) SetRow(axis string, values []interface{}) error {
}
if !sw.sheetWritten {
if len(sw.cols) > 0 {
- sw.rawData.WriteString("<cols>" + sw.cols + "</cols>")
+ _, _ = sw.rawData.WriteString("<cols>" + sw.cols + "</cols>")
}
_, _ = sw.rawData.WriteString(`<sheetData>`)
sw.sheetWritten = true
@@ -481,9 +481,9 @@ func (sw *StreamWriter) Flush() error {
}
sheetPath := sw.File.sheetMap[trimSheetName(sw.Sheet)]
- delete(sw.File.Sheet, sheetPath)
+ sw.File.Sheet.Delete(sheetPath)
delete(sw.File.checked, sheetPath)
- delete(sw.File.XLSX, sheetPath)
+ sw.File.Pkg.Delete(sheetPath)
return nil
}
diff --git a/stream_test.go b/stream_test.go
index cf133f1..f911ccc 100644
--- a/stream_test.go
+++ b/stream_test.go
@@ -99,8 +99,8 @@ func TestStreamWriter(t *testing.T) {
// Test unsupported charset
file = NewFile()
- delete(file.Sheet, "xl/worksheets/sheet1.xml")
- file.XLSX["xl/worksheets/sheet1.xml"] = MacintoshCyrillicCharset
+ file.Sheet.Delete("xl/worksheets/sheet1.xml")
+ file.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset)
_, err = file.NewStreamWriter("Sheet1")
assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
@@ -145,7 +145,9 @@ func TestStreamTable(t *testing.T) {
// Verify the table has names.
var table xlsxTable
- assert.NoError(t, xml.Unmarshal(file.XLSX["xl/tables/table1.xml"], &table))
+ val, ok := file.Pkg.Load("xl/tables/table1.xml")
+ assert.True(t, ok)
+ assert.NoError(t, xml.Unmarshal(val.([]byte), &table))
assert.Equal(t, "A", table.TableColumns.TableColumn[0].Name)
assert.Equal(t, "B", table.TableColumns.TableColumn[1].Name)
assert.Equal(t, "C", table.TableColumns.TableColumn[2].Name)
diff --git a/styles.go b/styles.go
index 1ba9f08..235746c 100644
--- a/styles.go
+++ b/styles.go
@@ -15,7 +15,6 @@ import (
"bytes"
"encoding/json"
"encoding/xml"
- "errors"
"fmt"
"io"
"log"
@@ -1104,14 +1103,14 @@ func parseFormatStyleSet(style interface{}) (*Style, error) {
case *Style:
fs = *v
default:
- err = errors.New("invalid parameter type")
+ err = ErrParameterInvalid
}
if fs.Font != nil {
if len(fs.Font.Family) > MaxFontFamilyLength {
- return &fs, errors.New("the length of the font family name must be smaller than or equal to 31")
+ return &fs, ErrFontLength
}
if fs.Font.Size > MaxFontSize {
- return &fs, errors.New("font size must be between 1 and 409 points")
+ return &fs, ErrFontSize
}
}
return &fs, err
diff --git a/styles_test.go b/styles_test.go
index e2eed1d..5d452f6 100644
--- a/styles_test.go
+++ b/styles_test.go
@@ -201,7 +201,7 @@ func TestNewStyle(t *testing.T) {
_, err = f.NewStyle(&Style{})
assert.NoError(t, err)
_, err = f.NewStyle(Style{})
- assert.EqualError(t, err, "invalid parameter type")
+ assert.EqualError(t, err, ErrParameterInvalid.Error())
_, err = f.NewStyle(&Style{Font: &Font{Family: strings.Repeat("s", MaxFontFamilyLength+1)}})
assert.EqualError(t, err, "the length of the font family name must be smaller than or equal to 31")
@@ -261,14 +261,14 @@ func TestStylesReader(t *testing.T) {
f := NewFile()
// Test read styles with unsupported charset.
f.Styles = nil
- f.XLSX["xl/styles.xml"] = MacintoshCyrillicCharset
+ f.Pkg.Store("xl/styles.xml", MacintoshCyrillicCharset)
assert.EqualValues(t, new(xlsxStyleSheet), f.stylesReader())
}
func TestThemeReader(t *testing.T) {
f := NewFile()
// Test read theme with unsupported charset.
- f.XLSX["xl/theme/theme1.xml"] = MacintoshCyrillicCharset
+ f.Pkg.Store("xl/theme/theme1.xml", MacintoshCyrillicCharset)
assert.EqualValues(t, new(xlsxTheme), f.themeReader())
}
diff --git a/table.go b/table.go
index 12ef41a..620cf20 100644
--- a/table.go
+++ b/table.go
@@ -105,11 +105,12 @@ func (f *File) AddTable(sheet, hcell, vcell, format string) error {
// folder xl/tables.
func (f *File) countTables() int {
count := 0
- for k := range f.XLSX {
- if strings.Contains(k, "xl/tables/table") {
+ f.Pkg.Range(func(k, v interface{}) bool {
+ if strings.Contains(k.(string), "xl/tables/table") {
count++
}
- }
+ return true
+ })
return count
}