diff --git a/crypt_test.go b/crypt_test.go index f7c465ed096..95b6f52cc42 100644 --- a/crypt_test.go +++ b/crypt_test.go @@ -48,6 +48,8 @@ func TestEncrypt(t *testing.T) { cell, err = f.GetCellValue("Sheet1", "A1") assert.NoError(t, err) assert.Equal(t, "SECRET", cell) + // Test remove password by save workbook with options + assert.NoError(t, f.Save(Options{Password: ""})) assert.NoError(t, f.Close()) } diff --git a/excelize.go b/excelize.go index f1269fef682..bb4bde063a8 100644 --- a/excelize.go +++ b/excelize.go @@ -136,13 +136,13 @@ func newFile() *File { // OpenReader read data stream from io.Reader and return a populated // spreadsheet file. -func OpenReader(r io.Reader, opt ...Options) (*File, error) { +func OpenReader(r io.Reader, opts ...Options) (*File, error) { b, err := ioutil.ReadAll(r) if err != nil { return nil, err } f := newFile() - f.options = parseOptions(opt...) + f.options = parseOptions(opts...) if f.options.UnzipSizeLimit == 0 { f.options.UnzipSizeLimit = UnzipSizeLimit if f.options.UnzipXMLSizeLimit > f.options.UnzipSizeLimit { diff --git a/excelize_test.go b/excelize_test.go index 19aba7eeaf0..93cd2bf5bbb 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -186,18 +186,15 @@ func TestOpenFile(t *testing.T) { func TestSaveFile(t *testing.T) { f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) - if !assert.NoError(t, err) { - t.FailNow() - } + assert.NoError(t, err) assert.EqualError(t, f.SaveAs(filepath.Join("test", "TestSaveFile.xlsb")), ErrWorkbookFileFormat.Error()) for _, ext := range []string{".xlam", ".xlsm", ".xlsx", ".xltm", ".xltx"} { assert.NoError(t, f.SaveAs(filepath.Join("test", fmt.Sprintf("TestSaveFile%s", ext)))) } assert.NoError(t, f.Close()) + f, err = OpenFile(filepath.Join("test", "TestSaveFile.xlsx")) - if !assert.NoError(t, err) { - t.FailNow() - } + assert.NoError(t, err) assert.NoError(t, f.Save()) assert.NoError(t, f.Close()) } diff --git a/file.go b/file.go index 065e7c5a52a..c83d17efc44 100644 --- a/file.go +++ b/file.go @@ -55,44 +55,32 @@ func NewFile() *File { } // Save provides a function to override the spreadsheet with origin path. -func (f *File) Save() error { +func (f *File) Save(opts ...Options) error { if f.Path == "" { return ErrSave } - if f.options != nil { - return f.SaveAs(f.Path, *f.options) + for i := range opts { + f.options = &opts[i] } - return f.SaveAs(f.Path) + return f.SaveAs(f.Path, *f.options) } // SaveAs provides a function to create or update to a spreadsheet at the // provided path. -func (f *File) SaveAs(name string, opt ...Options) error { +func (f *File) SaveAs(name string, opts ...Options) error { if len(name) > MaxFilePathLength { return ErrMaxFilePathLength } f.Path = name - contentType, ok := map[string]string{ - ".xlam": ContentTypeAddinMacro, - ".xlsm": ContentTypeMacro, - ".xlsx": ContentTypeSheetML, - ".xltm": ContentTypeTemplateMacro, - ".xltx": ContentTypeTemplate, - }[filepath.Ext(f.Path)] - if !ok { + if _, ok := supportedContentType[filepath.Ext(f.Path)]; !ok { return ErrWorkbookFileFormat } - f.setContentTypePartProjectExtensions(contentType) file, err := os.OpenFile(filepath.Clean(name), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm) if err != nil { return err } defer file.Close() - f.options = nil - for i := range opt { - f.options = &opt[i] - } - return f.Write(file) + return f.Write(file, opts...) } // Close closes and cleanup the open temporary file for the spreadsheet. @@ -113,13 +101,23 @@ func (f *File) Close() error { } // Write provides a function to write to an io.Writer. -func (f *File) Write(w io.Writer) error { - _, err := f.WriteTo(w) +func (f *File) Write(w io.Writer, opts ...Options) error { + _, err := f.WriteTo(w, opts...) return err } // WriteTo implements io.WriterTo to write the file. -func (f *File) WriteTo(w io.Writer) (int64, error) { +func (f *File) WriteTo(w io.Writer, opts ...Options) (int64, error) { + for i := range opts { + f.options = &opts[i] + } + if len(f.Path) != 0 { + contentType, ok := supportedContentType[filepath.Ext(f.Path)] + if !ok { + return 0, ErrWorkbookFileFormat + } + f.setContentTypePartProjectExtensions(contentType) + } if f.options != nil && f.options.Password != "" { buf, err := f.WriteToBuffer() if err != nil { diff --git a/file_test.go b/file_test.go index 8e65c5d46de..83a9b786f62 100644 --- a/file_test.go +++ b/file_test.go @@ -71,6 +71,14 @@ func TestWriteTo(t *testing.T) { _, err := f.WriteTo(bufio.NewWriter(&buf)) assert.EqualError(t, err, "zip: FileHeader.Name too long") } + // Test write with unsupported workbook file format + { + f, buf := File{Pkg: sync.Map{}}, bytes.Buffer{} + f.Pkg.Store("/d", []byte("s")) + f.Path = "Book1.xls" + _, err := f.WriteTo(bufio.NewWriter(&buf)) + assert.EqualError(t, err, ErrWorkbookFileFormat.Error()) + } } func TestClose(t *testing.T) { diff --git a/xmlDrawing.go b/xmlDrawing.go index 34c9858382f..fc8dee58906 100644 --- a/xmlDrawing.go +++ b/xmlDrawing.go @@ -144,6 +144,15 @@ const ( // supportedImageTypes defined supported image types. var supportedImageTypes = map[string]string{".gif": ".gif", ".jpg": ".jpeg", ".jpeg": ".jpeg", ".png": ".png", ".tif": ".tiff", ".tiff": ".tiff", ".emf": ".emf", ".wmf": ".wmf", ".emz": ".emz", ".wmz": ".wmz"} +// supportedContentType defined supported file format types. +var supportedContentType = map[string]string{ + ".xlam": ContentTypeAddinMacro, + ".xlsm": ContentTypeMacro, + ".xlsx": ContentTypeSheetML, + ".xltm": ContentTypeTemplateMacro, + ".xltx": ContentTypeTemplate, +} + // xlsxCNvPr directly maps the cNvPr (Non-Visual Drawing Properties). This // element specifies non-visual canvas properties. This allows for additional // information that does not affect the appearance of the picture to be stored.