summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--calcchain.go6
-rw-r--r--calcchain_test.go2
-rw-r--r--cell.go90
-rw-r--r--cell_test.go17
-rw-r--r--docProps.go20
-rw-r--r--docProps_test.go16
-rw-r--r--errors.go4
-rw-r--r--excelize.go82
-rw-r--r--excelize_test.go8
-rw-r--r--file.go23
-rw-r--r--lib.go14
-rw-r--r--rows.go51
-rw-r--r--rows_test.go11
-rw-r--r--sheet.go6
-rw-r--r--stream.go2
-rw-r--r--styles.go6
-rw-r--r--styles_test.go2
-rw-r--r--templates.go15
-rw-r--r--xmlDrawing.go29
19 files changed, 260 insertions, 144 deletions
diff --git a/calcchain.go b/calcchain.go
index 671d144..8f5e277 100644
--- a/calcchain.go
+++ b/calcchain.go
@@ -25,7 +25,7 @@ func (f *File) calcChainReader() *xlsxCalcChain {
if f.CalcChain == nil {
f.CalcChain = new(xlsxCalcChain)
- if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/calcChain.xml")))).
+ if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(dafaultXMLPathCalcChain)))).
Decode(f.CalcChain); err != nil && err != io.EOF {
log.Printf("xml decode error: %s", err)
}
@@ -39,7 +39,7 @@ func (f *File) calcChainReader() *xlsxCalcChain {
func (f *File) calcChainWriter() {
if f.CalcChain != nil && f.CalcChain.C != nil {
output, _ := xml.Marshal(f.CalcChain)
- f.saveFileList("xl/calcChain.xml", output)
+ f.saveFileList(dafaultXMLPathCalcChain, output)
}
}
@@ -54,7 +54,7 @@ func (f *File) deleteCalcChain(index int, axis string) {
}
if len(calc.C) == 0 {
f.CalcChain = nil
- f.Pkg.Delete("xl/calcChain.xml")
+ f.Pkg.Delete(dafaultXMLPathCalcChain)
content := f.contentTypesReader()
content.Lock()
defer content.Unlock()
diff --git a/calcchain_test.go b/calcchain_test.go
index 4956f60..6144ed5 100644
--- a/calcchain_test.go
+++ b/calcchain_test.go
@@ -5,7 +5,7 @@ import "testing"
func TestCalcChainReader(t *testing.T) {
f := NewFile()
f.CalcChain = nil
- f.Pkg.Store("xl/calcChain.xml", MacintoshCyrillicCharset)
+ f.Pkg.Store(dafaultXMLPathCalcChain, MacintoshCyrillicCharset)
f.calcChainReader()
}
diff --git a/cell.go b/cell.go
index 5c34bb9..daff3d9 100644
--- a/cell.go
+++ b/cell.go
@@ -14,6 +14,7 @@ package excelize
import (
"encoding/xml"
"fmt"
+ "os"
"reflect"
"strconv"
"strings"
@@ -348,28 +349,49 @@ func (f *File) SetCellStr(sheet, axis, value string) error {
ws.Lock()
defer ws.Unlock()
cellData.S = f.prepareCellStyle(ws, col, cellData.S)
- cellData.T, cellData.V = f.setCellString(value)
+ cellData.T, cellData.V, err = f.setCellString(value)
return err
}
// setCellString provides a function to set string type to shared string
// table.
-func (f *File) setCellString(value string) (t string, v string) {
+func (f *File) setCellString(value string) (t, v string, err error) {
if len(value) > TotalCellChars {
value = value[:TotalCellChars]
}
t = "s"
- v = strconv.Itoa(f.setSharedString(value))
+ var si int
+ if si, err = f.setSharedString(value); err != nil {
+ return
+ }
+ v = strconv.Itoa(si)
+ return
+}
+
+// sharedStringsLoader load shared string table from system temporary file to
+// memory, and reset shared string table for reader.
+func (f *File) sharedStringsLoader() (err error) {
+ f.Lock()
+ defer f.Unlock()
+ if path, ok := f.tempFiles.Load(dafaultXMLPathSharedStrings); ok {
+ f.Pkg.Store(dafaultXMLPathSharedStrings, f.readBytes(dafaultXMLPathSharedStrings))
+ f.tempFiles.Delete(dafaultXMLPathSharedStrings)
+ err = os.Remove(path.(string))
+ f.SharedStrings, f.sharedStringItemMap = nil, nil
+ }
return
}
// setSharedString provides a function to add string to the share string table.
-func (f *File) setSharedString(val string) int {
+func (f *File) setSharedString(val string) (int, error) {
+ if err := f.sharedStringsLoader(); err != nil {
+ return 0, err
+ }
sst := f.sharedStringsReader()
f.Lock()
defer f.Unlock()
if i, ok := f.sharedStringsMap[val]; ok {
- return i
+ return i, nil
}
sst.Count++
sst.UniqueCount++
@@ -377,7 +399,7 @@ func (f *File) setSharedString(val string) int {
_, val, t.Space = setCellStr(val)
sst.SI = append(sst.SI, xlsxSI{T: &t})
f.sharedStringsMap[val] = sst.UniqueCount - 1
- return sst.UniqueCount - 1
+ return sst.UniqueCount - 1, nil
}
// setCellStr provides a function to set string type to cell.
@@ -762,6 +784,34 @@ func (f *File) GetCellRichText(sheet, cell string) (runs []RichTextRun, err erro
return
}
+// newRpr create run properties for the rich text by given font format.
+func newRpr(fnt *Font) *xlsxRPr {
+ rpr := xlsxRPr{}
+ trueVal := ""
+ if fnt.Bold {
+ rpr.B = &trueVal
+ }
+ if fnt.Italic {
+ rpr.I = &trueVal
+ }
+ if fnt.Strike {
+ rpr.Strike = &trueVal
+ }
+ if fnt.Underline != "" {
+ rpr.U = &attrValString{Val: &fnt.Underline}
+ }
+ if fnt.Family != "" {
+ rpr.RFont = &attrValString{Val: &fnt.Family}
+ }
+ if fnt.Size > 0.0 {
+ rpr.Sz = &attrValFloat{Val: &fnt.Size}
+ }
+ if fnt.Color != "" {
+ rpr.Color = &xlsxColor{RGB: getPaletteColor(fnt.Color)}
+ }
+ return &rpr
+}
+
// SetCellRichText provides a function to set cell with rich text by given
// worksheet. For example, set rich text on the A1 cell of the worksheet named
// Sheet1:
@@ -875,6 +925,9 @@ func (f *File) SetCellRichText(sheet, cell string, runs []RichTextRun) error {
if err != nil {
return err
}
+ if err := f.sharedStringsLoader(); err != nil {
+ return err
+ }
cellData.S = f.prepareCellStyle(ws, col, cellData.S)
si := xlsxSI{}
sst := f.sharedStringsReader()
@@ -889,30 +942,7 @@ func (f *File) SetCellRichText(sheet, cell string, runs []RichTextRun) error {
_, run.T.Val, run.T.Space = setCellStr(textRun.Text)
fnt := textRun.Font
if fnt != nil {
- rpr := xlsxRPr{}
- trueVal := ""
- if fnt.Bold {
- rpr.B = &trueVal
- }
- if fnt.Italic {
- rpr.I = &trueVal
- }
- if fnt.Strike {
- rpr.Strike = &trueVal
- }
- if fnt.Underline != "" {
- rpr.U = &attrValString{Val: &fnt.Underline}
- }
- if fnt.Family != "" {
- rpr.RFont = &attrValString{Val: &fnt.Family}
- }
- if fnt.Size > 0.0 {
- rpr.Sz = &attrValFloat{Val: &fnt.Size}
- }
- if fnt.Color != "" {
- rpr.Color = &xlsxColor{RGB: getPaletteColor(fnt.Color)}
- }
- run.RPr = &rpr
+ run.RPr = newRpr(fnt)
}
textRuns = append(textRuns, run)
}
diff --git a/cell_test.go b/cell_test.go
index 4a78a06..03de73b 100644
--- a/cell_test.go
+++ b/cell_test.go
@@ -649,3 +649,20 @@ func TestFormattedValue2(t *testing.T) {
v = f.formattedValue(1, "43528", false)
assert.Equal(t, "43528", v)
}
+
+func TestSharedStringsError(t *testing.T) {
+ f, err := OpenFile(filepath.Join("test", "Book1.xlsx"), Options{UnzipXMLSizeLimit: 128})
+ assert.NoError(t, err)
+ f.tempFiles.Store(dafaultXMLPathSharedStrings, "")
+ assert.Equal(t, "1", f.getFromStringItemMap(1))
+
+ // Test reload the file error on set cell cell and rich text. The error message was different between macOS and Windows.
+ err = f.SetCellValue("Sheet1", "A19", "A19")
+ assert.Error(t, err)
+
+ f.tempFiles.Store(dafaultXMLPathSharedStrings, "")
+ err = f.SetCellRichText("Sheet1", "A19", []RichTextRun{})
+ assert.Error(t, err)
+
+ assert.NoError(t, f.Close())
+}
diff --git a/docProps.go b/docProps.go
index c8ab27c..271b370 100644
--- a/docProps.go
+++ b/docProps.go
@@ -27,8 +27,8 @@ import (
// Application | The name of the application that created this document.
// |
// ScaleCrop | Indicates the display mode of the document thumbnail. Set this element
-// | to TRUE to enable scaling of the document thumbnail to the display. Set
-// | this element to FALSE to enable cropping of the document thumbnail to
+// | to 'true' to enable scaling of the document thumbnail to the display. Set
+// | this element to 'false' to enable cropping of the document thumbnail to
// | show only sections that will fit the display.
// |
// DocSecurity | Security level of a document as a numeric value. Document security is
@@ -41,8 +41,8 @@ import (
// Company | The name of a company associated with the document.
// |
// LinksUpToDate | Indicates whether hyperlinks in a document are up-to-date. Set this
-// | element to TRUE to indicate that hyperlinks are updated. Set this
-// | element to FALSE to indicate that hyperlinks are outdated.
+// | element to 'true' to indicate that hyperlinks are updated. Set this
+// | element to 'false' to indicate that hyperlinks are outdated.
// |
// HyperlinksChanged | Specifies that one or more hyperlinks in this part were updated
// | exclusively in this part by a producer. The next producer to open this
@@ -75,7 +75,7 @@ func (f *File) SetAppProps(appProperties *AppProperties) (err error) {
field string
)
app = new(xlsxProperties)
- if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/app.xml")))).
+ if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(dafaultXMLPathDocPropsApp)))).
Decode(app); err != nil && err != io.EOF {
err = fmt.Errorf("xml decode error: %s", err)
return
@@ -95,14 +95,14 @@ func (f *File) SetAppProps(appProperties *AppProperties) (err error) {
}
app.Vt = NameSpaceDocumentPropertiesVariantTypes.Value
output, err = xml.Marshal(app)
- f.saveFileList("docProps/app.xml", output)
+ f.saveFileList(dafaultXMLPathDocPropsApp, output)
return
}
// GetAppProps provides a function to get document application properties.
func (f *File) GetAppProps() (ret *AppProperties, err error) {
var app = new(xlsxProperties)
- if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/app.xml")))).
+ if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(dafaultXMLPathDocPropsApp)))).
Decode(app); err != nil && err != io.EOF {
err = fmt.Errorf("xml decode error: %s", err)
return
@@ -181,7 +181,7 @@ func (f *File) SetDocProps(docProperties *DocProperties) (err error) {
)
core = new(decodeCoreProperties)
- if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/core.xml")))).
+ if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(dafaultXMLPathDocPropsCore)))).
Decode(core); err != nil && err != io.EOF {
err = fmt.Errorf("xml decode error: %s", err)
return
@@ -223,7 +223,7 @@ func (f *File) SetDocProps(docProperties *DocProperties) (err error) {
newProps.Modified.Text = docProperties.Modified
}
output, err = xml.Marshal(newProps)
- f.saveFileList("docProps/core.xml", output)
+ f.saveFileList(dafaultXMLPathDocPropsCore, output)
return
}
@@ -232,7 +232,7 @@ func (f *File) SetDocProps(docProperties *DocProperties) (err error) {
func (f *File) GetDocProps() (ret *DocProperties, err error) {
var core = new(decodeCoreProperties)
- if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/core.xml")))).
+ if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(dafaultXMLPathDocPropsCore)))).
Decode(core); err != nil && err != io.EOF {
err = fmt.Errorf("xml decode error: %s", err)
return
diff --git a/docProps_test.go b/docProps_test.go
index a5c35f7..97948c1 100644
--- a/docProps_test.go
+++ b/docProps_test.go
@@ -35,13 +35,13 @@ func TestSetAppProps(t *testing.T) {
AppVersion: "16.0000",
}))
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetAppProps.xlsx")))
- f.Pkg.Store("docProps/app.xml", nil)
+ f.Pkg.Store(dafaultXMLPathDocPropsApp, nil)
assert.NoError(t, f.SetAppProps(&AppProperties{}))
assert.NoError(t, f.Close())
// Test unsupported charset
f = NewFile()
- f.Pkg.Store("docProps/app.xml", MacintoshCyrillicCharset)
+ f.Pkg.Store(dafaultXMLPathDocPropsApp, MacintoshCyrillicCharset)
assert.EqualError(t, f.SetAppProps(&AppProperties{}), "xml decode error: XML syntax error on line 1: invalid UTF-8")
}
@@ -53,14 +53,14 @@ func TestGetAppProps(t *testing.T) {
props, err := f.GetAppProps()
assert.NoError(t, err)
assert.Equal(t, props.Application, "Microsoft Macintosh Excel")
- f.Pkg.Store("docProps/app.xml", nil)
+ f.Pkg.Store(dafaultXMLPathDocPropsApp, nil)
_, err = f.GetAppProps()
assert.NoError(t, err)
assert.NoError(t, f.Close())
// Test unsupported charset
f = NewFile()
- f.Pkg.Store("docProps/app.xml", MacintoshCyrillicCharset)
+ f.Pkg.Store(dafaultXMLPathDocPropsApp, MacintoshCyrillicCharset)
_, err = f.GetAppProps()
assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
}
@@ -87,13 +87,13 @@ func TestSetDocProps(t *testing.T) {
Version: "1.0.0",
}))
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetDocProps.xlsx")))
- f.Pkg.Store("docProps/core.xml", nil)
+ f.Pkg.Store(dafaultXMLPathDocPropsCore, nil)
assert.NoError(t, f.SetDocProps(&DocProperties{}))
assert.NoError(t, f.Close())
// Test unsupported charset
f = NewFile()
- f.Pkg.Store("docProps/core.xml", MacintoshCyrillicCharset)
+ f.Pkg.Store(dafaultXMLPathDocPropsCore, MacintoshCyrillicCharset)
assert.EqualError(t, f.SetDocProps(&DocProperties{}), "xml decode error: XML syntax error on line 1: invalid UTF-8")
}
@@ -105,14 +105,14 @@ func TestGetDocProps(t *testing.T) {
props, err := f.GetDocProps()
assert.NoError(t, err)
assert.Equal(t, props.Creator, "Microsoft Office User")
- f.Pkg.Store("docProps/core.xml", nil)
+ f.Pkg.Store(dafaultXMLPathDocPropsCore, nil)
_, err = f.GetDocProps()
assert.NoError(t, err)
assert.NoError(t, f.Close())
// Test unsupported charset
f = NewFile()
- f.Pkg.Store("docProps/core.xml", MacintoshCyrillicCharset)
+ f.Pkg.Store(dafaultXMLPathDocPropsCore, MacintoshCyrillicCharset)
_, err = f.GetDocProps()
assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
}
diff --git a/errors.go b/errors.go
index 4230c14..9460803 100644
--- a/errors.go
+++ b/errors.go
@@ -143,8 +143,8 @@ var (
// characters length that exceeds the limit.
ErrCellCharsLength = fmt.Errorf("cell value must be 0-%d characters", TotalCellChars)
// ErrOptionsUnzipSizeLimit defined the error message for receiving
- // invalid UnzipSizeLimit and WorksheetUnzipMemLimit.
- ErrOptionsUnzipSizeLimit = errors.New("the value of UnzipSizeLimit should be greater than or equal to WorksheetUnzipMemLimit")
+ // invalid UnzipSizeLimit and UnzipXMLSizeLimit.
+ ErrOptionsUnzipSizeLimit = errors.New("the value of UnzipSizeLimit should be greater than or equal to UnzipXMLSizeLimit")
// ErrSave defined the error message for saving file.
ErrSave = errors.New("no path defined for file, consider File.WriteTo or File.Write")
// ErrAttrValBool defined the error message on marshal and unmarshal
diff --git a/excelize.go b/excelize.go
index c5778c8..25acd54 100644
--- a/excelize.go
+++ b/excelize.go
@@ -32,29 +32,30 @@ import (
// File define a populated spreadsheet file struct.
type File struct {
sync.Mutex
- options *Options
- xmlAttr map[string][]xml.Attr
- checked map[string]bool
- sheetMap map[string]string
- streams map[string]*StreamWriter
- tempFiles sync.Map
- CalcChain *xlsxCalcChain
- Comments map[string]*xlsxComments
- ContentTypes *xlsxTypes
- Drawings sync.Map
- Path string
- SharedStrings *xlsxSST
- sharedStringsMap map[string]int
- Sheet sync.Map
- SheetCount int
- Styles *xlsxStyleSheet
- Theme *xlsxTheme
- DecodeVMLDrawing map[string]*decodeVmlDrawing
- VMLDrawing map[string]*vmlDrawing
- WorkBook *xlsxWorkbook
- Relationships sync.Map
- Pkg sync.Map
- CharsetReader charsetTranscoderFn
+ options *Options
+ xmlAttr map[string][]xml.Attr
+ checked map[string]bool
+ sheetMap map[string]string
+ streams map[string]*StreamWriter
+ tempFiles sync.Map
+ CalcChain *xlsxCalcChain
+ Comments map[string]*xlsxComments
+ ContentTypes *xlsxTypes
+ Drawings sync.Map
+ Path string
+ SharedStrings *xlsxSST
+ sharedStringsMap map[string]int
+ sharedStringItemMap *sync.Map
+ Sheet sync.Map
+ SheetCount int
+ Styles *xlsxStyleSheet
+ Theme *xlsxTheme
+ DecodeVMLDrawing map[string]*decodeVmlDrawing
+ VMLDrawing map[string]*vmlDrawing
+ WorkBook *xlsxWorkbook
+ Relationships sync.Map
+ Pkg sync.Map
+ CharsetReader charsetTranscoderFn
}
type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, err error)
@@ -68,17 +69,18 @@ type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, e
//
// UnzipSizeLimit specifies the unzip size limit in bytes on open the
// spreadsheet, this value should be greater than or equal to
-// WorksheetUnzipMemLimit, the default size limit is 16GB.
+// UnzipXMLSizeLimit, the default size limit is 16GB.
//
-// WorksheetUnzipMemLimit specifies the memory limit on unzipping worksheet in
-// bytes, worksheet XML will be extracted to system temporary directory when
-// the file size is over this value, this value should be less than or equal
-// to UnzipSizeLimit, the default value is 16MB.
+// UnzipXMLSizeLimit specifies the memory limit on unzipping worksheet and
+// shared string table in bytes, worksheet XML will be extracted to system
+// temporary directory when the file size is over this value, this value
+// should be less than or equal to UnzipSizeLimit, the default value is
+// 16MB.
type Options struct {
- Password string
- RawCellValue bool
- UnzipSizeLimit int64
- WorksheetUnzipMemLimit int64
+ Password string
+ RawCellValue bool
+ UnzipSizeLimit int64
+ UnzipXMLSizeLimit int64
}
// OpenFile take the name of an spreadsheet file and returns a populated
@@ -111,7 +113,7 @@ func OpenFile(filename string, opt ...Options) (*File, error) {
// newFile is object builder
func newFile() *File {
return &File{
- options: &Options{UnzipSizeLimit: UnzipSizeLimit, WorksheetUnzipMemLimit: StreamChunkSize},
+ options: &Options{UnzipSizeLimit: UnzipSizeLimit, UnzipXMLSizeLimit: StreamChunkSize},
xmlAttr: make(map[string][]xml.Attr),
checked: make(map[string]bool),
sheetMap: make(map[string]string),
@@ -138,17 +140,17 @@ func OpenReader(r io.Reader, opt ...Options) (*File, error) {
f.options = parseOptions(opt...)
if f.options.UnzipSizeLimit == 0 {
f.options.UnzipSizeLimit = UnzipSizeLimit
- if f.options.WorksheetUnzipMemLimit > f.options.UnzipSizeLimit {
- f.options.UnzipSizeLimit = f.options.WorksheetUnzipMemLimit
+ if f.options.UnzipXMLSizeLimit > f.options.UnzipSizeLimit {
+ f.options.UnzipSizeLimit = f.options.UnzipXMLSizeLimit
}
}
- if f.options.WorksheetUnzipMemLimit == 0 {
- f.options.WorksheetUnzipMemLimit = StreamChunkSize
- if f.options.UnzipSizeLimit < f.options.WorksheetUnzipMemLimit {
- f.options.WorksheetUnzipMemLimit = f.options.UnzipSizeLimit
+ if f.options.UnzipXMLSizeLimit == 0 {
+ f.options.UnzipXMLSizeLimit = StreamChunkSize
+ if f.options.UnzipSizeLimit < f.options.UnzipXMLSizeLimit {
+ f.options.UnzipXMLSizeLimit = f.options.UnzipSizeLimit
}
}
- if f.options.WorksheetUnzipMemLimit > f.options.UnzipSizeLimit {
+ if f.options.UnzipXMLSizeLimit > f.options.UnzipSizeLimit {
return nil, ErrOptionsUnzipSizeLimit
}
if bytes.Contains(b, oleIdentifier) {
diff --git a/excelize_test.go b/excelize_test.go
index 4c136b6..9aaaae9 100644
--- a/excelize_test.go
+++ b/excelize_test.go
@@ -201,7 +201,7 @@ func TestCharsetTranscoder(t *testing.T) {
func TestOpenReader(t *testing.T) {
_, err := OpenReader(strings.NewReader(""))
assert.EqualError(t, err, "zip: not a valid zip file")
- _, err = OpenReader(bytes.NewReader(oleIdentifier), Options{Password: "password", WorksheetUnzipMemLimit: UnzipSizeLimit + 1})
+ _, err = OpenReader(bytes.NewReader(oleIdentifier), Options{Password: "password", UnzipXMLSizeLimit: UnzipSizeLimit + 1})
assert.EqualError(t, err, "decrypted file failed")
// Test open spreadsheet with unzip size limit.
@@ -225,7 +225,7 @@ func TestOpenReader(t *testing.T) {
assert.NoError(t, f.Close())
// Test open spreadsheet with invalid optioins.
- _, err = OpenReader(bytes.NewReader(oleIdentifier), Options{UnzipSizeLimit: 1, WorksheetUnzipMemLimit: 2})
+ _, err = OpenReader(bytes.NewReader(oleIdentifier), Options{UnzipSizeLimit: 1, UnzipXMLSizeLimit: 2})
assert.EqualError(t, err, ErrOptionsUnzipSizeLimit.Error())
// Test unexpected EOF.
@@ -1208,7 +1208,7 @@ func TestContentTypesReader(t *testing.T) {
// Test unsupported charset.
f := NewFile()
f.ContentTypes = nil
- f.Pkg.Store("[Content_Types].xml", MacintoshCyrillicCharset)
+ f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
f.contentTypesReader()
}
@@ -1216,7 +1216,7 @@ func TestWorkbookReader(t *testing.T) {
// Test unsupported charset.
f := NewFile()
f.WorkBook = nil
- f.Pkg.Store("xl/workbook.xml", MacintoshCyrillicCharset)
+ f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
f.workbookReader()
}
diff --git a/file.go b/file.go
index e2aeb4a..6c8bd93 100644
--- a/file.go
+++ b/file.go
@@ -14,6 +14,7 @@ package excelize
import (
"archive/zip"
"bytes"
+ "encoding/xml"
"io"
"os"
"path/filepath"
@@ -27,15 +28,15 @@ import (
//
func NewFile() *File {
f := newFile()
- 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.Pkg.Store("_rels/.rels", []byte(xml.Header+templateRels))
+ f.Pkg.Store(dafaultXMLPathDocPropsApp, []byte(xml.Header+templateDocpropsApp))
+ f.Pkg.Store(dafaultXMLPathDocPropsCore, []byte(xml.Header+templateDocpropsCore))
+ f.Pkg.Store("xl/_rels/workbook.xml.rels", []byte(xml.Header+templateWorkbookRels))
+ f.Pkg.Store("xl/theme/theme1.xml", []byte(xml.Header+templateTheme))
+ f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(xml.Header+templateSheet))
+ f.Pkg.Store(defaultXMLPathStyles, []byte(xml.Header+templateStyles))
+ f.Pkg.Store(defaultXMLPathWorkbook, []byte(xml.Header+templateWorkbook))
+ f.Pkg.Store(defaultXMLPathContentTypes, []byte(xml.Header+templateContentTypes))
f.SheetCount = 1
f.CalcChain = f.calcChainReader()
f.Comments = make(map[string]*xlsxComments)
@@ -159,6 +160,7 @@ func (f *File) writeToZip(zw *zip.Writer) error {
f.workBookWriter()
f.workSheetWriter()
f.relsWriter()
+ f.sharedStringsLoader()
f.sharedStringsWriter()
f.styleSheetWriter()
@@ -196,6 +198,9 @@ func (f *File) writeToZip(zw *zip.Writer) error {
return true
})
f.tempFiles.Range(func(path, content interface{}) bool {
+ if _, ok := f.Pkg.Load(path); ok {
+ return true
+ }
var fi io.Writer
fi, err = zw.Create(path.(string))
if err != nil {
diff --git a/lib.go b/lib.go
index 0efc180..8ec121b 100644
--- a/lib.go
+++ b/lib.go
@@ -30,8 +30,8 @@ func (f *File) ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
var (
err error
docPart = map[string]string{
- "[content_types].xml": "[Content_Types].xml",
- "xl/sharedstrings.xml": "xl/sharedStrings.xml",
+ "[content_types].xml": defaultXMLPathContentTypes,
+ "xl/sharedstrings.xml": dafaultXMLPathSharedStrings,
}
fileList = make(map[string][]byte, len(r.File))
worksheets int
@@ -47,9 +47,15 @@ func (f *File) ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
if partName, ok := docPart[strings.ToLower(fileName)]; ok {
fileName = partName
}
+ if strings.EqualFold(fileName, dafaultXMLPathSharedStrings) && fileSize > f.options.UnzipXMLSizeLimit {
+ if tempFile, err := f.unzipToTemp(v); err == nil {
+ f.tempFiles.Store(fileName, tempFile)
+ continue
+ }
+ }
if strings.HasPrefix(fileName, "xl/worksheets/sheet") {
worksheets++
- if fileSize > f.options.WorksheetUnzipMemLimit && !v.FileInfo().IsDir() {
+ if fileSize > f.options.UnzipXMLSizeLimit && !v.FileInfo().IsDir() {
if tempFile, err := f.unzipToTemp(v); err == nil {
f.tempFiles.Store(fileName, tempFile)
continue
@@ -120,7 +126,7 @@ func (f *File) readTemp(name string) (file *os.File, err error) {
// saveFileList provides a function to update given file content in file list
// of spreadsheet.
func (f *File) saveFileList(name string, content []byte) {
- f.Pkg.Store(name, append([]byte(XMLHeader), content...))
+ f.Pkg.Store(name, append([]byte(xml.Header), content...))
}
// Read file content as string in a archive file.
diff --git a/rows.go b/rows.go
index 7b2f52f..5071bb6 100644
--- a/rows.go
+++ b/rows.go
@@ -21,6 +21,7 @@ import (
"math/big"
"os"
"strconv"
+ "sync"
"github.com/mohae/deepcopy"
)
@@ -244,7 +245,7 @@ func (f *File) Rows(sheet string) (*Rows, error) {
decoder *xml.Decoder
tempFile *os.File
)
- if needClose, decoder, tempFile, err = f.sheetDecoder(name); needClose && err == nil {
+ if needClose, decoder, tempFile, err = f.xmlDecoder(name); needClose && err == nil {
defer tempFile.Close()
}
for {
@@ -271,7 +272,7 @@ func (f *File) Rows(sheet string) (*Rows, error) {
if xmlElement.Name.Local == "sheetData" {
rows.f = f
rows.sheet = name
- _, rows.decoder, rows.tempFile, err = f.sheetDecoder(name)
+ _, rows.decoder, rows.tempFile, err = f.xmlDecoder(name)
return &rows, err
}
}
@@ -279,9 +280,46 @@ func (f *File) Rows(sheet string) (*Rows, error) {
return &rows, nil
}
-// sheetDecoder creates XML decoder by given path in the zip from memory data
+// getFromStringItemMap build shared string item map from system temporary
+// file at one time, and return value by given to string index.
+func (f *File) getFromStringItemMap(index int) string {
+ if f.sharedStringItemMap != nil {
+ if value, ok := f.sharedStringItemMap.Load(index); ok {
+ return value.(string)
+ }
+ return strconv.Itoa(index)
+ }
+ f.sharedStringItemMap = &sync.Map{}
+ needClose, decoder, tempFile, err := f.xmlDecoder(dafaultXMLPathSharedStrings)
+ if needClose && err == nil {
+ defer tempFile.Close()
+ }
+ var (
+ inElement string
+ i int
+ )
+ for {
+ token, _ := decoder.Token()
+ if token == nil {
+ break
+ }
+ switch xmlElement := token.(type) {
+ case xml.StartElement:
+ inElement = xmlElement.Name.Local
+ if inElement == "si" {
+ si := xlsxSI{}
+ _ = decoder.DecodeElement(&si, &xmlElement)
+ f.sharedStringItemMap.Store(i, si.String())
+ i++
+ }
+ }
+ }
+ return f.getFromStringItemMap(index)
+}
+
+// xmlDecoder creates XML decoder by given path in the zip from memory data
// or system temporary file.
-func (f *File) sheetDecoder(name string) (bool, *xml.Decoder, *os.File, error) {
+func (f *File) xmlDecoder(name string) (bool, *xml.Decoder, *os.File, error) {
var (
content []byte
err error
@@ -373,7 +411,7 @@ func (f *File) sharedStringsReader() *xlsxSST {
relPath := f.getWorkbookRelsPath()
if f.SharedStrings == nil {
var sharedStrings xlsxSST
- ss := f.readXML("xl/sharedStrings.xml")
+ ss := f.readXML(dafaultXMLPathSharedStrings)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))).
Decode(&sharedStrings); err != nil && err != io.EOF {
log.Printf("xml decode error: %s", err)
@@ -415,6 +453,9 @@ func (c *xlsxC) getValueFrom(f *File, d *xlsxSST, raw bool) (string, error) {
if c.V != "" {
xlsxSI := 0
xlsxSI, _ = strconv.Atoi(c.V)
+ if _, ok := f.tempFiles.Load(dafaultXMLPathSharedStrings); ok {
+ return f.formattedValue(c.S, f.getFromStringItemMap(xlsxSI), raw), nil
+ }
if len(d.SI) > xlsxSI {
return f.formattedValue(c.S, d.SI[xlsxSI].String(), raw), nil
}
diff --git a/rows_test.go b/rows_test.go
index 0c154a4..63321ce 100644
--- a/rows_test.go
+++ b/rows_test.go
@@ -56,11 +56,18 @@ func TestRows(t *testing.T) {
assert.NoError(t, err)
// Test reload the file to memory from system temporary directory.
- f, err = OpenFile(filepath.Join("test", "Book1.xlsx"), Options{WorksheetUnzipMemLimit: 1024})
+ f, err = OpenFile(filepath.Join("test", "Book1.xlsx"), Options{UnzipXMLSizeLimit: 128})
assert.NoError(t, err)
value, err := f.GetCellValue("Sheet1", "A19")
assert.NoError(t, err)
assert.Equal(t, "Total:", value)
+ // Test load shared string table to memory
+ err = f.SetCellValue("Sheet1", "A19", "A19")
+ assert.NoError(t, err)
+ value, err = f.GetCellValue("Sheet1", "A19")
+ assert.NoError(t, err)
+ assert.Equal(t, "A19", value)
+ assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetRow.xlsx")))
assert.NoError(t, f.Close())
}
@@ -200,7 +207,7 @@ func TestColumns(t *testing.T) {
func TestSharedStringsReader(t *testing.T) {
f := NewFile()
- f.Pkg.Store("xl/sharedStrings.xml", MacintoshCyrillicCharset)
+ f.Pkg.Store(dafaultXMLPathSharedStrings, MacintoshCyrillicCharset)
f.sharedStringsReader()
si := xlsxSI{}
assert.EqualValues(t, "", si.String())
diff --git a/sheet.go b/sheet.go
index 99ae1a2..17f6693 100644
--- a/sheet.go
+++ b/sheet.go
@@ -76,7 +76,7 @@ func (f *File) contentTypesReader() *xlsxTypes {
f.ContentTypes = new(xlsxTypes)
f.ContentTypes.Lock()
defer f.ContentTypes.Unlock()
- if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("[Content_Types].xml")))).
+ if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathContentTypes)))).
Decode(f.ContentTypes); err != nil && err != io.EOF {
log.Printf("xml decode error: %s", err)
}
@@ -89,7 +89,7 @@ func (f *File) contentTypesReader() *xlsxTypes {
func (f *File) contentTypesWriter() {
if f.ContentTypes != nil {
output, _ := xml.Marshal(f.ContentTypes)
- f.saveFileList("[Content_Types].xml", output)
+ f.saveFileList(defaultXMLPathContentTypes, output)
}
}
@@ -304,7 +304,7 @@ func (f *File) relsWriter() {
// setAppXML update docProps/app.xml file of XML.
func (f *File) setAppXML() {
- f.saveFileList("docProps/app.xml", []byte(templateDocpropsApp))
+ f.saveFileList(dafaultXMLPathDocPropsApp, []byte(templateDocpropsApp))
}
// replaceRelationshipsBytes; Some tools that read spreadsheet files have very
diff --git a/stream.go b/stream.go
index 65d6b72..4bd721e 100644
--- a/stream.go
+++ b/stream.go
@@ -112,7 +112,7 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
}
f.streams[sheetPath] = sw
- _, _ = sw.rawData.WriteString(XMLHeader + `<worksheet` + templateNamespaceIDMap)
+ _, _ = sw.rawData.WriteString(xml.Header + `<worksheet` + templateNamespaceIDMap)
bulkAppendFields(&sw.rawData, sw.worksheet, 2, 5)
return sw, err
}
diff --git a/styles.go b/styles.go
index d9e3e8f..da532fd 100644
--- a/styles.go
+++ b/styles.go
@@ -1078,7 +1078,7 @@ func (f *File) stylesReader() *xlsxStyleSheet {
if f.Styles == nil {
f.Styles = new(xlsxStyleSheet)
- if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/styles.xml")))).
+ if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathStyles)))).
Decode(f.Styles); err != nil && err != io.EOF {
log.Printf("xml decode error: %s", err)
}
@@ -1092,7 +1092,7 @@ func (f *File) stylesReader() *xlsxStyleSheet {
func (f *File) styleSheetWriter() {
if f.Styles != nil {
output, _ := xml.Marshal(f.Styles)
- f.saveFileList("xl/styles.xml", f.replaceNameSpaceBytes("xl/styles.xml", output))
+ f.saveFileList(defaultXMLPathStyles, f.replaceNameSpaceBytes(defaultXMLPathStyles, output))
}
}
@@ -1101,7 +1101,7 @@ func (f *File) styleSheetWriter() {
func (f *File) sharedStringsWriter() {
if f.SharedStrings != nil {
output, _ := xml.Marshal(f.SharedStrings)
- f.saveFileList("xl/sharedStrings.xml", f.replaceNameSpaceBytes("xl/sharedStrings.xml", output))
+ f.saveFileList(dafaultXMLPathSharedStrings, f.replaceNameSpaceBytes(dafaultXMLPathSharedStrings, output))
}
}
diff --git a/styles_test.go b/styles_test.go
index 720340f..3597c36 100644
--- a/styles_test.go
+++ b/styles_test.go
@@ -300,7 +300,7 @@ func TestStylesReader(t *testing.T) {
f := NewFile()
// Test read styles with unsupported charset.
f.Styles = nil
- f.Pkg.Store("xl/styles.xml", MacintoshCyrillicCharset)
+ f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
assert.EqualValues(t, new(xlsxStyleSheet), f.stylesReader())
}
diff --git a/templates.go b/templates.go
index 56588c4..1783d7c 100644
--- a/templates.go
+++ b/templates.go
@@ -14,13 +14,22 @@
package excelize
-// XMLHeader define an XML declaration can also contain a standalone declaration.
-const XMLHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
+import "encoding/xml"
var (
// XMLHeaderByte define an XML declaration can also contain a standalone
// declaration.
- XMLHeaderByte = []byte(XMLHeader)
+ XMLHeaderByte = []byte(xml.Header)
+)
+
+const (
+ defaultXMLPathContentTypes = "[Content_Types].xml"
+ dafaultXMLPathDocPropsApp = "docProps/app.xml"
+ dafaultXMLPathDocPropsCore = "docProps/core.xml"
+ dafaultXMLPathCalcChain = "xl/calcChain.xml"
+ dafaultXMLPathSharedStrings = "xl/sharedStrings.xml"
+ defaultXMLPathStyles = "xl/styles.xml"
+ defaultXMLPathWorkbook = "xl/workbook.xml"
)
const templateDocpropsApp = `<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><TotalTime>0</TotalTime><Application>Go Excelize</Application></Properties>`
diff --git a/xmlDrawing.go b/xmlDrawing.go
index 1690554..4ae6a29 100644
--- a/xmlDrawing.go
+++ b/xmlDrawing.go
@@ -37,21 +37,20 @@ var (
// Source relationship and namespace.
const (
- SourceRelationshipOfficeDocument = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
- SourceRelationshipChart = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"
- SourceRelationshipComments = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"
- SourceRelationshipImage = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
- SourceRelationshipTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table"
- SourceRelationshipDrawingML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"
- SourceRelationshipDrawingVML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
- SourceRelationshipHyperLink = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
- SourceRelationshipWorkSheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"
- SourceRelationshipChartsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet"
- SourceRelationshipDialogsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet"
- SourceRelationshipPivotTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable"
- SourceRelationshipPivotCache = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition"
- SourceRelationshipSharedStrings = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"
-
+ SourceRelationshipOfficeDocument = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
+ SourceRelationshipChart = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"
+ SourceRelationshipComments = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"
+ SourceRelationshipImage = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
+ SourceRelationshipTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table"
+ SourceRelationshipDrawingML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"
+ SourceRelationshipDrawingVML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
+ SourceRelationshipHyperLink = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
+ SourceRelationshipWorkSheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"
+ SourceRelationshipChartsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet"
+ SourceRelationshipDialogsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet"
+ SourceRelationshipPivotTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable"
+ SourceRelationshipPivotCache = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition"
+ SourceRelationshipSharedStrings = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"
SourceRelationshipVBAProject = "http://schemas.microsoft.com/office/2006/relationships/vbaProject"
NameSpaceXML = "http://www.w3.org/XML/1998/namespace"
NameSpaceXMLSchemaInstance = "http://www.w3.org/2001/XMLSchema-instance"