- Support concurrency iterate rows and columns

- Rename exported field `File.XLSX` to `File.Pkg`
- Exported error message
This commit is contained in:
xuri 2021-07-05 00:03:56 +08:00
parent 0e02329bed
commit 544ef18a8c
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
38 changed files with 379 additions and 264 deletions

View File

@ -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"`)

View File

@ -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" {

View File

@ -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()
}

12
cell.go
View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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])

7
col.go
View File

@ -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

View File

@ -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()

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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]
}

View File

@ -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")
}

View File

@ -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 {

View File

@ -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

View File

@ -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")
)

View File

@ -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,12 +175,14 @@ 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 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
@ -203,9 +208,7 @@ func (f *File) workSheetReader(sheet string) (ws *xlsxWorksheet, err error) {
}
f.checked[name] = true
}
f.Sheet[name] = ws
}
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
}

View File

@ -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)
}

54
file.go
View File

@ -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 false
}
if _, ok := f.streams[path.(string)]; ok {
return true
}
var fi io.Writer
fi, err = zw.Create(path.(string))
if err != nil {
return false
}
_, err = fi.Write(content.([]byte))
return true
})
return err
}
_, err = fi.Write(content)
if err != nil {
return err
}
}
return nil
}

View File

@ -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}}

6
lib.go
View File

@ -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.

View File

@ -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"`)
}

View File

@ -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) {
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
}
}

View File

@ -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"`)
}

View File

@ -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
}

View File

@ -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",

View File

@ -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 (

View File

@ -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 "-"`)
}

View File

@ -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

View File

@ -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())
}

View File

@ -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

View File

@ -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")

View File

@ -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">

View File

@ -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
}

View File

@ -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)

View File

@ -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

View File

@ -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())
}

View File

@ -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
}