已验证 提交 48c16de8 编写于 作者: xurime's avatar xurime

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
上级 f6f14f50
......@@ -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
......
......@@ -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")
}
......@@ -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")
}
......@@ -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()
......
......@@ -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 {
......
......@@ -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")
......
......@@ -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()
}
......
......@@ -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")
}
......
......@@ -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"
......
......@@ -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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册