Improve security and simplify code
- Make variable name more semantic - Reduce cyclomatic complexities for the formula calculate function - Support specified unzip size limit on open file options, avoid zip bombs vulnerability attack - Typo fix for documentation and error message
This commit is contained in:
parent
f6f14f507e
commit
48c16de8bf
59
calc.go
59
calc.go
|
@ -6026,6 +6026,39 @@ func (fn *formulaFuncs) DATE(argsList *list.List) formulaArg {
|
|||
return newStringFormulaArg(timeFromExcelTime(daysBetween(excelMinTime1900.Unix(), d)+1, false).String())
|
||||
}
|
||||
|
||||
// calcDateDif is an implementation of the formula function DATEDIF,
|
||||
// calculation difference between two dates.
|
||||
func calcDateDif(unit string, diff float64, seq []int, startArg, endArg formulaArg) float64 {
|
||||
ey, sy, em, sm, ed, sd := seq[0], seq[1], seq[2], seq[3], seq[4], seq[5]
|
||||
switch unit {
|
||||
case "d":
|
||||
diff = endArg.Number - startArg.Number
|
||||
case "md":
|
||||
smMD := em
|
||||
if ed < sd {
|
||||
smMD--
|
||||
}
|
||||
diff = endArg.Number - daysBetween(excelMinTime1900.Unix(), makeDate(ey, time.Month(smMD), sd)) - 1
|
||||
case "ym":
|
||||
diff = float64(em - sm)
|
||||
if ed < sd {
|
||||
diff--
|
||||
}
|
||||
if diff < 0 {
|
||||
diff += 12
|
||||
}
|
||||
case "yd":
|
||||
syYD := sy
|
||||
if em < sm || (em == sm && ed < sd) {
|
||||
syYD++
|
||||
}
|
||||
s := daysBetween(excelMinTime1900.Unix(), makeDate(syYD, time.Month(em), ed))
|
||||
e := daysBetween(excelMinTime1900.Unix(), makeDate(sy, time.Month(sm), sd))
|
||||
diff = s - e
|
||||
}
|
||||
return diff
|
||||
}
|
||||
|
||||
// DATEDIF function calculates the number of days, months, or years between
|
||||
// two dates. The syntax of the function is:
|
||||
//
|
||||
|
@ -6051,8 +6084,6 @@ func (fn *formulaFuncs) DATEDIF(argsList *list.List) formulaArg {
|
|||
ey, emm, ed := endDate.Date()
|
||||
sm, em, diff := int(smm), int(emm), 0.0
|
||||
switch unit {
|
||||
case "d":
|
||||
return newNumberFormulaArg(endArg.Number - startArg.Number)
|
||||
case "y":
|
||||
diff = float64(ey - sy)
|
||||
if em < sm || (em == sm && ed < sd) {
|
||||
|
@ -6069,28 +6100,8 @@ func (fn *formulaFuncs) DATEDIF(argsList *list.List) formulaArg {
|
|||
mdiff += 12
|
||||
}
|
||||
diff = float64(ydiff*12 + mdiff)
|
||||
case "md":
|
||||
smMD := em
|
||||
if ed < sd {
|
||||
smMD--
|
||||
}
|
||||
diff = endArg.Number - daysBetween(excelMinTime1900.Unix(), makeDate(ey, time.Month(smMD), sd)) - 1
|
||||
case "ym":
|
||||
diff = float64(em - sm)
|
||||
if ed < sd {
|
||||
diff--
|
||||
}
|
||||
if diff < 0 {
|
||||
diff += 12
|
||||
}
|
||||
case "yd":
|
||||
syYD := sy
|
||||
if em < sm || (em == sm && ed < sd) {
|
||||
syYD++
|
||||
}
|
||||
s := daysBetween(excelMinTime1900.Unix(), makeDate(syYD, time.Month(em), ed))
|
||||
e := daysBetween(excelMinTime1900.Unix(), makeDate(sy, time.Month(sm), sd))
|
||||
diff = s - e
|
||||
case "d", "md", "ym", "yd":
|
||||
diff = calcDateDif(unit, diff, []int{ey, sy, em, sm, ed, sd}, startArg, endArg)
|
||||
default:
|
||||
return newErrorFormulaArg(formulaErrorVALUE, "DATEDIF has invalid unit")
|
||||
}
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
// the LICENSE file.
|
||||
//
|
||||
// Package excelize providing a set of functions that allow you to write to
|
||||
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||
// charts of XLSX. This library needs Go version 1.15 or later.
|
||||
// and read from XLSX / XLSM / XLTM files. Supports reading and writing
|
||||
// spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports
|
||||
// complex components by high compatibility, and provided streaming API for
|
||||
// generating or reading data from a worksheet with huge amounts of data. This
|
||||
// library needs Go version 1.15 or later.
|
||||
|
||||
package excelize
|
||||
|
||||
|
|
10
crypt.go
10
crypt.go
|
@ -3,9 +3,11 @@
|
|||
// the LICENSE file.
|
||||
//
|
||||
// Package excelize providing a set of functions that allow you to write to
|
||||
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||
// charts of XLSX. This library needs Go version 1.15 or later.
|
||||
// and read from XLSX / XLSM / XLTM files. Supports reading and writing
|
||||
// spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports
|
||||
// complex components by high compatibility, and provided streaming API for
|
||||
// generating or reading data from a worksheet with huge amounts of data. This
|
||||
// library needs Go version 1.15 or later.
|
||||
|
||||
package excelize
|
||||
|
||||
|
@ -15,6 +17,7 @@ import (
|
|||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
|
@ -22,7 +25,6 @@ import (
|
|||
"encoding/binary"
|
||||
"encoding/xml"
|
||||
"hash"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
// the LICENSE file.
|
||||
//
|
||||
// Package excelize providing a set of functions that allow you to write to
|
||||
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||
// charts of XLSX. This library needs Go version 1.15 or later.
|
||||
// and read from XLSX / XLSM / XLTM files. Supports reading and writing
|
||||
// spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports
|
||||
// complex components by high compatibility, and provided streaming API for
|
||||
// generating or reading data from a worksheet with huge amounts of data. This
|
||||
// library needs Go version 1.15 or later.
|
||||
|
||||
package excelize
|
||||
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
// the LICENSE file.
|
||||
//
|
||||
// Package excelize providing a set of functions that allow you to write to
|
||||
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||
// charts of XLSX. This library needs Go version 1.15 or later.
|
||||
// and read from XLSX / XLSM / XLTM files. Supports reading and writing
|
||||
// spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports
|
||||
// complex components by high compatibility, and provided streaming API for
|
||||
// generating or reading data from a worksheet with huge amounts of data. This
|
||||
// library needs Go version 1.15 or later.
|
||||
|
||||
package excelize
|
||||
|
||||
|
|
|
@ -85,5 +85,5 @@ func TestExcelDateToTime(t *testing.T) {
|
|||
}
|
||||
// Check error case
|
||||
_, err := ExcelDateToTime(-1, false)
|
||||
assert.EqualError(t, err, "invalid date value -1.000000, negative values are not supported supported")
|
||||
assert.EqualError(t, err, "invalid date value -1.000000, negative values are not supported")
|
||||
}
|
||||
|
|
12
errors.go
12
errors.go
|
@ -16,26 +16,36 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
// newInvalidColumnNameError defined the error message on receiving the invalid column name.
|
||||
func newInvalidColumnNameError(col string) error {
|
||||
return fmt.Errorf("invalid column name %q", col)
|
||||
}
|
||||
|
||||
// newInvalidRowNumberError defined the error message on receiving the invalid row number.
|
||||
func newInvalidRowNumberError(row int) error {
|
||||
return fmt.Errorf("invalid row number %d", row)
|
||||
}
|
||||
|
||||
// newInvalidCellNameError defined the error message on receiving the invalid cell name.
|
||||
func newInvalidCellNameError(cell string) error {
|
||||
return fmt.Errorf("invalid cell name %q", cell)
|
||||
}
|
||||
|
||||
// newInvalidExcelDateError defined the error message on receiving the data with negative values.
|
||||
func newInvalidExcelDateError(dateValue float64) error {
|
||||
return fmt.Errorf("invalid date value %f, negative values are not supported supported", dateValue)
|
||||
return fmt.Errorf("invalid date value %f, negative values are not supported", dateValue)
|
||||
}
|
||||
|
||||
// newUnsupportChartType defined the error message on receiving the chart type are unsupported.
|
||||
func newUnsupportChartType(chartType string) error {
|
||||
return fmt.Errorf("unsupported chart type %s", chartType)
|
||||
}
|
||||
|
||||
// newUnzipSizeLimitError defined the error message on unzip size exceeds the limit.
|
||||
func newUnzipSizeLimitError(unzipSizeLimit int64) error {
|
||||
return fmt.Errorf("unzip size exceeds the %d bytes limit", unzipSizeLimit)
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrStreamSetColWidth defined the error message on set column width in
|
||||
// stream writing mode.
|
||||
|
|
|
@ -21,5 +21,5 @@ func TestNewInvalidCellNameError(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNewInvalidExcelDateError(t *testing.T) {
|
||||
assert.EqualError(t, newInvalidExcelDateError(-1), "invalid date value -1.000000, negative values are not supported supported")
|
||||
assert.EqualError(t, newInvalidExcelDateError(-1), "invalid date value -1.000000, negative values are not supported")
|
||||
}
|
||||
|
|
44
excelize.go
44
excelize.go
|
@ -21,6 +21,7 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -59,21 +60,27 @@ type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, e
|
|||
|
||||
// Options define the options for open spreadsheet.
|
||||
type Options struct {
|
||||
Password string
|
||||
Password string
|
||||
UnzipSizeLimit int64
|
||||
}
|
||||
|
||||
// OpenFile take the name of an spreadsheet file and returns a populated spreadsheet file struct
|
||||
// for it. For example, open spreadsheet with password protection:
|
||||
// OpenFile take the name of an spreadsheet file and returns a populated
|
||||
// spreadsheet file struct for it. For example, open spreadsheet with
|
||||
// password protection:
|
||||
//
|
||||
// f, err := excelize.OpenFile("Book1.xlsx", excelize.Options{Password: "password"})
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// Note that the excelize just support decrypt and not support encrypt currently, the spreadsheet
|
||||
// saved by Save and SaveAs will be without password unprotected.
|
||||
// Note that the excelize just support decrypt and not support encrypt
|
||||
// currently, the spreadsheet saved by Save and SaveAs will be without
|
||||
// password unprotected.
|
||||
//
|
||||
// UnzipSizeLimit specified the unzip size limit in bytes on open the
|
||||
// spreadsheet, the default size limit is 16GB.
|
||||
func OpenFile(filename string, opt ...Options) (*File, error) {
|
||||
file, err := os.Open(filename)
|
||||
file, err := os.Open(filepath.Clean(filename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -89,6 +96,7 @@ func OpenFile(filename string, opt ...Options) (*File, error) {
|
|||
// newFile is object builder
|
||||
func newFile() *File {
|
||||
return &File{
|
||||
options: &Options{UnzipSizeLimit: UnzipSizeLimit},
|
||||
xmlAttr: make(map[string][]xml.Attr),
|
||||
checked: make(map[string]bool),
|
||||
sheetMap: make(map[string]string),
|
||||
|
@ -111,10 +119,13 @@ func OpenReader(r io.Reader, opt ...Options) (*File, error) {
|
|||
return nil, err
|
||||
}
|
||||
f := newFile()
|
||||
if bytes.Contains(b, oleIdentifier) && len(opt) > 0 {
|
||||
for _, o := range opt {
|
||||
f.options = &o
|
||||
for i := range opt {
|
||||
f.options = &opt[i]
|
||||
if f.options.UnzipSizeLimit == 0 {
|
||||
f.options.UnzipSizeLimit = UnzipSizeLimit
|
||||
}
|
||||
}
|
||||
if bytes.Contains(b, oleIdentifier) {
|
||||
b, err = Decrypt(b, f.options)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decrypted file failed")
|
||||
|
@ -124,8 +135,7 @@ func OpenReader(r io.Reader, opt ...Options) (*File, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file, sheetCount, err := ReadZipReader(zr)
|
||||
file, sheetCount, err := ReadZipReader(zr, f.options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -316,18 +326,18 @@ func (f *File) UpdateLinkedValue() error {
|
|||
// recalculate formulas
|
||||
wb.CalcPr = nil
|
||||
for _, name := range f.GetSheetList() {
|
||||
xlsx, err := f.workSheetReader(name)
|
||||
ws, err := f.workSheetReader(name)
|
||||
if err != nil {
|
||||
if err.Error() == fmt.Sprintf("sheet %s is chart sheet", trimSheetName(name)) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
for indexR := range xlsx.SheetData.Row {
|
||||
for indexC, col := range xlsx.SheetData.Row[indexR].C {
|
||||
for indexR := range ws.SheetData.Row {
|
||||
for indexC, col := range ws.SheetData.Row[indexR].C {
|
||||
if col.F != nil && col.V != "" {
|
||||
xlsx.SheetData.Row[indexR].C[indexC].V = ""
|
||||
xlsx.SheetData.Row[indexR].C[indexC].T = ""
|
||||
ws.SheetData.Row[indexR].C[indexC].V = ""
|
||||
ws.SheetData.Row[indexR].C[indexC].T = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -381,7 +391,7 @@ func (f *File) AddVBAProject(bin string) error {
|
|||
Type: SourceRelationshipVBAProject,
|
||||
})
|
||||
}
|
||||
file, _ := ioutil.ReadFile(bin)
|
||||
file, _ := ioutil.ReadFile(filepath.Clean(bin))
|
||||
f.Pkg.Store("xl/vbaProject.bin", file)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -184,13 +184,9 @@ func TestSaveFile(t *testing.T) {
|
|||
|
||||
func TestSaveAsWrongPath(t *testing.T) {
|
||||
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
||||
if assert.NoError(t, err) {
|
||||
// Test write file to not exist directory.
|
||||
err = f.SaveAs("")
|
||||
if assert.Error(t, err) {
|
||||
assert.True(t, os.IsNotExist(err), "Error: %v: Expected os.IsNotExists(err) == true", err)
|
||||
}
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
// Test write file to not exist directory.
|
||||
assert.EqualError(t, f.SaveAs(""), "open .: is a directory")
|
||||
}
|
||||
|
||||
func TestCharsetTranscoder(t *testing.T) {
|
||||
|
@ -204,6 +200,10 @@ func TestOpenReader(t *testing.T) {
|
|||
_, err = OpenReader(bytes.NewReader(oleIdentifier), Options{Password: "password"})
|
||||
assert.EqualError(t, err, "decrypted file failed")
|
||||
|
||||
// Test open spreadsheet with unzip size limit.
|
||||
_, err = OpenFile(filepath.Join("test", "Book1.xlsx"), Options{UnzipSizeLimit: 100})
|
||||
assert.EqualError(t, err, newUnzipSizeLimitError(100).Error())
|
||||
|
||||
// Test open password protected spreadsheet created by Microsoft Office Excel 2010.
|
||||
f, err := OpenFile(filepath.Join("test", "encryptSHA1.xlsx"), Options{Password: "password"})
|
||||
assert.NoError(t, err)
|
||||
|
@ -1226,6 +1226,7 @@ func TestWorkSheetReader(t *testing.T) {
|
|||
f.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset)
|
||||
_, err := f.workSheetReader("Sheet1")
|
||||
assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8")
|
||||
assert.EqualError(t, f.UpdateLinkedValue(), "xml decode error: XML syntax error on line 1: invalid UTF-8")
|
||||
|
||||
// Test on no checked worksheet.
|
||||
f = NewFile()
|
||||
|
|
16
file.go
16
file.go
|
@ -17,6 +17,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
@ -69,14 +70,14 @@ func (f *File) SaveAs(name string, opt ...Options) error {
|
|||
return ErrMaxFileNameLength
|
||||
}
|
||||
f.Path = name
|
||||
file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
|
||||
file, err := os.OpenFile(filepath.Clean(name), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
f.options = nil
|
||||
for _, o := range opt {
|
||||
f.options = &o
|
||||
for i := range opt {
|
||||
f.options = &opt[i]
|
||||
}
|
||||
return f.Write(file)
|
||||
}
|
||||
|
@ -102,7 +103,8 @@ func (f *File) WriteTo(w io.Writer) (int64, error) {
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
// WriteToBuffer provides a function to get bytes.Buffer from the saved file. And it allocate space in memory. Be careful when the file size is large.
|
||||
// WriteToBuffer provides a function to get bytes.Buffer from the saved file,
|
||||
// and it allocates space in memory. Be careful when the file size is large.
|
||||
func (f *File) WriteToBuffer() (*bytes.Buffer, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
zw := zip.NewWriter(buf)
|
||||
|
@ -130,7 +132,7 @@ func (f *File) WriteToBuffer() (*bytes.Buffer, error) {
|
|||
func (f *File) writeDirectToWriter(w io.Writer) error {
|
||||
zw := zip.NewWriter(w)
|
||||
if err := f.writeToZip(zw); err != nil {
|
||||
zw.Close()
|
||||
_ = zw.Close()
|
||||
return err
|
||||
}
|
||||
return zw.Close()
|
||||
|
@ -157,14 +159,14 @@ func (f *File) writeToZip(zw *zip.Writer) error {
|
|||
var from io.Reader
|
||||
from, err = stream.rawData.Reader()
|
||||
if err != nil {
|
||||
stream.rawData.Close()
|
||||
_ = stream.rawData.Close()
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(fi, from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stream.rawData.Close()
|
||||
_ = stream.rawData.Close()
|
||||
}
|
||||
var err error
|
||||
f.Pkg.Range(func(path, content interface{}) bool {
|
||||
|
|
12
file_test.go
12
file_test.go
|
@ -37,9 +37,7 @@ func BenchmarkWrite(b *testing.B) {
|
|||
func TestWriteTo(t *testing.T) {
|
||||
// Test WriteToBuffer err
|
||||
{
|
||||
f := File{}
|
||||
buf := bytes.Buffer{}
|
||||
f.Pkg = sync.Map{}
|
||||
f, buf := File{Pkg: sync.Map{}}, bytes.Buffer{}
|
||||
f.Pkg.Store("/d/", []byte("s"))
|
||||
_, err := f.WriteTo(bufio.NewWriter(&buf))
|
||||
assert.EqualError(t, err, "zip: write to directory")
|
||||
|
@ -47,9 +45,7 @@ func TestWriteTo(t *testing.T) {
|
|||
}
|
||||
// Test file path overflow
|
||||
{
|
||||
f := File{}
|
||||
buf := bytes.Buffer{}
|
||||
f.Pkg = sync.Map{}
|
||||
f, buf := File{Pkg: sync.Map{}}, bytes.Buffer{}
|
||||
const maxUint16 = 1<<16 - 1
|
||||
f.Pkg.Store(strings.Repeat("s", maxUint16+1), nil)
|
||||
_, err := f.WriteTo(bufio.NewWriter(&buf))
|
||||
|
@ -57,9 +53,7 @@ func TestWriteTo(t *testing.T) {
|
|||
}
|
||||
// Test StreamsWriter err
|
||||
{
|
||||
f := File{}
|
||||
buf := bytes.Buffer{}
|
||||
f.Pkg = sync.Map{}
|
||||
f, buf := File{Pkg: sync.Map{}}, bytes.Buffer{}
|
||||
f.Pkg.Store("s", nil)
|
||||
f.streams = make(map[string]*StreamWriter)
|
||||
file, _ := os.Open("123")
|
||||
|
|
28
lib.go
28
lib.go
|
@ -26,15 +26,22 @@ import (
|
|||
|
||||
// ReadZipReader can be used to read the spreadsheet in memory without touching the
|
||||
// filesystem.
|
||||
func ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
|
||||
var err error
|
||||
var docPart = map[string]string{
|
||||
"[content_types].xml": "[Content_Types].xml",
|
||||
"xl/sharedstrings.xml": "xl/sharedStrings.xml",
|
||||
}
|
||||
fileList := make(map[string][]byte, len(r.File))
|
||||
worksheets := 0
|
||||
func ReadZipReader(r *zip.Reader, o *Options) (map[string][]byte, int, error) {
|
||||
var (
|
||||
err error
|
||||
docPart = map[string]string{
|
||||
"[content_types].xml": "[Content_Types].xml",
|
||||
"xl/sharedstrings.xml": "xl/sharedStrings.xml",
|
||||
}
|
||||
fileList = make(map[string][]byte, len(r.File))
|
||||
worksheets int
|
||||
unzipSize int64
|
||||
)
|
||||
for _, v := range r.File {
|
||||
unzipSize += v.FileInfo().Size()
|
||||
if unzipSize > o.UnzipSizeLimit {
|
||||
return fileList, worksheets, newUnzipSizeLimitError(o.UnzipSizeLimit)
|
||||
}
|
||||
fileName := strings.Replace(v.Name, "\\", "/", -1)
|
||||
if partName, ok := docPart[strings.ToLower(fileName)]; ok {
|
||||
fileName = partName
|
||||
|
@ -61,7 +68,7 @@ func (f *File) readXML(name string) []byte {
|
|||
}
|
||||
|
||||
// saveFileList provides a function to update given file content in file list
|
||||
// of XLSX.
|
||||
// of spreadsheet.
|
||||
func (f *File) saveFileList(name string, content []byte) {
|
||||
f.Pkg.Store(name, append([]byte(XMLHeader), content...))
|
||||
}
|
||||
|
@ -75,8 +82,7 @@ func readFile(file *zip.File) ([]byte, error) {
|
|||
dat := make([]byte, 0, file.FileInfo().Size())
|
||||
buff := bytes.NewBuffer(dat)
|
||||
_, _ = io.Copy(buff, rc)
|
||||
rc.Close()
|
||||
return buff.Bytes(), nil
|
||||
return buff.Bytes(), rc.Close()
|
||||
}
|
||||
|
||||
// SplitCellName splits cell name to column name and row number.
|
||||
|
|
|
@ -148,16 +148,16 @@ func TestUnmergeCell(t *testing.T) {
|
|||
}
|
||||
sheet1 := f.GetSheetName(0)
|
||||
|
||||
xlsx, err := f.workSheetReader(sheet1)
|
||||
sheet, err := f.workSheetReader(sheet1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
mergeCellNum := len(xlsx.MergeCells.Cells)
|
||||
mergeCellNum := len(sheet.MergeCells.Cells)
|
||||
|
||||
assert.EqualError(t, f.UnmergeCell("Sheet1", "A", "A"), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
|
||||
|
||||
// unmerge the mergecell that contains A1
|
||||
assert.NoError(t, f.UnmergeCell(sheet1, "A1", "A1"))
|
||||
if len(xlsx.MergeCells.Cells) != mergeCellNum-1 {
|
||||
if len(sheet.MergeCells.Cells) != mergeCellNum-1 {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
|
|
14
picture.go
14
picture.go
|
@ -94,7 +94,7 @@ func (f *File) AddPicture(sheet, cell, picture, format string) error {
|
|||
if !ok {
|
||||
return ErrImgExt
|
||||
}
|
||||
file, _ := ioutil.ReadFile(picture)
|
||||
file, _ := ioutil.ReadFile(filepath.Clean(picture))
|
||||
_, name := filepath.Split(picture)
|
||||
return f.AddPictureFromBytes(sheet, cell, format, name, ext, file)
|
||||
}
|
||||
|
@ -199,8 +199,8 @@ func (f *File) deleteSheetRelationships(sheet, rID string) {
|
|||
// addSheetLegacyDrawing provides a function to add legacy drawing element to
|
||||
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
|
||||
func (f *File) addSheetLegacyDrawing(sheet string, rID int) {
|
||||
xlsx, _ := f.workSheetReader(sheet)
|
||||
xlsx.LegacyDrawing = &xlsxLegacyDrawing{
|
||||
ws, _ := f.workSheetReader(sheet)
|
||||
ws.LegacyDrawing = &xlsxLegacyDrawing{
|
||||
RID: "rId" + strconv.Itoa(rID),
|
||||
}
|
||||
}
|
||||
|
@ -208,8 +208,8 @@ func (f *File) addSheetLegacyDrawing(sheet string, rID int) {
|
|||
// addSheetDrawing provides a function to add drawing element to
|
||||
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
|
||||
func (f *File) addSheetDrawing(sheet string, rID int) {
|
||||
xlsx, _ := f.workSheetReader(sheet)
|
||||
xlsx.Drawing = &xlsxDrawing{
|
||||
ws, _ := f.workSheetReader(sheet)
|
||||
ws.Drawing = &xlsxDrawing{
|
||||
RID: "rId" + strconv.Itoa(rID),
|
||||
}
|
||||
}
|
||||
|
@ -217,8 +217,8 @@ func (f *File) addSheetDrawing(sheet string, rID int) {
|
|||
// addSheetPicture provides a function to add picture element to
|
||||
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
|
||||
func (f *File) addSheetPicture(sheet string, rID int) {
|
||||
xlsx, _ := f.workSheetReader(sheet)
|
||||
xlsx.Picture = &xlsxPicture{
|
||||
ws, _ := f.workSheetReader(sheet)
|
||||
ws.Picture = &xlsxPicture{
|
||||
RID: "rId" + strconv.Itoa(rID),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,24 +71,24 @@ func TestAddPicture(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAddPictureErrors(t *testing.T) {
|
||||
xlsx, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
||||
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test add picture to worksheet with invalid file path.
|
||||
err = xlsx.AddPicture("Sheet1", "G21", filepath.Join("test", "not_exists_dir", "not_exists.icon"), "")
|
||||
err = f.AddPicture("Sheet1", "G21", filepath.Join("test", "not_exists_dir", "not_exists.icon"), "")
|
||||
if assert.Error(t, err) {
|
||||
assert.True(t, os.IsNotExist(err), "Expected os.IsNotExist(err) == true")
|
||||
}
|
||||
|
||||
// Test add picture to worksheet with unsupported file type.
|
||||
err = xlsx.AddPicture("Sheet1", "G21", filepath.Join("test", "Book1.xlsx"), "")
|
||||
err = f.AddPicture("Sheet1", "G21", filepath.Join("test", "Book1.xlsx"), "")
|
||||
assert.EqualError(t, err, ErrImgExt.Error())
|
||||
|
||||
err = xlsx.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", "jpg", make([]byte, 1))
|
||||
err = f.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", "jpg", make([]byte, 1))
|
||||
assert.EqualError(t, err, ErrImgExt.Error())
|
||||
|
||||
// Test add picture to worksheet with invalid file data.
|
||||
err = xlsx.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", ".jpg", make([]byte, 1))
|
||||
err = f.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", ".jpg", make([]byte, 1))
|
||||
assert.EqualError(t, err, "image: unknown format")
|
||||
}
|
||||
|
||||
|
|
8
sheet.go
8
sheet.go
|
@ -480,7 +480,7 @@ func (f *File) SetSheetBackground(sheet, picture string) error {
|
|||
if !ok {
|
||||
return ErrImgExt
|
||||
}
|
||||
file, _ := ioutil.ReadFile(picture)
|
||||
file, _ := ioutil.ReadFile(filepath.Clean(picture))
|
||||
name := f.addMedia(file, ext)
|
||||
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
|
||||
rID := f.addRels(sheetRels, SourceRelationshipImage, strings.Replace(name, "xl", "..", 1), "")
|
||||
|
@ -655,13 +655,13 @@ func (f *File) SetSheetVisible(name string, visible bool) error {
|
|||
}
|
||||
}
|
||||
for k, v := range content.Sheets.Sheet {
|
||||
xlsx, err := f.workSheetReader(v.Name)
|
||||
ws, err := f.workSheetReader(v.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tabSelected := false
|
||||
if len(xlsx.SheetViews.SheetView) > 0 {
|
||||
tabSelected = xlsx.SheetViews.SheetView[0].TabSelected
|
||||
if len(ws.SheetViews.SheetView) > 0 {
|
||||
tabSelected = ws.SheetViews.SheetView[0].TabSelected
|
||||
}
|
||||
if v.Name == name && count > 1 && !tabSelected {
|
||||
content.Sheets.Sheet[k].State = "hidden"
|
||||
|
|
10
sheetpr.go
10
sheetpr.go
|
@ -182,14 +182,14 @@ func (o *AutoPageBreaks) getSheetPrOption(pr *xlsxSheetPr) {
|
|||
// AutoPageBreaks(bool)
|
||||
// OutlineSummaryBelow(bool)
|
||||
func (f *File) SetSheetPrOptions(name string, opts ...SheetPrOption) error {
|
||||
sheet, err := f.workSheetReader(name)
|
||||
ws, err := f.workSheetReader(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pr := sheet.SheetPr
|
||||
pr := ws.SheetPr
|
||||
if pr == nil {
|
||||
pr = new(xlsxSheetPr)
|
||||
sheet.SheetPr = pr
|
||||
ws.SheetPr = pr
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
|
@ -208,11 +208,11 @@ func (f *File) SetSheetPrOptions(name string, opts ...SheetPrOption) error {
|
|||
// AutoPageBreaks(bool)
|
||||
// OutlineSummaryBelow(bool)
|
||||
func (f *File) GetSheetPrOptions(name string, opts ...SheetPrOptionPtr) error {
|
||||
sheet, err := f.workSheetReader(name)
|
||||
ws, err := f.workSheetReader(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pr := sheet.SheetPr
|
||||
pr := ws.SheetPr
|
||||
|
||||
for _, opt := range opts {
|
||||
opt.getSheetPrOption(pr)
|
||||
|
|
|
@ -346,8 +346,8 @@ func (sw *StreamWriter) SetRow(axis string, values []interface{}, opts ...RowOpt
|
|||
// marshalRowAttrs prepare attributes of the row by given options.
|
||||
func marshalRowAttrs(opts ...RowOpts) (attrs string, err error) {
|
||||
var opt *RowOpts
|
||||
for _, o := range opts {
|
||||
opt = &o
|
||||
for i := range opts {
|
||||
opt = &opts[i]
|
||||
}
|
||||
if opt == nil {
|
||||
return
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
// struct code generated by github.com/xuri/xgen
|
||||
//
|
||||
// Package excelize providing a set of functions that allow you to write to
|
||||
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||
// charts of XLSX. This library needs Go version 1.15 or later.
|
||||
// and read from XLSX / XLSM / XLTM files. Supports reading and writing
|
||||
// spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports
|
||||
// complex components by high compatibility, and provided streaming API for
|
||||
// generating or reading data from a worksheet with huge amounts of data. This
|
||||
// library needs Go version 1.15 or later.
|
||||
|
||||
package excelize
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ const (
|
|||
|
||||
// Excel specifications and limits
|
||||
const (
|
||||
UnzipSizeLimit = 1000 << 24
|
||||
StreamChunkSize = 1 << 24
|
||||
MaxFontFamilyLength = 31
|
||||
MaxFontSize = 409
|
||||
|
|
Loading…
Reference in New Issue