From 493dfae38889697021d785c1597791f6a6d824ad Mon Sep 17 00:00:00 2001 From: Alex Geer Date: Wed, 18 Dec 2019 17:21:51 +0300 Subject: [PATCH 1/6] Fixed issue #539 Fixed error opening excel file created in encoding different from UTF-8, added logging of possible errors when decoding xml, if the function does not provide exit with error --- calcchain.go | 23 +++++++++++++--- charset_reader.go | 38 ++++++++++++++++++++++++++ chart.go | 17 +++++++++--- comment.go | 30 ++++++++++++++++----- docProps.go | 69 +++++++++++++++++++++++++++++------------------ excelize.go | 35 +++++++++++++++--------- picture.go | 18 +++++++++++-- rows.go | 13 ++++++++- sheet.go | 42 ++++++++++++++++++++++++----- sparkline.go | 12 ++++++--- styles.go | 30 +++++++++++++++++---- 11 files changed, 257 insertions(+), 70 deletions(-) create mode 100644 charset_reader.go diff --git a/calcchain.go b/calcchain.go index b4cadefe0b..07f2941404 100644 --- a/calcchain.go +++ b/calcchain.go @@ -9,16 +9,31 @@ package excelize -import "encoding/xml" +import ( + "bytes" + "encoding/xml" + "io" + "log" +) // calcChainReader provides a function to get the pointer to the structure // after deserialization of xl/calcChain.xml. func (f *File) calcChainReader() *xlsxCalcChain { + var ( + err error + decoder *xml.Decoder + ) + if f.CalcChain == nil { - var c xlsxCalcChain - _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML("xl/calcChain.xml")), &c) - f.CalcChain = &c + f.CalcChain = new(xlsxCalcChain) + + decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/calcChain.xml")))) + decoder.CharsetReader = CharsetReader + if err = decoder.Decode(f.CalcChain); err != nil && err != io.EOF { + log.Printf("xml decode error: %s", err) + } } + return f.CalcChain } diff --git a/charset_reader.go b/charset_reader.go new file mode 100644 index 0000000000..5aa914e698 --- /dev/null +++ b/charset_reader.go @@ -0,0 +1,38 @@ +package excelize + +//import "gopkg.in/webnice/debug.v1" +//import "gopkg.in/webnice/log.v2" +import ( + "fmt" + "io" + "regexp" + "strings" + + "golang.org/x/text/encoding" + "golang.org/x/text/encoding/charmap" +) + +// CharsetReader Decoder from all codepages to UTF-8 +func CharsetReader(charset string, input io.Reader) (rdr io.Reader, err error) { + var ( + sm, nm string + item, enc encoding.Encoding + i int + rexReplaceCharsetName = regexp.MustCompile(`[-_ ]`) + ) + + sm = rexReplaceCharsetName.ReplaceAllString(strings.ToLower(charset), ``) + for i = range charmap.All { + item, nm = charmap.All[i], rexReplaceCharsetName.ReplaceAllString(strings.ToLower(fmt.Sprintf("%s", item)), ``) + if strings.EqualFold(sm, nm) { + enc = item + } + } + if enc == nil { + err = fmt.Errorf("CharsetReader: unexpected charset: %q", charset) + return + } + rdr = enc.NewDecoder().Reader(input) + + return +} diff --git a/chart.go b/chart.go index 7d40405a26..ccb555348e 100644 --- a/chart.go +++ b/chart.go @@ -10,9 +10,11 @@ package excelize import ( + "bytes" "encoding/json" "encoding/xml" "errors" + "log" "strconv" "strings" ) @@ -1735,14 +1737,23 @@ func (f *File) drawPlotAreaTxPr() *cTxPr { // deserialization, two different structures: decodeWsDr and encodeWsDr are // defined. func (f *File) drawingParser(path string) (*xlsxWsDr, int) { + var ( + err error + decoder *xml.Decoder + ok bool + ) + if f.Drawings[path] == nil { content := xlsxWsDr{} content.A = NameSpaceDrawingML content.Xdr = NameSpaceDrawingMLSpreadSheet - _, ok := f.XLSX[path] - if ok { // Append Model + if _, ok = f.XLSX[path]; ok { // Append Model decodeWsDr := decodeWsDr{} - _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(path)), &decodeWsDr) + decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))) + decoder.CharsetReader = CharsetReader + if err = decoder.Decode(&decodeWsDr); err != nil { + log.Printf("xml decode error: %s", err) + } content.R = decodeWsDr.R for _, v := range decodeWsDr.OneCellAnchor { content.OneCellAnchor = append(content.OneCellAnchor, &xdrCellAnchor{ diff --git a/comment.go b/comment.go index 7f3b10dbac..16b1fe90c9 100644 --- a/comment.go +++ b/comment.go @@ -10,9 +10,11 @@ package excelize import ( + "bytes" "encoding/json" "encoding/xml" "fmt" + "log" "strconv" "strings" ) @@ -303,12 +305,20 @@ func (f *File) countComments() int { // decodeVMLDrawingReader provides a function to get the pointer to the // structure after deserialization of xl/drawings/vmlDrawing%d.xml. func (f *File) decodeVMLDrawingReader(path string) *decodeVmlDrawing { + var ( + err error + decoder *xml.Decoder + ) + if f.DecodeVMLDrawing[path] == nil { c, ok := f.XLSX[path] if ok { - d := decodeVmlDrawing{} - _ = xml.Unmarshal(namespaceStrictToTransitional(c), &d) - f.DecodeVMLDrawing[path] = &d + f.DecodeVMLDrawing[path] = new(decodeVmlDrawing) + decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(c))) + decoder.CharsetReader = CharsetReader + if err = decoder.Decode(f.DecodeVMLDrawing[path]); err != nil { + log.Printf("xml decode error: %s", err) + } } } return f.DecodeVMLDrawing[path] @@ -328,12 +338,20 @@ func (f *File) vmlDrawingWriter() { // commentsReader provides a function to get the pointer to the structure // after deserialization of xl/comments%d.xml. func (f *File) commentsReader(path string) *xlsxComments { + var ( + err error + decoder *xml.Decoder + ) + if f.Comments[path] == nil { content, ok := f.XLSX[path] if ok { - c := xlsxComments{} - _ = xml.Unmarshal(namespaceStrictToTransitional(content), &c) - f.Comments[path] = &c + f.Comments[path] = new(xlsxComments) + decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(content))) + decoder.CharsetReader = CharsetReader + if err = decoder.Decode(f.Comments[path]); err != nil { + log.Printf("xml decode error: %s", err) + } } } return f.Comments[path] diff --git a/docProps.go b/docProps.go index 166512f73c..271ab9925b 100644 --- a/docProps.go +++ b/docProps.go @@ -10,6 +10,7 @@ package excelize import ( + "bytes" "encoding/xml" "reflect" ) @@ -65,13 +66,23 @@ import ( // Version: "1.0.0", // }) // -func (f *File) SetDocProps(docProperties *DocProperties) error { - core := decodeCoreProperties{} - err := xml.Unmarshal(namespaceStrictToTransitional(f.readXML("docProps/core.xml")), &core) - if err != nil { - return err +func (f *File) SetDocProps(docProperties *DocProperties) (err error) { + var ( + decoder *xml.Decoder + core *decodeCoreProperties + newProps *xlsxCoreProperties + fields []string + output []byte + immutable, mutable reflect.Value + field, val string + ) + + decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/core.xml")))) + decoder.CharsetReader, core = CharsetReader, new(decodeCoreProperties) + if err = decoder.Decode(core); err != nil { + return } - newProps := xlsxCoreProperties{ + newProps = &xlsxCoreProperties{ Dc: NameSpaceDublinCore, Dcterms: NameSpaceDublinCoreTerms, Dcmitype: NameSpaceDublinCoreMetadataIntiative, @@ -89,17 +100,15 @@ func (f *File) SetDocProps(docProperties *DocProperties) error { Category: core.Category, Version: core.Version, } - newProps.Created.Text = core.Created.Text - newProps.Created.Type = core.Created.Type - newProps.Modified.Text = core.Modified.Text - newProps.Modified.Type = core.Modified.Type - - fields := []string{"Category", "ContentStatus", "Creator", "Description", "Identifier", "Keywords", "LastModifiedBy", "Revision", "Subject", "Title", "Language", "Version"} - immutable := reflect.ValueOf(*docProperties) - mutable := reflect.ValueOf(&newProps).Elem() - for _, field := range fields { - val := immutable.FieldByName(field).String() - if val != "" { + newProps.Created.Text, newProps.Created.Type, newProps.Modified.Text, newProps.Modified.Type = + core.Created.Text, core.Created.Type, core.Modified.Text, core.Modified.Type + fields = []string{ + "Category", "ContentStatus", "Creator", "Description", "Identifier", "Keywords", + "LastModifiedBy", "Revision", "Subject", "Title", "Language", "Version", + } + immutable, mutable = reflect.ValueOf(*docProperties), reflect.ValueOf(newProps).Elem() + for _, field = range fields { + if val = immutable.FieldByName(field).String(); val != "" { mutable.FieldByName(field).SetString(val) } } @@ -109,19 +118,25 @@ func (f *File) SetDocProps(docProperties *DocProperties) error { if docProperties.Modified != "" { newProps.Modified.Text = docProperties.Modified } - output, err := xml.Marshal(&newProps) + output, err = xml.Marshal(newProps) f.saveFileList("docProps/core.xml", output) - return err + + return } // GetDocProps provides a function to get document core properties. -func (f *File) GetDocProps() (*DocProperties, error) { - core := decodeCoreProperties{} - err := xml.Unmarshal(namespaceStrictToTransitional(f.readXML("docProps/core.xml")), &core) - if err != nil { - return nil, err +func (f *File) GetDocProps() (ret *DocProperties, err error) { + var ( + decoder *xml.Decoder + core *decodeCoreProperties + ) + + decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/core.xml")))) + decoder.CharsetReader, core = CharsetReader, new(decodeCoreProperties) + if err = decoder.Decode(core); err != nil { + return } - return &DocProperties{ + ret = &DocProperties{ Category: core.Category, ContentStatus: core.ContentStatus, Created: core.Created.Text, @@ -136,5 +151,7 @@ func (f *File) GetDocProps() (*DocProperties, error) { Title: core.Title, Language: core.Language, Version: core.Version, - }, nil + } + + return } diff --git a/excelize.go b/excelize.go index 4d46b94e9b..1451e5bc3c 100644 --- a/excelize.go +++ b/excelize.go @@ -123,26 +123,35 @@ func (f *File) setDefaultTimeStyle(sheet, axis string, format int) error { // workSheetReader provides a function to get the pointer to the structure // after deserialization by given worksheet name. -func (f *File) workSheetReader(sheet string) (*xlsxWorksheet, error) { - name, ok := f.sheetMap[trimSheetName(sheet)] - if !ok { - return nil, fmt.Errorf("sheet %s is not exist", sheet) +func (f *File) workSheetReader(sheet string) (xlsx *xlsxWorksheet, err error) { + var ( + name string + ok bool + decoder *xml.Decoder + ) + + if name, ok = f.sheetMap[trimSheetName(sheet)]; !ok { + err = fmt.Errorf("sheet %s is not exist", sheet) + return } - if f.Sheet[name] == nil { - var xlsx xlsxWorksheet - _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(name)), &xlsx) + if xlsx = f.Sheet[name]; f.Sheet[name] == nil { + xlsx = new(xlsxWorksheet) + decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(name)))) + decoder.CharsetReader = CharsetReader + if err = decoder.Decode(xlsx); err != nil { + return xlsx, err + } if f.checked == nil { f.checked = make(map[string]bool) } - ok := f.checked[name] - if !ok { - checkSheet(&xlsx) - checkRow(&xlsx) + if ok = f.checked[name]; !ok { + checkSheet(xlsx) + checkRow(xlsx) f.checked[name] = true } - f.Sheet[name] = &xlsx + f.Sheet[name] = xlsx } - return f.Sheet[name], nil + return } // checkSheet provides a function to fill each row element and make that is diff --git a/picture.go b/picture.go index ff40863369..54703e3f60 100644 --- a/picture.go +++ b/picture.go @@ -16,6 +16,7 @@ import ( "errors" "image" "io/ioutil" + "log" "os" "path" "path/filepath" @@ -472,6 +473,11 @@ func (f *File) GetPicture(sheet, cell string) (string, []byte, error) { // getPicture provides a function to get picture base name and raw content // embed in XLSX by given coordinates and drawing relationships. func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) (string, []byte, error) { + var ( + err error + decoder *xml.Decoder + ) + wsDr, _ := f.drawingParser(drawingXML) for _, anchor := range wsDr.TwoCellAnchor { if anchor.From != nil && anchor.Pic != nil { @@ -489,10 +495,18 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) } decodeWsDr := decodeWsDr{} - _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(drawingXML)), &decodeWsDr) + decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(drawingXML)))) + decoder.CharsetReader = CharsetReader + if err = decoder.Decode(&decodeWsDr); err != nil { + log.Printf("xml decode error: %s", err) + } for _, anchor := range decodeWsDr.TwoCellAnchor { decodeTwoCellAnchor := decodeTwoCellAnchor{} - _ = xml.Unmarshal([]byte(""+anchor.Content+""), &decodeTwoCellAnchor) + decoder = xml.NewDecoder(bytes.NewReader([]byte("" + anchor.Content + ""))) + decoder.CharsetReader = CharsetReader + if err = decoder.Decode(&decodeTwoCellAnchor); err != nil { + log.Printf("xml decode error: %s", err) + } if decodeTwoCellAnchor.From != nil && decodeTwoCellAnchor.Pic != nil { if decodeTwoCellAnchor.From.Col == col && decodeTwoCellAnchor.From.Row == row { xlsxRelationship := f.getDrawingRelationships(drawingRelationships, decodeTwoCellAnchor.Pic.BlipFill.Blip.Embed) diff --git a/rows.go b/rows.go index 379644110c..faf44252d7 100644 --- a/rows.go +++ b/rows.go @@ -10,9 +10,11 @@ package excelize import ( + "bytes" "encoding/xml" "errors" "fmt" + "log" "math" "strconv" ) @@ -187,13 +189,22 @@ func (f *File) GetRowHeight(sheet string, row int) (float64, error) { // sharedStringsReader provides a function to get the pointer to the structure // after deserialization of xl/sharedStrings.xml. func (f *File) sharedStringsReader() *xlsxSST { + var ( + err error + decoder *xml.Decoder + ) + if f.SharedStrings == nil { var sharedStrings xlsxSST ss := f.readXML("xl/sharedStrings.xml") if len(ss) == 0 { ss = f.readXML("xl/SharedStrings.xml") } - _ = xml.Unmarshal(namespaceStrictToTransitional(ss), &sharedStrings) + decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))) + decoder.CharsetReader = CharsetReader + if err = decoder.Decode(&sharedStrings); err != nil { + log.Printf("xml decode error: %s", err) + } f.SharedStrings = &sharedStrings } return f.SharedStrings diff --git a/sheet.go b/sheet.go index 951baf9239..a4c02de1de 100644 --- a/sheet.go +++ b/sheet.go @@ -16,6 +16,7 @@ import ( "errors" "fmt" "io/ioutil" + "log" "os" "path" "reflect" @@ -61,11 +62,20 @@ func (f *File) NewSheet(name string) int { // contentTypesReader provides a function to get the pointer to the // [Content_Types].xml structure after deserialization. func (f *File) contentTypesReader() *xlsxTypes { + var ( + err error + decoder *xml.Decoder + ) + if f.ContentTypes == nil { - var content xlsxTypes - _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML("[Content_Types].xml")), &content) - f.ContentTypes = &content + f.ContentTypes = new(xlsxTypes) + decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("[Content_Types].xml")))) + decoder.CharsetReader = CharsetReader + if err = decoder.Decode(f.ContentTypes); err != nil { + log.Printf("xml decode error: %s", err) + } } + return f.ContentTypes } @@ -81,11 +91,20 @@ func (f *File) contentTypesWriter() { // workbookReader provides a function to get the pointer to the xl/workbook.xml // structure after deserialization. func (f *File) workbookReader() *xlsxWorkbook { + var ( + err error + decoder *xml.Decoder + ) + if f.WorkBook == nil { - var content xlsxWorkbook - _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML("xl/workbook.xml")), &content) - f.WorkBook = &content + f.WorkBook = new(xlsxWorkbook) + decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/workbook.xml")))) + decoder.CharsetReader = CharsetReader + if err = decoder.Decode(f.WorkBook); err != nil { + log.Printf("xml decode error: %s", err) + } } + return f.WorkBook } @@ -1360,11 +1379,20 @@ func (f *File) UngroupSheets() error { // relsReader provides a function to get the pointer to the structure // after deserialization of xl/worksheets/_rels/sheet%d.xml.rels. func (f *File) relsReader(path string) *xlsxRelationships { + var ( + err error + decoder *xml.Decoder + ) + if f.Relationships[path] == nil { _, ok := f.XLSX[path] if ok { c := xlsxRelationships{} - _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(path)), &c) + decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))) + decoder.CharsetReader = CharsetReader + if err = decoder.Decode(&c); err != nil { + log.Printf("xml decode error: %s", err) + } f.Relationships[path] = &c } } diff --git a/sparkline.go b/sparkline.go index b09dbf4051..76a77bd610 100644 --- a/sparkline.go +++ b/sparkline.go @@ -10,6 +10,7 @@ package excelize import ( + "bytes" "encoding/xml" "errors" "strings" @@ -387,6 +388,8 @@ func (f *File) addSparklineGroupByStyle(ID int) *xlsxX14SparklineGroup { // Axis | Show sparkline axis // func (f *File) AddSparkline(sheet string, opt *SparklineOption) error { + var decoder *xml.Decoder + // parameter validation ws, err := f.parseFormatAddSparklineSet(sheet, opt) if err != nil { @@ -424,14 +427,17 @@ func (f *File) AddSparkline(sheet string, opt *SparklineOption) error { f.addSparkline(opt, group) if ws.ExtLst.Ext != "" { // append mode ext decodeExtLst := decodeWorksheetExt{} - err = xml.Unmarshal([]byte(""+ws.ExtLst.Ext+""), &decodeExtLst) - if err != nil { + decoder = xml.NewDecoder(bytes.NewReader([]byte("" + ws.ExtLst.Ext + ""))) + decoder.CharsetReader = CharsetReader + if err = decoder.Decode(&decodeExtLst); err != nil { return err } for idx, ext := range decodeExtLst.Ext { if ext.URI == ExtURISparklineGroups { decodeSparklineGroups := decodeX14SparklineGroups{} - _ = xml.Unmarshal([]byte(ext.Content), &decodeSparklineGroups) + decoder = xml.NewDecoder(bytes.NewReader([]byte(ext.Content))) + decoder.CharsetReader = CharsetReader + _ = decoder.Decode(&decodeSparklineGroups) sparklineGroupBytes, _ := xml.Marshal(group) groups := xlsxX14SparklineGroups{ XMLNSXM: NameSpaceSpreadSheetExcel2006Main, diff --git a/styles.go b/styles.go index 3244be240a..2455d42715 100644 --- a/styles.go +++ b/styles.go @@ -10,9 +10,12 @@ package excelize import ( + "bytes" "encoding/json" "encoding/xml" "fmt" + "io" + "log" "math" "strconv" "strings" @@ -997,10 +1000,18 @@ func is12HourTime(format string) bool { // stylesReader provides a function to get the pointer to the structure after // deserialization of xl/styles.xml. func (f *File) stylesReader() *xlsxStyleSheet { + var ( + err error + decoder *xml.Decoder + ) + if f.Styles == nil { - var styleSheet xlsxStyleSheet - _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML("xl/styles.xml")), &styleSheet) - f.Styles = &styleSheet + f.Styles = new(xlsxStyleSheet) + decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/styles.xml")))) + decoder.CharsetReader = CharsetReader + if err = decoder.Decode(f.Styles); err != nil { + log.Printf("xml decode error: %s", err) + } } return f.Styles } @@ -2803,8 +2814,17 @@ func getPaletteColor(color string) string { // themeReader provides a function to get the pointer to the xl/theme/theme1.xml // structure after deserialization. func (f *File) themeReader() *xlsxTheme { - var theme xlsxTheme - _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML("xl/theme/theme1.xml")), &theme) + var ( + err error + decoder *xml.Decoder + theme xlsxTheme + ) + + decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/theme/theme1.xml")))) + decoder.CharsetReader = CharsetReader + if err = decoder.Decode(&theme); err != nil && err != io.EOF { + log.Printf("xml decoder error: %s", err) + } return &theme } From 339a74e24f5c63b5eedf9b26e9685dbc20ad0816 Mon Sep 17 00:00:00 2001 From: Alex Geer Date: Wed, 18 Dec 2019 20:35:44 +0300 Subject: [PATCH 2/6] Added test for CharsetReader --- .gitignore | 3 +- charset_reader.go | 6 +-- charset_reader_test.go | 112 +++++++++++++++++++++++++++++++++++++++++ go.mod | 2 + go.sum | 3 ++ 5 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 charset_reader_test.go diff --git a/.gitignore b/.gitignore index bafda04266..a3fcff22aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ ~$*.xlsx test/Test*.xlsx *.out -*.test \ No newline at end of file +*.test +.idea diff --git a/charset_reader.go b/charset_reader.go index 5aa914e698..fdb088a186 100644 --- a/charset_reader.go +++ b/charset_reader.go @@ -1,7 +1,5 @@ package excelize -//import "gopkg.in/webnice/debug.v1" -//import "gopkg.in/webnice/log.v2" import ( "fmt" "io" @@ -23,9 +21,11 @@ func CharsetReader(charset string, input io.Reader) (rdr io.Reader, err error) { sm = rexReplaceCharsetName.ReplaceAllString(strings.ToLower(charset), ``) for i = range charmap.All { - item, nm = charmap.All[i], rexReplaceCharsetName.ReplaceAllString(strings.ToLower(fmt.Sprintf("%s", item)), ``) + item = charmap.All[i] + nm = rexReplaceCharsetName.ReplaceAllString(strings.ToLower(fmt.Sprintf("%s", item)), ``) if strings.EqualFold(sm, nm) { enc = item + break } } if enc == nil { diff --git a/charset_reader_test.go b/charset_reader_test.go new file mode 100644 index 0000000000..732ff4332a --- /dev/null +++ b/charset_reader_test.go @@ -0,0 +1,112 @@ +package excelize + +//import "gopkg.in/webnice/log.v2" +import ( + "bytes" + "io" + "io/ioutil" + "testing" + + "golang.org/x/text/encoding/charmap" +) + +func TestCharsetReaderSupported(t *testing.T) { + const unknownCodepage = `DEE26011` + var ( + err error + inp io.Reader + out io.Reader + i int + supportedCodepages = []string{ + charmap.CodePage037.String(), + charmap.CodePage437.String(), + charmap.CodePage037.String(), + charmap.CodePage850.String(), + charmap.CodePage852.String(), + charmap.CodePage855.String(), + charmap.CodePage858.String(), + charmap.CodePage860.String(), + charmap.CodePage862.String(), + charmap.CodePage863.String(), + charmap.CodePage865.String(), + charmap.CodePage866.String(), + charmap.CodePage1047.String(), + charmap.CodePage1140.String(), + charmap.ISO8859_1.String(), + charmap.ISO8859_2.String(), + charmap.ISO8859_3.String(), + charmap.ISO8859_4.String(), + charmap.ISO8859_5.String(), + charmap.ISO8859_6.String(), + charmap.ISO8859_7.String(), + charmap.ISO8859_8.String(), + charmap.ISO8859_9.String(), + charmap.ISO8859_10.String(), + charmap.ISO8859_13.String(), + charmap.ISO8859_14.String(), + charmap.ISO8859_15.String(), + charmap.ISO8859_16.String(), + charmap.KOI8R.String(), + charmap.KOI8U.String(), + charmap.Macintosh.String(), + charmap.MacintoshCyrillic.String(), + charmap.Windows874.String(), + charmap.Windows1250.String(), + charmap.Windows1251.String(), + charmap.Windows1252.String(), + charmap.Windows1253.String(), + charmap.Windows1254.String(), + charmap.Windows1255.String(), + charmap.Windows1256.String(), + charmap.Windows1257.String(), + charmap.Windows1258.String(), + } + ) + + inp = bytes.NewReader([]byte{}) + // Unknown codepage + if out, err = CharsetReader(unknownCodepage, inp); err == nil || out != nil { + t.Fatalf("incorrect CharsetReader implementation") + } + // Known codepages + for i = range supportedCodepages { + if out, err = CharsetReader(supportedCodepages[i], inp); err != nil || out == nil { + t.Fatalf("codepage %q is not supported", supportedCodepages[i]) + } + } +} + +func TestCharsetReaderTranslation(t *testing.T) { + var ( + err error + out io.Reader + buf []byte + destination = []byte{ + 0xD0, 0x9F, 0xD1, 0x80, 0xD0, 0xB8, 0xD0, 0xB2, 0xD0, 0xB5, + 0xD1, 0x82, 0x20, 0xD0, 0xBC, 0xD0, 0xB8, 0xD1, 0x80, + } + tests = []struct { + Source []byte + Destination []byte + Name string + }{ + {[]byte{0xF0, 0xD2, 0xC9, 0xD7, 0xC5, 0xD4, 0x20, 0xCD, 0xC9, 0xD2}, destination, "koi8-r"}, + {[]byte{0x8F, 0xE0, 0xA8, 0xA2, 0xA5, 0xE2, 0x20, 0xAC, 0xA8, 0xE0}, destination, "ibm code page 866"}, + {[]byte{0xCF, 0xF0, 0xE8, 0xE2, 0xE5, 0xF2, 0x20, 0xEC, 0xE8, 0xF0}, destination, "windows-1251"}, + {[]byte{0x8F, 0xF0, 0xE8, 0xE2, 0xE5, 0xF2, 0x20, 0xEC, 0xE8, 0xF0}, destination, "macintosh cyrillic"}, + } + ) + + for _, test := range tests { + if out, err = CharsetReader(test.Name, bytes.NewReader(test.Source)); err != nil { + t.Fatalf("incorrect CharsetReader implementation, error: %s", err) + } + if buf, err = ioutil.ReadAll(out); err != nil { + t.Fatalf("ioutil ReadAll error: %s", err) + } + if !bytes.EqualFold(buf, test.Destination) { + t.Fatalf("transcode error, incorrect decoding %s", test.Name) + } + buf, out = buf[:0], nil + } +} diff --git a/go.mod b/go.mod index 892f3066da..f789f6977a 100644 --- a/go.mod +++ b/go.mod @@ -7,4 +7,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/stretchr/testify v1.3.0 golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a + golang.org/x/text v0.3.0 + gopkg.in/webnice/debug.v1 v1.0.0 ) diff --git a/go.sum b/go.sum index 2d29d33a51..f7f2297d8d 100644 --- a/go.sum +++ b/go.sum @@ -11,4 +11,7 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a h1:gHevYm0pO4QUbwy8Dmdr01R5r1BuKtfYqRqF0h/Cbh0= golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/webnice/debug.v1 v1.0.0 h1:vslbCq7qIWgCwW2Ryxfefb5NuM3dwY1zuRx2a220U2s= +gopkg.in/webnice/debug.v1 v1.0.0/go.mod h1:YJnufE3IA7M5YQblj2k23QNBBYmtrw9o5XE48VS9CHU= From 1947b00158c8ae986a4612af80852bf08d3816bd Mon Sep 17 00:00:00 2001 From: Alex Geer Date: Wed, 18 Dec 2019 21:00:23 +0300 Subject: [PATCH 3/6] Fixed #discussion_r359397878 Discussion: https://github.com/360EntSecGroup-Skylar/excelize/pull/540#discussion_r359397878 --- picture.go | 61 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/picture.go b/picture.go index 54703e3f60..a0994213cf 100644 --- a/picture.go +++ b/picture.go @@ -14,9 +14,9 @@ import ( "encoding/json" "encoding/xml" "errors" + "fmt" "image" "io/ioutil" - "log" "os" "path" "path/filepath" @@ -472,52 +472,55 @@ func (f *File) GetPicture(sheet, cell string) (string, []byte, error) { // getPicture provides a function to get picture base name and raw content // embed in XLSX by given coordinates and drawing relationships. -func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) (string, []byte, error) { +func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) (ret string, buf []byte, err error) { var ( - err error - decoder *xml.Decoder + decoder *xml.Decoder + wsDr *xlsxWsDr + ok bool + anchor *xdrCellAnchor + DecodeWsDr *decodeWsDr + XlsxRelationship *xlsxRelationship + DecodeTwoCellAnchor *decodeTwoCellAnchor ) - wsDr, _ := f.drawingParser(drawingXML) - for _, anchor := range wsDr.TwoCellAnchor { + wsDr, _ = f.drawingParser(drawingXML) + for _, anchor = range wsDr.TwoCellAnchor { if anchor.From != nil && anchor.Pic != nil { if anchor.From.Col == col && anchor.From.Row == row { - xlsxRelationship := f.getDrawingRelationships(drawingRelationships, + XlsxRelationship = f.getDrawingRelationships(drawingRelationships, anchor.Pic.BlipFill.Blip.Embed) - _, ok := supportImageTypes[filepath.Ext(xlsxRelationship.Target)] - if ok { - return filepath.Base(xlsxRelationship.Target), - []byte(f.XLSX[strings.Replace(xlsxRelationship.Target, - "..", "xl", -1)]), nil + if _, ok = supportImageTypes[filepath.Ext(XlsxRelationship.Target)]; ok { + ret, buf = filepath.Base(XlsxRelationship.Target), []byte(f.XLSX[strings.Replace(XlsxRelationship.Target, "..", "xl", -1)]) + return } } } } - - decodeWsDr := decodeWsDr{} decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(drawingXML)))) - decoder.CharsetReader = CharsetReader - if err = decoder.Decode(&decodeWsDr); err != nil { - log.Printf("xml decode error: %s", err) + decoder.CharsetReader, DecodeWsDr = CharsetReader, new(decodeWsDr) + if err = decoder.Decode(DecodeWsDr); err != nil { + err = fmt.Errorf("xml decode error: %s", err) + return } - for _, anchor := range decodeWsDr.TwoCellAnchor { - decodeTwoCellAnchor := decodeTwoCellAnchor{} + for _, anchor := range DecodeWsDr.TwoCellAnchor { decoder = xml.NewDecoder(bytes.NewReader([]byte("" + anchor.Content + ""))) - decoder.CharsetReader = CharsetReader - if err = decoder.Decode(&decodeTwoCellAnchor); err != nil { - log.Printf("xml decode error: %s", err) + decoder.CharsetReader, DecodeTwoCellAnchor = CharsetReader, new(decodeTwoCellAnchor) + if err = decoder.Decode(DecodeTwoCellAnchor); err != nil { + err = fmt.Errorf("xml decode error: %s", err) + return } - if decodeTwoCellAnchor.From != nil && decodeTwoCellAnchor.Pic != nil { - if decodeTwoCellAnchor.From.Col == col && decodeTwoCellAnchor.From.Row == row { - xlsxRelationship := f.getDrawingRelationships(drawingRelationships, decodeTwoCellAnchor.Pic.BlipFill.Blip.Embed) - _, ok := supportImageTypes[filepath.Ext(xlsxRelationship.Target)] - if ok { - return filepath.Base(xlsxRelationship.Target), []byte(f.XLSX[strings.Replace(xlsxRelationship.Target, "..", "xl", -1)]), nil + if DecodeTwoCellAnchor.From != nil && DecodeTwoCellAnchor.Pic != nil { + if DecodeTwoCellAnchor.From.Col == col && DecodeTwoCellAnchor.From.Row == row { + XlsxRelationship = f.getDrawingRelationships(drawingRelationships, DecodeTwoCellAnchor.Pic.BlipFill.Blip.Embed) + if _, ok = supportImageTypes[filepath.Ext(XlsxRelationship.Target)]; ok { + ret, buf = filepath.Base(XlsxRelationship.Target), []byte(f.XLSX[strings.Replace(XlsxRelationship.Target, "..", "xl", -1)]) + return } } } } - return "", nil, nil + + return } // getDrawingRelationships provides a function to get drawing relationships From 8d6b17d00d880c53c208dbbf96b5269cd0cb573d Mon Sep 17 00:00:00 2001 From: Alex Geer Date: Wed, 18 Dec 2019 21:32:32 +0300 Subject: [PATCH 4/6] Fixed go fmt --- excelize.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/excelize.go b/excelize.go index 1451e5bc3c..631bd88962 100644 --- a/excelize.go +++ b/excelize.go @@ -125,8 +125,8 @@ func (f *File) setDefaultTimeStyle(sheet, axis string, format int) error { // after deserialization by given worksheet name. func (f *File) workSheetReader(sheet string) (xlsx *xlsxWorksheet, err error) { var ( - name string - ok bool + name string + ok bool decoder *xml.Decoder ) @@ -139,7 +139,7 @@ func (f *File) workSheetReader(sheet string) (xlsx *xlsxWorksheet, err error) { decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(name)))) decoder.CharsetReader = CharsetReader if err = decoder.Decode(xlsx); err != nil { - return xlsx, err + return } if f.checked == nil { f.checked = make(map[string]bool) From 98716ccd09711c7987cf90cd7513a43b904cc48a Mon Sep 17 00:00:00 2001 From: Alex Geer Date: Thu, 19 Dec 2019 11:39:36 +0300 Subject: [PATCH 5/6] go mod tidy and removed unused imports --- charset_reader_test.go | 1 - go.mod | 1 - go.sum | 2 -- 3 files changed, 4 deletions(-) diff --git a/charset_reader_test.go b/charset_reader_test.go index 732ff4332a..ca53722122 100644 --- a/charset_reader_test.go +++ b/charset_reader_test.go @@ -1,6 +1,5 @@ package excelize -//import "gopkg.in/webnice/log.v2" import ( "bytes" "io" diff --git a/go.mod b/go.mod index f789f6977a..dd3c2d2712 100644 --- a/go.mod +++ b/go.mod @@ -8,5 +8,4 @@ require ( github.com/stretchr/testify v1.3.0 golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a golang.org/x/text v0.3.0 - gopkg.in/webnice/debug.v1 v1.0.0 ) diff --git a/go.sum b/go.sum index f7f2297d8d..b4489c31d5 100644 --- a/go.sum +++ b/go.sum @@ -13,5 +13,3 @@ golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a h1:gHevYm0pO4QUbwy8Dmdr01R golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/webnice/debug.v1 v1.0.0 h1:vslbCq7qIWgCwW2Ryxfefb5NuM3dwY1zuRx2a220U2s= -gopkg.in/webnice/debug.v1 v1.0.0/go.mod h1:YJnufE3IA7M5YQblj2k23QNBBYmtrw9o5XE48VS9CHU= From 207c926a3acf522699c23d866ceef19909ec77b6 Mon Sep 17 00:00:00 2001 From: Alex Geer Date: Thu, 19 Dec 2019 19:04:53 +0300 Subject: [PATCH 6/6] The code has been refactored --- calcchain.go | 11 ++-- charset_reader.go | 38 -------------- charset_reader_test.go | 111 ----------------------------------------- chart.go | 11 ++-- comment.go | 21 +++----- docProps.go | 29 ++++++----- docProps_test.go | 4 +- excelize.go | 60 +++++++++++++++------- file.go | 8 +-- go.mod | 3 +- go.sum | 7 +++ picture.go | 45 +++++++++-------- rows.go | 13 ++--- sheet.go | 78 ++++++++++++++--------------- sparkline.go | 89 +++++++++++++++++++++------------ styles.go | 22 ++++---- 16 files changed, 218 insertions(+), 332 deletions(-) delete mode 100644 charset_reader.go delete mode 100644 charset_reader_test.go diff --git a/calcchain.go b/calcchain.go index 07f2941404..413f470ec1 100644 --- a/calcchain.go +++ b/calcchain.go @@ -19,17 +19,12 @@ import ( // calcChainReader provides a function to get the pointer to the structure // after deserialization of xl/calcChain.xml. func (f *File) calcChainReader() *xlsxCalcChain { - var ( - err error - decoder *xml.Decoder - ) + var err error if f.CalcChain == nil { f.CalcChain = new(xlsxCalcChain) - - decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/calcChain.xml")))) - decoder.CharsetReader = CharsetReader - if err = decoder.Decode(f.CalcChain); err != nil && err != io.EOF { + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/calcChain.xml")))). + Decode(f.CalcChain); err != nil && err != io.EOF { log.Printf("xml decode error: %s", err) } } diff --git a/charset_reader.go b/charset_reader.go deleted file mode 100644 index fdb088a186..0000000000 --- a/charset_reader.go +++ /dev/null @@ -1,38 +0,0 @@ -package excelize - -import ( - "fmt" - "io" - "regexp" - "strings" - - "golang.org/x/text/encoding" - "golang.org/x/text/encoding/charmap" -) - -// CharsetReader Decoder from all codepages to UTF-8 -func CharsetReader(charset string, input io.Reader) (rdr io.Reader, err error) { - var ( - sm, nm string - item, enc encoding.Encoding - i int - rexReplaceCharsetName = regexp.MustCompile(`[-_ ]`) - ) - - sm = rexReplaceCharsetName.ReplaceAllString(strings.ToLower(charset), ``) - for i = range charmap.All { - item = charmap.All[i] - nm = rexReplaceCharsetName.ReplaceAllString(strings.ToLower(fmt.Sprintf("%s", item)), ``) - if strings.EqualFold(sm, nm) { - enc = item - break - } - } - if enc == nil { - err = fmt.Errorf("CharsetReader: unexpected charset: %q", charset) - return - } - rdr = enc.NewDecoder().Reader(input) - - return -} diff --git a/charset_reader_test.go b/charset_reader_test.go deleted file mode 100644 index ca53722122..0000000000 --- a/charset_reader_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package excelize - -import ( - "bytes" - "io" - "io/ioutil" - "testing" - - "golang.org/x/text/encoding/charmap" -) - -func TestCharsetReaderSupported(t *testing.T) { - const unknownCodepage = `DEE26011` - var ( - err error - inp io.Reader - out io.Reader - i int - supportedCodepages = []string{ - charmap.CodePage037.String(), - charmap.CodePage437.String(), - charmap.CodePage037.String(), - charmap.CodePage850.String(), - charmap.CodePage852.String(), - charmap.CodePage855.String(), - charmap.CodePage858.String(), - charmap.CodePage860.String(), - charmap.CodePage862.String(), - charmap.CodePage863.String(), - charmap.CodePage865.String(), - charmap.CodePage866.String(), - charmap.CodePage1047.String(), - charmap.CodePage1140.String(), - charmap.ISO8859_1.String(), - charmap.ISO8859_2.String(), - charmap.ISO8859_3.String(), - charmap.ISO8859_4.String(), - charmap.ISO8859_5.String(), - charmap.ISO8859_6.String(), - charmap.ISO8859_7.String(), - charmap.ISO8859_8.String(), - charmap.ISO8859_9.String(), - charmap.ISO8859_10.String(), - charmap.ISO8859_13.String(), - charmap.ISO8859_14.String(), - charmap.ISO8859_15.String(), - charmap.ISO8859_16.String(), - charmap.KOI8R.String(), - charmap.KOI8U.String(), - charmap.Macintosh.String(), - charmap.MacintoshCyrillic.String(), - charmap.Windows874.String(), - charmap.Windows1250.String(), - charmap.Windows1251.String(), - charmap.Windows1252.String(), - charmap.Windows1253.String(), - charmap.Windows1254.String(), - charmap.Windows1255.String(), - charmap.Windows1256.String(), - charmap.Windows1257.String(), - charmap.Windows1258.String(), - } - ) - - inp = bytes.NewReader([]byte{}) - // Unknown codepage - if out, err = CharsetReader(unknownCodepage, inp); err == nil || out != nil { - t.Fatalf("incorrect CharsetReader implementation") - } - // Known codepages - for i = range supportedCodepages { - if out, err = CharsetReader(supportedCodepages[i], inp); err != nil || out == nil { - t.Fatalf("codepage %q is not supported", supportedCodepages[i]) - } - } -} - -func TestCharsetReaderTranslation(t *testing.T) { - var ( - err error - out io.Reader - buf []byte - destination = []byte{ - 0xD0, 0x9F, 0xD1, 0x80, 0xD0, 0xB8, 0xD0, 0xB2, 0xD0, 0xB5, - 0xD1, 0x82, 0x20, 0xD0, 0xBC, 0xD0, 0xB8, 0xD1, 0x80, - } - tests = []struct { - Source []byte - Destination []byte - Name string - }{ - {[]byte{0xF0, 0xD2, 0xC9, 0xD7, 0xC5, 0xD4, 0x20, 0xCD, 0xC9, 0xD2}, destination, "koi8-r"}, - {[]byte{0x8F, 0xE0, 0xA8, 0xA2, 0xA5, 0xE2, 0x20, 0xAC, 0xA8, 0xE0}, destination, "ibm code page 866"}, - {[]byte{0xCF, 0xF0, 0xE8, 0xE2, 0xE5, 0xF2, 0x20, 0xEC, 0xE8, 0xF0}, destination, "windows-1251"}, - {[]byte{0x8F, 0xF0, 0xE8, 0xE2, 0xE5, 0xF2, 0x20, 0xEC, 0xE8, 0xF0}, destination, "macintosh cyrillic"}, - } - ) - - for _, test := range tests { - if out, err = CharsetReader(test.Name, bytes.NewReader(test.Source)); err != nil { - t.Fatalf("incorrect CharsetReader implementation, error: %s", err) - } - if buf, err = ioutil.ReadAll(out); err != nil { - t.Fatalf("ioutil ReadAll error: %s", err) - } - if !bytes.EqualFold(buf, test.Destination) { - t.Fatalf("transcode error, incorrect decoding %s", test.Name) - } - buf, out = buf[:0], nil - } -} diff --git a/chart.go b/chart.go index ccb555348e..aaa7cd6844 100644 --- a/chart.go +++ b/chart.go @@ -14,6 +14,7 @@ import ( "encoding/json" "encoding/xml" "errors" + "io" "log" "strconv" "strings" @@ -1738,9 +1739,8 @@ func (f *File) drawPlotAreaTxPr() *cTxPr { // defined. func (f *File) drawingParser(path string) (*xlsxWsDr, int) { var ( - err error - decoder *xml.Decoder - ok bool + err error + ok bool ) if f.Drawings[path] == nil { @@ -1749,9 +1749,8 @@ func (f *File) drawingParser(path string) (*xlsxWsDr, int) { content.Xdr = NameSpaceDrawingMLSpreadSheet if _, ok = f.XLSX[path]; ok { // Append Model decodeWsDr := decodeWsDr{} - decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))) - decoder.CharsetReader = CharsetReader - if err = decoder.Decode(&decodeWsDr); err != nil { + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))). + Decode(&decodeWsDr); err != nil && err != io.EOF { log.Printf("xml decode error: %s", err) } content.R = decodeWsDr.R diff --git a/comment.go b/comment.go index 16b1fe90c9..99630c93f7 100644 --- a/comment.go +++ b/comment.go @@ -14,6 +14,7 @@ import ( "encoding/json" "encoding/xml" "fmt" + "io" "log" "strconv" "strings" @@ -305,18 +306,14 @@ func (f *File) countComments() int { // decodeVMLDrawingReader provides a function to get the pointer to the // structure after deserialization of xl/drawings/vmlDrawing%d.xml. func (f *File) decodeVMLDrawingReader(path string) *decodeVmlDrawing { - var ( - err error - decoder *xml.Decoder - ) + var err error if f.DecodeVMLDrawing[path] == nil { c, ok := f.XLSX[path] if ok { f.DecodeVMLDrawing[path] = new(decodeVmlDrawing) - decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(c))) - decoder.CharsetReader = CharsetReader - if err = decoder.Decode(f.DecodeVMLDrawing[path]); err != nil { + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(c))). + Decode(f.DecodeVMLDrawing[path]); err != nil && err != io.EOF { log.Printf("xml decode error: %s", err) } } @@ -338,18 +335,14 @@ func (f *File) vmlDrawingWriter() { // commentsReader provides a function to get the pointer to the structure // after deserialization of xl/comments%d.xml. func (f *File) commentsReader(path string) *xlsxComments { - var ( - err error - decoder *xml.Decoder - ) + var err error if f.Comments[path] == nil { content, ok := f.XLSX[path] if ok { f.Comments[path] = new(xlsxComments) - decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(content))) - decoder.CharsetReader = CharsetReader - if err = decoder.Decode(f.Comments[path]); err != nil { + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content))). + Decode(f.Comments[path]); err != nil && err != io.EOF { log.Printf("xml decode error: %s", err) } } diff --git a/docProps.go b/docProps.go index 271ab9925b..884eb6317f 100644 --- a/docProps.go +++ b/docProps.go @@ -12,6 +12,8 @@ package excelize import ( "bytes" "encoding/xml" + "fmt" + "io" "reflect" ) @@ -68,7 +70,6 @@ import ( // func (f *File) SetDocProps(docProperties *DocProperties) (err error) { var ( - decoder *xml.Decoder core *decodeCoreProperties newProps *xlsxCoreProperties fields []string @@ -77,12 +78,13 @@ func (f *File) SetDocProps(docProperties *DocProperties) (err error) { field, val string ) - decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/core.xml")))) - decoder.CharsetReader, core = CharsetReader, new(decodeCoreProperties) - if err = decoder.Decode(core); err != nil { + core = new(decodeCoreProperties) + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/core.xml")))). + Decode(core); err != nil && err != io.EOF { + err = fmt.Errorf("xml decode error: %s", err) return } - newProps = &xlsxCoreProperties{ + newProps, err = &xlsxCoreProperties{ Dc: NameSpaceDublinCore, Dcterms: NameSpaceDublinCoreTerms, Dcmitype: NameSpaceDublinCoreMetadataIntiative, @@ -99,7 +101,7 @@ func (f *File) SetDocProps(docProperties *DocProperties) (err error) { ContentStatus: core.ContentStatus, Category: core.Category, Version: core.Version, - } + }, nil newProps.Created.Text, newProps.Created.Type, newProps.Modified.Text, newProps.Modified.Type = core.Created.Text, core.Created.Type, core.Modified.Text, core.Modified.Type fields = []string{ @@ -126,17 +128,14 @@ func (f *File) SetDocProps(docProperties *DocProperties) (err error) { // GetDocProps provides a function to get document core properties. func (f *File) GetDocProps() (ret *DocProperties, err error) { - var ( - decoder *xml.Decoder - core *decodeCoreProperties - ) + var core = new(decodeCoreProperties) - decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/core.xml")))) - decoder.CharsetReader, core = CharsetReader, new(decodeCoreProperties) - if err = decoder.Decode(core); err != nil { + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/core.xml")))). + Decode(core); err != nil && err != io.EOF { + err = fmt.Errorf("xml decode error: %s", err) return } - ret = &DocProperties{ + ret, err = &DocProperties{ Category: core.Category, ContentStatus: core.ContentStatus, Created: core.Created.Text, @@ -151,7 +150,7 @@ func (f *File) GetDocProps() (ret *DocProperties, err error) { Title: core.Title, Language: core.Language, Version: core.Version, - } + }, nil return } diff --git a/docProps_test.go b/docProps_test.go index 671d998cc9..df3122b19a 100644 --- a/docProps_test.go +++ b/docProps_test.go @@ -39,7 +39,7 @@ func TestSetDocProps(t *testing.T) { })) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetDocProps.xlsx"))) f.XLSX["docProps/core.xml"] = nil - assert.EqualError(t, f.SetDocProps(&DocProperties{}), "EOF") + assert.NoError(t, f.SetDocProps(&DocProperties{})) } func TestGetDocProps(t *testing.T) { @@ -52,5 +52,5 @@ func TestGetDocProps(t *testing.T) { assert.Equal(t, props.Creator, "Microsoft Office User") f.XLSX["docProps/core.xml"] = nil _, err = f.GetDocProps() - assert.EqualError(t, err, "EOF") + assert.NoError(t, err) } diff --git a/excelize.go b/excelize.go index 631bd88962..fe227b9524 100644 --- a/excelize.go +++ b/excelize.go @@ -22,6 +22,8 @@ import ( "path" "strconv" "strings" + + "golang.org/x/net/html/charset" ) // File define a populated XLSX file struct. @@ -43,8 +45,11 @@ type File struct { WorkBook *xlsxWorkbook Relationships map[string]*xlsxRelationships XLSX map[string][]byte + CharsetReader charsetTranscoderFn } +type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, err error) + // OpenFile take the name of an XLSX file and returns a populated XLSX file // struct for it. func OpenFile(filename string) (*File, error) { @@ -61,6 +66,21 @@ func OpenFile(filename string) (*File, error) { return f, nil } +// object builder +func newFile() *File { + return &File{ + checked: make(map[string]bool), + sheetMap: make(map[string]string), + Comments: make(map[string]*xlsxComments), + Drawings: make(map[string]*xlsxWsDr), + Sheet: make(map[string]*xlsxWorksheet), + DecodeVMLDrawing: make(map[string]*decodeVmlDrawing), + VMLDrawing: make(map[string]*vmlDrawing), + Relationships: make(map[string]*xlsxRelationships), + CharsetReader: charset.NewReaderLabel, + } +} + // OpenReader take an io.Reader and return a populated XLSX file. func OpenReader(r io.Reader) (*File, error) { b, err := ioutil.ReadAll(r) @@ -88,17 +108,8 @@ func OpenReader(r io.Reader) (*File, error) { if err != nil { return nil, err } - f := &File{ - checked: make(map[string]bool), - Comments: make(map[string]*xlsxComments), - Drawings: make(map[string]*xlsxWsDr), - Sheet: make(map[string]*xlsxWorksheet), - SheetCount: sheetCount, - DecodeVMLDrawing: make(map[string]*decodeVmlDrawing), - VMLDrawing: make(map[string]*vmlDrawing), - Relationships: make(map[string]*xlsxRelationships), - XLSX: file, - } + f := newFile() + f.SheetCount, f.XLSX = sheetCount, file f.CalcChain = f.calcChainReader() f.sheetMap = f.getSheetMap() f.Styles = f.stylesReader() @@ -106,6 +117,16 @@ func OpenReader(r io.Reader) (*File, error) { return f, nil } +// CharsetTranscoder Set user defined codepage transcoder function for open XLSX from non UTF-8 encoding +func (f *File) CharsetTranscoder(fn charsetTranscoderFn) *File { f.CharsetReader = fn; return f } + +// Creates new XML decoder with charset reader +func (f *File) xmlNewDecoder(rdr io.Reader) (ret *xml.Decoder) { + ret = xml.NewDecoder(rdr) + ret.CharsetReader = f.CharsetReader + return +} + // setDefaultTimeStyle provides a function to set default numbers format for // time.Time type cell value by given worksheet name, cell coordinates and // number format code. @@ -125,9 +146,8 @@ func (f *File) setDefaultTimeStyle(sheet, axis string, format int) error { // after deserialization by given worksheet name. func (f *File) workSheetReader(sheet string) (xlsx *xlsxWorksheet, err error) { var ( - name string - ok bool - decoder *xml.Decoder + name string + ok bool ) if name, ok = f.sheetMap[trimSheetName(sheet)]; !ok { @@ -136,21 +156,25 @@ func (f *File) workSheetReader(sheet string) (xlsx *xlsxWorksheet, err error) { } if xlsx = f.Sheet[name]; f.Sheet[name] == nil { xlsx = new(xlsxWorksheet) - decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(name)))) - decoder.CharsetReader = CharsetReader - if err = decoder.Decode(xlsx); err != nil { + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(name)))). + Decode(xlsx); 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(xlsx) - checkRow(xlsx) + if err = checkRow(xlsx); err != nil { + return + } f.checked[name] = true } f.Sheet[name] = xlsx } + return } diff --git a/file.go b/file.go index 2e0d27bef4..d8f10facfa 100644 --- a/file.go +++ b/file.go @@ -33,12 +33,8 @@ func NewFile() *File { file["xl/styles.xml"] = []byte(XMLHeader + templateStyles) file["xl/workbook.xml"] = []byte(XMLHeader + templateWorkbook) file["[Content_Types].xml"] = []byte(XMLHeader + templateContentTypes) - f := &File{ - sheetMap: make(map[string]string), - Sheet: make(map[string]*xlsxWorksheet), - SheetCount: 1, - XLSX: file, - } + f := newFile() + f.SheetCount, f.XLSX = 1, file f.CalcChain = f.calcChainReader() f.Comments = make(map[string]*xlsxComments) f.ContentTypes = f.contentTypesReader() diff --git a/go.mod b/go.mod index dd3c2d2712..420c64eb64 100644 --- a/go.mod +++ b/go.mod @@ -7,5 +7,6 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/stretchr/testify v1.3.0 golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a - golang.org/x/text v0.3.0 + golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 + golang.org/x/text v0.3.2 // indirect ) diff --git a/go.sum b/go.sum index b4489c31d5..54492ac2ed 100644 --- a/go.sum +++ b/go.sum @@ -9,7 +9,14 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a h1:gHevYm0pO4QUbwy8Dmdr01R5r1BuKtfYqRqF0h/Cbh0= golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/picture.go b/picture.go index a0994213cf..09c19559de 100644 --- a/picture.go +++ b/picture.go @@ -16,6 +16,7 @@ import ( "errors" "fmt" "image" + "io" "io/ioutil" "os" "path" @@ -474,46 +475,46 @@ func (f *File) GetPicture(sheet, cell string) (string, []byte, error) { // embed in XLSX by given coordinates and drawing relationships. func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) (ret string, buf []byte, err error) { var ( - decoder *xml.Decoder - wsDr *xlsxWsDr - ok bool - anchor *xdrCellAnchor - DecodeWsDr *decodeWsDr - XlsxRelationship *xlsxRelationship - DecodeTwoCellAnchor *decodeTwoCellAnchor + wsDr *xlsxWsDr + ok bool + anchor *xdrCellAnchor + deWsDr *decodeWsDr + xxRelationship *xlsxRelationship + deTwoCellAnchor *decodeTwoCellAnchor ) wsDr, _ = f.drawingParser(drawingXML) for _, anchor = range wsDr.TwoCellAnchor { if anchor.From != nil && anchor.Pic != nil { if anchor.From.Col == col && anchor.From.Row == row { - XlsxRelationship = f.getDrawingRelationships(drawingRelationships, + xxRelationship = f.getDrawingRelationships(drawingRelationships, anchor.Pic.BlipFill.Blip.Embed) - if _, ok = supportImageTypes[filepath.Ext(XlsxRelationship.Target)]; ok { - ret, buf = filepath.Base(XlsxRelationship.Target), []byte(f.XLSX[strings.Replace(XlsxRelationship.Target, "..", "xl", -1)]) + if _, ok = supportImageTypes[filepath.Ext(xxRelationship.Target)]; ok { + ret, buf = filepath.Base(xxRelationship.Target), []byte(f.XLSX[strings.Replace(xxRelationship.Target, "..", "xl", -1)]) return } } } } - decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(drawingXML)))) - decoder.CharsetReader, DecodeWsDr = CharsetReader, new(decodeWsDr) - if err = decoder.Decode(DecodeWsDr); err != nil { + deWsDr = new(decodeWsDr) + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(drawingXML)))). + Decode(deWsDr); err != nil && err != io.EOF { err = fmt.Errorf("xml decode error: %s", err) return } - for _, anchor := range DecodeWsDr.TwoCellAnchor { - decoder = xml.NewDecoder(bytes.NewReader([]byte("" + anchor.Content + ""))) - decoder.CharsetReader, DecodeTwoCellAnchor = CharsetReader, new(decodeTwoCellAnchor) - if err = decoder.Decode(DecodeTwoCellAnchor); err != nil { + err = nil + for _, anchor := range deWsDr.TwoCellAnchor { + deTwoCellAnchor = new(decodeTwoCellAnchor) + if err = f.xmlNewDecoder(bytes.NewReader([]byte("" + anchor.Content + ""))). + Decode(deTwoCellAnchor); err != nil && err != io.EOF { err = fmt.Errorf("xml decode error: %s", err) return } - if DecodeTwoCellAnchor.From != nil && DecodeTwoCellAnchor.Pic != nil { - if DecodeTwoCellAnchor.From.Col == col && DecodeTwoCellAnchor.From.Row == row { - XlsxRelationship = f.getDrawingRelationships(drawingRelationships, DecodeTwoCellAnchor.Pic.BlipFill.Blip.Embed) - if _, ok = supportImageTypes[filepath.Ext(XlsxRelationship.Target)]; ok { - ret, buf = filepath.Base(XlsxRelationship.Target), []byte(f.XLSX[strings.Replace(XlsxRelationship.Target, "..", "xl", -1)]) + if err = nil; deTwoCellAnchor.From != nil && deTwoCellAnchor.Pic != nil { + if deTwoCellAnchor.From.Col == col && deTwoCellAnchor.From.Row == row { + xxRelationship = f.getDrawingRelationships(drawingRelationships, deTwoCellAnchor.Pic.BlipFill.Blip.Embed) + if _, ok = supportImageTypes[filepath.Ext(xxRelationship.Target)]; ok { + ret, buf = filepath.Base(xxRelationship.Target), []byte(f.XLSX[strings.Replace(xxRelationship.Target, "..", "xl", -1)]) return } } diff --git a/rows.go b/rows.go index faf44252d7..e12e349a02 100644 --- a/rows.go +++ b/rows.go @@ -11,9 +11,9 @@ package excelize import ( "bytes" - "encoding/xml" "errors" "fmt" + "io" "log" "math" "strconv" @@ -189,10 +189,7 @@ func (f *File) GetRowHeight(sheet string, row int) (float64, error) { // sharedStringsReader provides a function to get the pointer to the structure // after deserialization of xl/sharedStrings.xml. func (f *File) sharedStringsReader() *xlsxSST { - var ( - err error - decoder *xml.Decoder - ) + var err error if f.SharedStrings == nil { var sharedStrings xlsxSST @@ -200,13 +197,13 @@ func (f *File) sharedStringsReader() *xlsxSST { if len(ss) == 0 { ss = f.readXML("xl/SharedStrings.xml") } - decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))) - decoder.CharsetReader = CharsetReader - if err = decoder.Decode(&sharedStrings); err != nil { + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))). + Decode(&sharedStrings); err != nil && err != io.EOF { log.Printf("xml decode error: %s", err) } f.SharedStrings = &sharedStrings } + return f.SharedStrings } diff --git a/sheet.go b/sheet.go index a4c02de1de..42fd6b3394 100644 --- a/sheet.go +++ b/sheet.go @@ -15,6 +15,7 @@ import ( "encoding/xml" "errors" "fmt" + "io" "io/ioutil" "log" "os" @@ -62,16 +63,12 @@ func (f *File) NewSheet(name string) int { // contentTypesReader provides a function to get the pointer to the // [Content_Types].xml structure after deserialization. func (f *File) contentTypesReader() *xlsxTypes { - var ( - err error - decoder *xml.Decoder - ) + var err error if f.ContentTypes == nil { f.ContentTypes = new(xlsxTypes) - decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("[Content_Types].xml")))) - decoder.CharsetReader = CharsetReader - if err = decoder.Decode(f.ContentTypes); err != nil { + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("[Content_Types].xml")))). + Decode(f.ContentTypes); err != nil && err != io.EOF { log.Printf("xml decode error: %s", err) } } @@ -91,16 +88,12 @@ func (f *File) contentTypesWriter() { // workbookReader provides a function to get the pointer to the xl/workbook.xml // structure after deserialization. func (f *File) workbookReader() *xlsxWorkbook { - var ( - err error - decoder *xml.Decoder - ) + var err error if f.WorkBook == nil { f.WorkBook = new(xlsxWorkbook) - decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/workbook.xml")))) - decoder.CharsetReader = CharsetReader - if err = decoder.Decode(f.WorkBook); err != nil { + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/workbook.xml")))). + Decode(f.WorkBook); err != nil && err != io.EOF { log.Printf("xml decode error: %s", err) } } @@ -698,42 +691,51 @@ func (f *File) GetSheetVisible(name string) bool { // // result, err := f.SearchSheet("Sheet1", "[0-9]", true) // -func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) { +func (f *File) SearchSheet(sheet, value string, reg ...bool) (result []string, err error) { var ( - regSearch bool - result []string + xlsx *xlsxWorksheet + regSearch, r, ok bool + name string + output []byte ) - for _, r := range reg { + + for _, r = range reg { regSearch = r } - xlsx, err := f.workSheetReader(sheet) - if err != nil { - return result, err + if xlsx, err = f.workSheetReader(sheet); err != nil { + return } - name, ok := f.sheetMap[trimSheetName(sheet)] - if !ok { - return result, nil + if name, ok = f.sheetMap[trimSheetName(sheet)]; !ok { + return } if xlsx != nil { - output, _ := xml.Marshal(f.Sheet[name]) + if output, err = xml.Marshal(f.Sheet[name]); err != nil { + return + } f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output)) } + return f.searchSheet(name, value, regSearch) } // searchSheet provides a function to get coordinates by given worksheet name, // cell value, and regular expression. -func (f *File) searchSheet(name, value string, regSearch bool) ([]string, error) { +func (f *File) searchSheet(name, value string, regSearch bool) (result []string, err error) { var ( + d *xlsxSST + decoder *xml.Decoder inElement string - result []string r xlsxRow + token xml.Token ) - d := f.sharedStringsReader() - decoder := xml.NewDecoder(bytes.NewReader(f.readXML(name))) + + d = f.sharedStringsReader() + decoder = f.xmlNewDecoder(bytes.NewReader(f.readXML(name))) for { - token, _ := decoder.Token() - if token == nil { + if token, err = decoder.Token(); err != nil || token == nil { + if err == io.EOF { + err = nil + } break } switch startElement := token.(type) { @@ -769,7 +771,8 @@ func (f *File) searchSheet(name, value string, regSearch bool) ([]string, error) default: } } - return result, nil + + return } // SetHeaderFooter provides a function to set headers and footers by given @@ -1379,23 +1382,20 @@ func (f *File) UngroupSheets() error { // relsReader provides a function to get the pointer to the structure // after deserialization of xl/worksheets/_rels/sheet%d.xml.rels. func (f *File) relsReader(path string) *xlsxRelationships { - var ( - err error - decoder *xml.Decoder - ) + var err error if f.Relationships[path] == nil { _, ok := f.XLSX[path] if ok { c := xlsxRelationships{} - decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))) - decoder.CharsetReader = CharsetReader - if err = decoder.Decode(&c); err != nil { + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))). + Decode(&c); err != nil && err != io.EOF { log.Printf("xml decode error: %s", err) } f.Relationships[path] = &c } } + return f.Relationships[path] } diff --git a/sparkline.go b/sparkline.go index 76a77bd610..9ad5087775 100644 --- a/sparkline.go +++ b/sparkline.go @@ -13,6 +13,7 @@ import ( "bytes" "encoding/xml" "errors" + "io" "strings" ) @@ -387,25 +388,40 @@ func (f *File) addSparklineGroupByStyle(ID int) *xlsxX14SparklineGroup { // ColorAxis | An RGB Color is specified as RRGGBB // Axis | Show sparkline axis // -func (f *File) AddSparkline(sheet string, opt *SparklineOption) error { - var decoder *xml.Decoder +func (f *File) AddSparkline(sheet string, opt *SparklineOption) (err error) { + var ( + ws *xlsxWorksheet + sparkType string + sparkTypes map[string]string + specifiedSparkTypes string + ok bool + group *xlsxX14SparklineGroup + groups *xlsxX14SparklineGroups + decodeExtLst *decodeWorksheetExt + idx int + ext *xlsxWorksheetExt + decodeSparklineGroups *decodeX14SparklineGroups + sparklineGroupBytes []byte + sparklineGroupsBytes []byte + extLst string + extLstBytes, extBytes []byte + ) // parameter validation - ws, err := f.parseFormatAddSparklineSet(sheet, opt) - if err != nil { - return err + if ws, err = f.parseFormatAddSparklineSet(sheet, opt); err != nil { + return } // Handle the sparkline type - sparkType := "line" - sparkTypes := map[string]string{"line": "line", "column": "column", "win_loss": "stacked"} + sparkType = "line" + sparkTypes = map[string]string{"line": "line", "column": "column", "win_loss": "stacked"} if opt.Type != "" { - specifiedSparkTypes, ok := sparkTypes[opt.Type] - if !ok { - return errors.New("parameter 'Type' must be 'line', 'column' or 'win_loss'") + if specifiedSparkTypes, ok = sparkTypes[opt.Type]; !ok { + err = errors.New("parameter 'Type' must be 'line', 'column' or 'win_loss'") + return } sparkType = specifiedSparkTypes } - group := f.addSparklineGroupByStyle(opt.Style) + group = f.addSparklineGroupByStyle(opt.Style) group.Type = sparkType group.ColorAxis = &xlsxColor{RGB: "FF000000"} group.DisplayEmptyCellsAs = "gap" @@ -426,46 +442,57 @@ func (f *File) AddSparkline(sheet string, opt *SparklineOption) error { } f.addSparkline(opt, group) if ws.ExtLst.Ext != "" { // append mode ext - decodeExtLst := decodeWorksheetExt{} - decoder = xml.NewDecoder(bytes.NewReader([]byte("" + ws.ExtLst.Ext + ""))) - decoder.CharsetReader = CharsetReader - if err = decoder.Decode(&decodeExtLst); err != nil { - return err + decodeExtLst = new(decodeWorksheetExt) + if err = f.xmlNewDecoder(bytes.NewReader([]byte("" + ws.ExtLst.Ext + ""))). + Decode(decodeExtLst); err != nil && err != io.EOF { + return } - for idx, ext := range decodeExtLst.Ext { + for idx, ext = range decodeExtLst.Ext { if ext.URI == ExtURISparklineGroups { - decodeSparklineGroups := decodeX14SparklineGroups{} - decoder = xml.NewDecoder(bytes.NewReader([]byte(ext.Content))) - decoder.CharsetReader = CharsetReader - _ = decoder.Decode(&decodeSparklineGroups) - sparklineGroupBytes, _ := xml.Marshal(group) - groups := xlsxX14SparklineGroups{ + decodeSparklineGroups = new(decodeX14SparklineGroups) + if err = f.xmlNewDecoder(bytes.NewReader([]byte(ext.Content))). + Decode(decodeSparklineGroups); err != nil && err != io.EOF { + return + } + if sparklineGroupBytes, err = xml.Marshal(group); err != nil { + return + } + groups = &xlsxX14SparklineGroups{ XMLNSXM: NameSpaceSpreadSheetExcel2006Main, Content: decodeSparklineGroups.Content + string(sparklineGroupBytes), } - sparklineGroupsBytes, _ := xml.Marshal(groups) + if sparklineGroupsBytes, err = xml.Marshal(groups); err != nil { + return + } decodeExtLst.Ext[idx].Content = string(sparklineGroupsBytes) } } - extLstBytes, _ := xml.Marshal(decodeExtLst) - extLst := string(extLstBytes) + if extLstBytes, err = xml.Marshal(decodeExtLst); err != nil { + return + } + extLst = string(extLstBytes) ws.ExtLst = &xlsxExtLst{ Ext: strings.TrimSuffix(strings.TrimPrefix(extLst, ""), ""), } } else { - groups := xlsxX14SparklineGroups{ + groups = &xlsxX14SparklineGroups{ XMLNSXM: NameSpaceSpreadSheetExcel2006Main, SparklineGroups: []*xlsxX14SparklineGroup{group}, } - sparklineGroupsBytes, _ := xml.Marshal(groups) - extLst := xlsxWorksheetExt{ + if sparklineGroupsBytes, err = xml.Marshal(groups); err != nil { + return + } + ext = &xlsxWorksheetExt{ URI: ExtURISparklineGroups, Content: string(sparklineGroupsBytes), } - extBytes, _ := xml.Marshal(extLst) + if extBytes, err = xml.Marshal(ext); err != nil { + return + } ws.ExtLst.Ext = string(extBytes) } - return nil + + return } // parseFormatAddSparklineSet provides a function to validate sparkline diff --git a/styles.go b/styles.go index 2455d42715..fa0507ebc9 100644 --- a/styles.go +++ b/styles.go @@ -1000,19 +1000,16 @@ func is12HourTime(format string) bool { // stylesReader provides a function to get the pointer to the structure after // deserialization of xl/styles.xml. func (f *File) stylesReader() *xlsxStyleSheet { - var ( - err error - decoder *xml.Decoder - ) + var err error if f.Styles == nil { f.Styles = new(xlsxStyleSheet) - decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/styles.xml")))) - decoder.CharsetReader = CharsetReader - if err = decoder.Decode(f.Styles); err != nil { + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/styles.xml")))). + Decode(f.Styles); err != nil && err != io.EOF { log.Printf("xml decode error: %s", err) } } + return f.Styles } @@ -2815,16 +2812,15 @@ func getPaletteColor(color string) string { // structure after deserialization. func (f *File) themeReader() *xlsxTheme { var ( - err error - decoder *xml.Decoder - theme xlsxTheme + err error + theme xlsxTheme ) - decoder = xml.NewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/theme/theme1.xml")))) - decoder.CharsetReader = CharsetReader - if err = decoder.Decode(&theme); err != nil && err != io.EOF { + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/theme/theme1.xml")))). + Decode(&theme); err != nil && err != io.EOF { log.Printf("xml decoder error: %s", err) } + return &theme }