38 changes: 27 additions & 11 deletions file.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

Expand All @@ -29,8 +29,8 @@ import (
func NewFile() *File {
f := newFile()
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(defaultXMLPathDocPropsApp, []byte(xml.Header+templateDocpropsApp))
f.Pkg.Store(defaultXMLPathDocPropsCore, []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))
Expand Down Expand Up @@ -63,14 +63,25 @@ func (f *File) Save() error {
return f.SaveAs(f.Path)
}

// SaveAs provides a function to create or update to an spreadsheet at the
// SaveAs provides a function to create or update to a spreadsheet at the
// provided path.
func (f *File) SaveAs(name string, opt ...Options) error {
if len(name) > MaxFileNameLength {
return ErrMaxFileNameLength
}
f.Path = name
file, err := os.OpenFile(filepath.Clean(name), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)
contentType, ok := map[string]string{
".xlam": ContentTypeAddinMacro,
".xlsm": ContentTypeMacro,
".xlsx": ContentTypeSheetML,
".xltm": ContentTypeTemplateMacro,
".xltx": ContentTypeTemplate,
}[filepath.Ext(f.Path)]
if !ok {
return ErrWorkbookExt
}
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
}
Expand All @@ -85,6 +96,11 @@ func (f *File) SaveAs(name string, opt ...Options) error {
// Close closes and cleanup the open temporary file for the spreadsheet.
func (f *File) Close() error {
var err error
if f.sharedStringTemp != nil {
if err := f.sharedStringTemp.Close(); err != nil {
return err
}
}
f.tempFiles.Range(func(k, v interface{}) bool {
if err = os.Remove(v.(string)); err != nil {
return false
Expand Down
15 changes: 8 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ go 1.15

require (
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/richardlehane/mscfb v1.0.3
github.com/stretchr/testify v1.6.1
github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985
golang.org/x/text v0.3.6
github.com/richardlehane/mscfb v1.0.4
github.com/stretchr/testify v1.7.0
github.com/xuri/efp v0.0.0-20220407160117-ad0f7a785be8
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3
golang.org/x/text v0.3.7
)
35 changes: 19 additions & 16 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,33 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/richardlehane/mscfb v1.0.3 h1:rD8TBkYWkObWO0oLDFCbwMeZ4KoalxQy+QgniCj3nKI=
github.com/richardlehane/mscfb v1.0.3/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
github.com/richardlehane/msoleps v1.0.1 h1:RfrALnSNXzmXLbGct/P2b4xkFz4e8Gmj/0Vj9M9xC1o=
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3 h1:EpI0bqf/eX9SdZDwlMmahKM+CDBgNbsXMhsN28XrM8o=
github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk=
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/xuri/efp v0.0.0-20220407160117-ad0f7a785be8 h1:3X7aE0iLKJ5j+tz58BpvIZkXNV7Yq4jC93Z/rbN2Fxk=
github.com/xuri/efp v0.0.0-20220407160117-ad0f7a785be8/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 h1:OAmKAfT06//esDdpi/DZ8Qsdt4+M5+ltca05dA5bG2M=
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921 h1:iU7T1X1J6yxDr0rda54sWGkHgOp5XJrqm79gcNlC2VM=
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 h1:EN5+DfgmRMvRUrMGERW2gQl3Vc+Z7ZMnI/xdEpPSf0c=
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
123 changes: 81 additions & 42 deletions lib.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

Expand All @@ -31,7 +31,7 @@ func (f *File) ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
err error
docPart = map[string]string{
"[content_types].xml": defaultXMLPathContentTypes,
"xl/sharedstrings.xml": dafaultXMLPathSharedStrings,
"xl/sharedstrings.xml": defaultXMLPathSharedStrings,
}
fileList = make(map[string][]byte, len(r.File))
worksheets int
Expand All @@ -47,7 +47,7 @@ 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 strings.EqualFold(fileName, defaultXMLPathSharedStrings) && fileSize > f.options.UnzipXMLSizeLimit {
if tempFile, err := f.unzipToTemp(v); err == nil {
f.tempFiles.Store(fileName, tempFile)
continue
Expand Down Expand Up @@ -80,10 +80,13 @@ func (f *File) unzipToTemp(zipFile *zip.File) (string, error) {
if err != nil {
return tmp.Name(), err
}
_, err = io.Copy(tmp, rc)
rc.Close()
tmp.Close()
return tmp.Name(), err
if _, err = io.Copy(tmp, rc); err != nil {
return tmp.Name(), err
}
if err = rc.Close(); err != nil {
return tmp.Name(), err
}
return tmp.Name(), tmp.Close()
}

// readXML provides a function to read XML content as bytes.
Expand All @@ -109,7 +112,7 @@ func (f *File) readBytes(name string) []byte {
}
content, _ = ioutil.ReadAll(file)
f.Pkg.Store(name, content)
file.Close()
_ = file.Close()
return content
}

Expand All @@ -129,7 +132,7 @@ func (f *File) saveFileList(name string, content []byte) {
f.Pkg.Store(name, append([]byte(xml.Header), content...))
}

// Read file content as string in a archive file.
// Read file content as string in an archive file.
func readFile(file *zip.File) ([]byte, error) {
rc, err := file.Open()
if err != nil {
Expand All @@ -154,8 +157,8 @@ func SplitCellName(cell string) (string, int, error) {
if strings.IndexFunc(cell, alpha) == 0 {
i := strings.LastIndexFunc(cell, alpha)
if i >= 0 && i < len(cell)-1 {
col, rowstr := strings.ReplaceAll(cell[:i+1], "$", ""), cell[i+1:]
if row, err := strconv.Atoi(rowstr); err == nil && row > 0 {
col, rowStr := strings.ReplaceAll(cell[:i+1], "$", ""), cell[i+1:]
if row, err := strconv.Atoi(rowStr); err == nil && row > 0 {
return col, row, nil
}
}
Expand Down Expand Up @@ -184,7 +187,7 @@ func JoinCellName(col string, row int) (string, error) {
}

// ColumnNameToNumber provides a function to convert Excel sheet column name
// to int. Column name case insensitive. The function returns an error if
// to int. Column name case-insensitive. The function returns an error if
// column name incorrect.
//
// Example:
Expand Down Expand Up @@ -245,14 +248,14 @@ func ColumnNumberToName(num int) (string, error) {
// excelize.CellNameToCoordinates("Z3") // returns 26, 3, nil
//
func CellNameToCoordinates(cell string) (int, int, error) {
colname, row, err := SplitCellName(cell)
colName, row, err := SplitCellName(cell)
if err != nil {
return -1, -1, newCellNameToCoordinatesError(cell, err)
}
if row > TotalRows {
return -1, -1, ErrMaxRows
}
col, err := ColumnNameToNumber(colname)
col, err := ColumnNameToNumber(colName)
return col, row, err
}

Expand All @@ -274,8 +277,8 @@ func CoordinatesToCellName(col, row int, abs ...bool) (string, error) {
sign = "$"
}
}
colname, err := ColumnNumberToName(col)
return sign + colname + sign + strconv.Itoa(row), err
colName, err := ColumnNumberToName(col)
return sign + colName + sign + strconv.Itoa(row), err
}

// areaRefToCoordinates provides a function to convert area reference to a
Expand Down Expand Up @@ -333,6 +336,27 @@ func (f *File) coordinatesToAreaRef(coordinates []int) (string, error) {
return firstCell + ":" + lastCell, err
}

// getDefinedNameRefTo convert defined name to reference range.
func (f *File) getDefinedNameRefTo(definedNameName string, currentSheet string) (refTo string) {
var workbookRefTo, worksheetRefTo string
for _, definedName := range f.GetDefinedName() {
if definedName.Name == definedNameName {
// worksheet scope takes precedence over scope workbook when both definedNames exist
if definedName.Scope == "Workbook" {
workbookRefTo = definedName.RefersTo
}
if definedName.Scope == currentSheet {
worksheetRefTo = definedName.RefersTo
}
}
}
refTo = workbookRefTo
if worksheetRefTo != "" {
refTo = worksheetRefTo
}
return
}

// flatSqref convert reference sequence to cell coordinates list.
func (f *File) flatSqref(sqref string) (cells map[int][][]int, err error) {
var coordinates []int
Expand Down Expand Up @@ -362,7 +386,7 @@ func (f *File) flatSqref(sqref string) (cells map[int][][]int, err error) {
return
}

// inCoordinates provides a method to check if an coordinate is present in
// inCoordinates provides a method to check if a coordinate is present in
// coordinates array, and return the index of its location, otherwise
// return -1.
func inCoordinates(a [][]int, x []int) int {
Expand All @@ -376,16 +400,19 @@ func inCoordinates(a [][]int, x []int) int {

// inStrSlice provides a method to check if an element is present in an array,
// and return the index of its location, otherwise return -1.
func inStrSlice(a []string, x string) int {
func inStrSlice(a []string, x string, caseSensitive bool) int {
for idx, n := range a {
if !caseSensitive && strings.EqualFold(x, n) {
return idx
}
if x == n {
return idx
}
}
return -1
}

// inFloat64Slice provides a method to check if an element is present in an
// inFloat64Slice provides a method to check if an element is present in a
// float64 array, and return the index of its location, otherwise return -1.
func inFloat64Slice(a []float64, x float64) int {
for idx, n := range a {
Expand All @@ -399,7 +426,7 @@ func inFloat64Slice(a []float64, x float64) int {
// boolPtr returns a pointer to a bool with the given value.
func boolPtr(b bool) *bool { return &b }

// intPtr returns a pointer to a int with the given value.
// intPtr returns a pointer to an int with the given value.
func intPtr(i int) *int { return &i }

// float64Ptr returns a pointer to a float64 with the given value.
Expand Down Expand Up @@ -434,9 +461,10 @@ func (avb attrValBool) MarshalXML(e *xml.Encoder, start xml.StartElement) error
}
}
start.Attr = []xml.Attr{attr}
e.EncodeToken(start)
e.EncodeToken(start.End())
return nil
if err := e.EncodeToken(start); err != nil {
return err
}
return e.EncodeToken(start.End())
}

// UnmarshalXML convert the literal values true, false, 1, 0 of the XML
Expand Down Expand Up @@ -473,6 +501,8 @@ func (avb *attrValBool) UnmarshalXML(d *xml.Decoder, start xml.StartElement) err
return nil
}
}
defaultVal := true
avb.Val = &defaultVal
return nil
}

Expand All @@ -488,7 +518,7 @@ func parseFormatSet(formatSet string) []byte {
// namespaceStrictToTransitional provides a method to convert Strict and
// Transitional namespaces.
func namespaceStrictToTransitional(content []byte) []byte {
var namespaceTranslationDic = map[string]string{
namespaceTranslationDic := map[string]string{
StrictSourceRelationship: SourceRelationship.Value,
StrictSourceRelationshipOfficeDocument: SourceRelationshipOfficeDocument,
StrictSourceRelationshipChart: SourceRelationshipChart,
Expand Down Expand Up @@ -553,7 +583,7 @@ func genSheetPasswd(plaintext string) string {
charPos++
rotatedBits := value >> 15 // rotated bits beyond bit 15
value &= 0x7fff // first 15 bits
password ^= (value | rotatedBits)
password ^= value | rotatedBits
}
password ^= int64(len(plaintext))
password ^= 0xCE4B
Expand Down Expand Up @@ -609,15 +639,15 @@ func getXMLNamespace(space string, attr []xml.Attr) string {
// replaceNameSpaceBytes provides a function to replace the XML root element
// attribute by the given component part path and XML content.
func (f *File) replaceNameSpaceBytes(path string, contentMarshal []byte) []byte {
var oldXmlns = []byte(`xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
var newXmlns = []byte(templateNamespaceIDMap)
oldXmlns := []byte(`xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
newXmlns := []byte(templateNamespaceIDMap)
if attr, ok := f.xmlAttr[path]; ok {
newXmlns = []byte(genXMLNamespace(attr))
}
return bytesReplace(contentMarshal, oldXmlns, newXmlns, -1)
}

// addNameSpaces provides a function to add a XML attribute by the given
// addNameSpaces provides a function to add an XML attribute by the given
// component part path.
func (f *File) addNameSpaces(path string, ns xml.Attr) {
exist := false
Expand Down Expand Up @@ -656,7 +686,7 @@ func (f *File) addNameSpaces(path string, ns xml.Attr) {
// by the given attribute.
func (f *File) setIgnorableNameSpace(path string, index int, ns xml.Attr) {
ignorableNS := []string{"c14", "cdr14", "a14", "pic14", "x14", "xdr14", "x14ac", "dsp", "mso14", "dgm14", "x15", "x12ac", "x15ac", "xr", "xr2", "xr3", "xr4", "xr5", "xr6", "xr7", "xr8", "xr9", "xr10", "xr11", "xr12", "xr13", "xr14", "xr15", "x15", "x16", "x16r2", "mo", "mx", "mv", "o", "v"}
if inStrSlice(strings.Fields(f.xmlAttr[path][index].Value), ns.Name.Local) == -1 && inStrSlice(ignorableNS, ns.Name.Local) != -1 {
if inStrSlice(strings.Fields(f.xmlAttr[path][index].Value), ns.Name.Local, true) == -1 && inStrSlice(ignorableNS, ns.Name.Local, true) != -1 {
f.xmlAttr[path][index].Value = strings.TrimSpace(fmt.Sprintf("%s %s", f.xmlAttr[path][index].Value, ns.Name.Local))
}
}
Expand All @@ -670,24 +700,33 @@ func (f *File) addSheetNameSpace(sheet string, ns xml.Attr) {
// isNumeric determines whether an expression is a valid numeric type and get
// the precision for the numeric.
func isNumeric(s string) (bool, int) {
dot := false
p := 0
dot, e, n, p := false, false, false, 0
for i, v := range s {
if v == '.' {
if dot {
return false, 0
}
dot = true
} else if v == 'E' || v == 'e' {
e = true
} else if v < '0' || v > '9' {
if i == 0 && v == '-' {
continue
}
if e && v == '-' {
return true, 0
}
if e && v == '+' {
p = 15
continue
}
return false, 0
} else if dot {
} else {
p++
}
n = true
}
return true, p
return n, p
}

var (
Expand All @@ -697,7 +736,7 @@ var (

// bstrUnmarshal parses the binary basic string, this will trim escaped string
// literal which not permitted in an XML 1.0 document. The basic string
// variant type can store any valid Unicode character. Unicode characters
// variant type can store any valid Unicode character. Unicode's characters
// that cannot be directly represented in XML as defined by the XML 1.0
// specification, shall be escaped using the Unicode numerical character
// representation escape character format _xHHHH_, where H represents a
Expand Down Expand Up @@ -779,8 +818,8 @@ type Stack struct {

// NewStack create a new stack.
func NewStack() *Stack {
list := list.New()
return &Stack{list}
l := list.New()
return &Stack{l}
}

// Push a value onto the top of the stack.
Expand Down
7 changes: 3 additions & 4 deletions lib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ func TestJoinCellName_Error(t *testing.T) {
test(col.Name, row)
}
}

}

func TestCellNameToCoordinates_OK(t *testing.T) {
Expand Down Expand Up @@ -235,7 +234,7 @@ func TestSortCoordinates(t *testing.T) {
}

func TestInStrSlice(t *testing.T) {
assert.EqualValues(t, -1, inStrSlice([]string{}, ""))
assert.EqualValues(t, -1, inStrSlice([]string{}, "", true))
}

func TestBoolValMarshal(t *testing.T) {
Expand Down Expand Up @@ -343,7 +342,7 @@ func TestReadBytes(t *testing.T) {
func TestUnzipToTemp(t *testing.T) {
os.Setenv("TMPDIR", "test")
defer os.Unsetenv("TMPDIR")
assert.NoError(t, os.Chmod(os.TempDir(), 0444))
assert.NoError(t, os.Chmod(os.TempDir(), 0o444))
f := NewFile()
data := []byte("PK\x03\x040000000PK\x01\x0200000" +
"0000000000000000000\x00" +
Expand All @@ -365,7 +364,7 @@ func TestUnzipToTemp(t *testing.T) {

_, err = f.unzipToTemp(z.File[0])
require.Error(t, err)
assert.NoError(t, os.Chmod(os.TempDir(), 0755))
assert.NoError(t, os.Chmod(os.TempDir(), 0o755))

_, err = f.unzipToTemp(z.File[0])
assert.EqualError(t, err, "EOF")
Expand Down
41 changes: 22 additions & 19 deletions merge.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

import "strings"
import (
"strings"
)

// Rect gets merged cell rectangle coordinates sequence.
func (mc *xlsxMergeCell) Rect() ([]int, error) {
Expand Down Expand Up @@ -46,29 +48,30 @@ func (mc *xlsxMergeCell) Rect() ([]int, error) {
// |A8(x3,y4) C8(x4,y4)|
// +------------------------+
//
func (f *File) MergeCell(sheet, hcell, vcell string) error {
rect, err := areaRefToCoordinates(hcell + ":" + vcell)
func (f *File) MergeCell(sheet, hCell, vCell string) error {
rect, err := areaRefToCoordinates(hCell + ":" + vCell)
if err != nil {
return err
}
// Correct the coordinate area, such correct C1:B3 to B1:C3.
_ = sortCoordinates(rect)

hcell, _ = CoordinatesToCellName(rect[0], rect[1])
vcell, _ = CoordinatesToCellName(rect[2], rect[3])
hCell, _ = CoordinatesToCellName(rect[0], rect[1])
vCell, _ = CoordinatesToCellName(rect[2], rect[3])

ws, err := f.workSheetReader(sheet)
if err != nil {
return err
}
ref := hcell + ":" + vcell
ref := hCell + ":" + vCell
if ws.MergeCells != nil {
ws.MergeCells.Cells = append(ws.MergeCells.Cells, &xlsxMergeCell{Ref: ref, rect: rect})
} else {
ws.MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: ref, rect: rect}}}
}
ws.MergeCells.Count = len(ws.MergeCells.Cells)
return err
styleID, _ := f.GetCellStyle(sheet, hCell)
return f.SetCellStyle(sheet, hCell, vCell, styleID)
}

// UnmergeCell provides a function to unmerge a given coordinate area.
Expand All @@ -77,12 +80,12 @@ func (f *File) MergeCell(sheet, hcell, vcell string) error {
// err := f.UnmergeCell("Sheet1", "D3", "E9")
//
// Attention: overlapped areas will also be unmerged.
func (f *File) UnmergeCell(sheet string, hcell, vcell string) error {
func (f *File) UnmergeCell(sheet string, hCell, vCell string) error {
ws, err := f.workSheetReader(sheet)
if err != nil {
return err
}
rect1, err := areaRefToCoordinates(hcell + ":" + vcell)
rect1, err := areaRefToCoordinates(hCell + ":" + vCell)
if err != nil {
return err
}
Expand Down Expand Up @@ -254,9 +257,9 @@ func mergeCell(cell1, cell2 *xlsxMergeCell) *xlsxMergeCell {
if rect1[3] < rect2[3] {
rect1[3], rect2[3] = rect2[3], rect1[3]
}
hcell, _ := CoordinatesToCellName(rect1[0], rect1[1])
vcell, _ := CoordinatesToCellName(rect1[2], rect1[3])
return &xlsxMergeCell{rect: rect1, Ref: hcell + ":" + vcell}
hCell, _ := CoordinatesToCellName(rect1[0], rect1[1])
vCell, _ := CoordinatesToCellName(rect1[2], rect1[3])
return &xlsxMergeCell{rect: rect1, Ref: hCell + ":" + vCell}
}

// MergeCell define a merged cell data.
Expand Down
2 changes: 1 addition & 1 deletion merge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func TestMergeCell(t *testing.T) {
assert.NoError(t, f.MergeCell("Sheet1", "G10", "K12"))
assert.NoError(t, f.SetCellValue("Sheet1", "G11", "set value in merged cell"))
assert.NoError(t, f.SetCellInt("Sheet1", "H11", 100))
assert.NoError(t, f.SetCellValue("Sheet1", "I11", float64(0.5)))
assert.NoError(t, f.SetCellValue("Sheet1", "I11", 0.5))
assert.NoError(t, f.SetCellHyperLink("Sheet1", "J11", "https://github.com/xuri/excelize", "External"))
assert.NoError(t, f.SetCellFormula("Sheet1", "G12", "SUM(Sheet1!B19,Sheet1!C19)"))
value, err := f.GetCellValue("Sheet1", "H11")
Expand Down
953 changes: 953 additions & 0 deletions numfmt.go

Large diffs are not rendered by default.

1,011 changes: 1,011 additions & 0 deletions numfmt_test.go

Large diffs are not rendered by default.

27 changes: 13 additions & 14 deletions picture.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

Expand Down Expand Up @@ -76,7 +76,7 @@ func parseFormatPictureSet(formatSet string) (*formatPicture, error) {
// }
// }
//
// The optional parameter "autofit" specifies if make image size auto fits the
// The optional parameter "autofit" specifies if you make image size auto-fits the
// cell, the default value of that is 'false'.
//
// The optional parameter "hyperlink" specifies the hyperlink of the image.
Expand All @@ -86,7 +86,7 @@ func parseFormatPictureSet(formatSet string) (*formatPicture, error) {
// cells in this workbook. When the "hyperlink_type" is "Location",
// coordinates need to start with "#".
//
// The optional parameter "positioning" defines two types of the position of a
// The optional parameter "positioning" defines two types of the position of an
// image in an Excel spreadsheet, "oneCell" (Move but don't size with
// cells) or "absolute" (Don't move or size with cells). If you don't set this
// parameter, the default positioning is move and size with cells.
Expand Down Expand Up @@ -209,7 +209,7 @@ func (f *File) deleteSheetRelationships(sheet, rID string) {
if !ok {
name = strings.ToLower(sheet) + ".xml"
}
var rels = "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
rels := "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
sheetRels := f.relsReader(rels)
if sheetRels == nil {
sheetRels = &xlsxRelationships{}
Expand Down Expand Up @@ -288,8 +288,7 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, he
}
col--
row--
colStart, rowStart, colEnd, rowEnd, x2, y2 :=
f.positionObjectPixels(sheet, col, row, formatSet.OffsetX, formatSet.OffsetY, width, height)
colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, col, row, formatSet.OffsetX, formatSet.OffsetY, width, height)
content, cNvPrID := f.drawingParser(drawingXML)
twoCellAnchor := xdrCellAnchor{}
twoCellAnchor.EditAs = formatSet.Positioning
Expand Down Expand Up @@ -372,7 +371,7 @@ func (f *File) addMedia(file []byte, ext string) string {
// setContentTypePartImageExtensions provides a function to set the content
// type for relationship parts and the Main Document part.
func (f *File) setContentTypePartImageExtensions() {
var imageTypes = map[string]bool{"jpeg": false, "png": false, "gif": false, "tiff": false}
imageTypes := map[string]bool{"jpeg": false, "png": false, "gif": false, "tiff": false}
content := f.contentTypesReader()
content.Lock()
defer content.Unlock()
Expand Down Expand Up @@ -465,7 +464,7 @@ func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
if !ok {
name = strings.ToLower(sheet) + ".xml"
}
var rels = "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
rels := "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
sheetRels := f.relsReader(rels)
if sheetRels == nil {
sheetRels = &xlsxRelationships{}
Expand Down
11 changes: 5 additions & 6 deletions picture_test.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package excelize

import (
"fmt"
_ "image/gif"
_ "image/jpeg"
_ "image/png"

_ "golang.org/x/image/tiff"

"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"

_ "golang.org/x/image/tiff"

"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -103,7 +102,7 @@ func TestGetPicture(t *testing.T) {
file, raw, err := f.GetPicture("Sheet1", "F21")
assert.NoError(t, err)
if !assert.NotEmpty(t, filepath.Join("test", file)) || !assert.NotEmpty(t, raw) ||
!assert.NoError(t, ioutil.WriteFile(filepath.Join("test", file), raw, 0644)) {
!assert.NoError(t, ioutil.WriteFile(filepath.Join("test", file), raw, 0o644)) {

t.FailNow()
}
Expand Down Expand Up @@ -138,7 +137,7 @@ func TestGetPicture(t *testing.T) {
file, raw, err = f.GetPicture("Sheet1", "F21")
assert.NoError(t, err)
if !assert.NotEmpty(t, filepath.Join("test", file)) || !assert.NotEmpty(t, raw) ||
!assert.NoError(t, ioutil.WriteFile(filepath.Join("test", file), raw, 0644)) {
!assert.NoError(t, ioutil.WriteFile(filepath.Join("test", file), raw, 0o644)) {
t.FailNow()
}

Expand Down
40 changes: 20 additions & 20 deletions pivotTable.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

Expand Down Expand Up @@ -132,7 +132,7 @@ type PivotTableField struct {
//
func (f *File) AddPivotTable(opt *PivotTableOption) error {
// parameter validation
dataSheet, pivotTableSheetPath, err := f.parseFormatPivotTableSet(opt)
_, pivotTableSheetPath, err := f.parseFormatPivotTableSet(opt)
if err != nil {
return err
}
Expand All @@ -143,7 +143,7 @@ func (f *File) AddPivotTable(opt *PivotTableOption) error {
sheetRelationshipsPivotTableXML := "../pivotTables/pivotTable" + strconv.Itoa(pivotTableID) + ".xml"
pivotTableXML := strings.Replace(sheetRelationshipsPivotTableXML, "..", "xl", -1)
pivotCacheXML := "xl/pivotCache/pivotCacheDefinition" + strconv.Itoa(pivotCacheID) + ".xml"
err = f.addPivotCache(pivotCacheID, pivotCacheXML, opt, dataSheet)
err = f.addPivotCache(pivotCacheXML, opt)
if err != nil {
return err
}
Expand Down Expand Up @@ -230,7 +230,7 @@ func (f *File) adjustRange(rangeStr string) (string, []int, error) {
// getPivotFieldsOrder provides a function to get order list of pivot table
// fields.
func (f *File) getPivotFieldsOrder(opt *PivotTableOption) ([]string, error) {
order := []string{}
var order []string
dataRange := f.getDefinedNameRefTo(opt.DataRange, opt.pivotTableSheetName)
if dataRange == "" {
dataRange = opt.DataRange
Expand All @@ -251,7 +251,7 @@ func (f *File) getPivotFieldsOrder(opt *PivotTableOption) ([]string, error) {
}

// addPivotCache provides a function to create a pivot cache by given properties.
func (f *File) addPivotCache(pivotCacheID int, pivotCacheXML string, opt *PivotTableOption, ws *xlsxWorksheet) error {
func (f *File) addPivotCache(pivotCacheXML string, opt *PivotTableOption) error {
// validate data range
definedNameRef := true
dataRange := f.getDefinedNameRefTo(opt.DataRange, opt.pivotTableSheetName)
Expand All @@ -265,8 +265,8 @@ func (f *File) addPivotCache(pivotCacheID int, pivotCacheXML string, opt *PivotT
}
// data range has been checked
order, _ := f.getPivotFieldsOrder(opt)
hcell, _ := CoordinatesToCellName(coordinates[0], coordinates[1])
vcell, _ := CoordinatesToCellName(coordinates[2], coordinates[3])
hCell, _ := CoordinatesToCellName(coordinates[0], coordinates[1])
vCell, _ := CoordinatesToCellName(coordinates[2], coordinates[3])
pc := xlsxPivotCacheDefinition{
SaveData: false,
RefreshOnLoad: true,
Expand All @@ -276,7 +276,7 @@ func (f *File) addPivotCache(pivotCacheID int, pivotCacheXML string, opt *PivotT
CacheSource: &xlsxCacheSource{
Type: "worksheet",
WorksheetSource: &xlsxWorksheetSource{
Ref: hcell + ":" + vcell,
Ref: hCell + ":" + vCell,
Sheet: dataSheet,
},
},
Expand Down Expand Up @@ -320,8 +320,8 @@ func (f *File) addPivotTable(cacheID, pivotTableID int, pivotTableXML string, op
return fmt.Errorf("parameter 'PivotTableRange' parsing error: %s", err.Error())
}

hcell, _ := CoordinatesToCellName(coordinates[0], coordinates[1])
vcell, _ := CoordinatesToCellName(coordinates[2], coordinates[3])
hCell, _ := CoordinatesToCellName(coordinates[0], coordinates[1])
vCell, _ := CoordinatesToCellName(coordinates[2], coordinates[3])

pivotTableStyle := func() string {
if opt.PivotTableStyleName == "" {
Expand All @@ -345,7 +345,7 @@ func (f *File) addPivotTable(cacheID, pivotTableID int, pivotTableXML string, op
ShowError: &opt.ShowError,
DataCaption: "Values",
Location: &xlsxLocation{
Ref: hcell + ":" + vcell,
Ref: hCell + ":" + vCell,
FirstDataCol: 1,
FirstDataRow: 1,
FirstHeaderRow: 1,
Expand Down Expand Up @@ -509,7 +509,7 @@ func (f *File) addPivotColFields(pt *xlsxPivotTableDefinition, opt *PivotTableOp
})
}

//in order to create pivot in case there is many Columns and Many Datas
// in order to create pivot in case there is many Columns and Data
if len(opt.Data) > 1 {
pt.ColFields.Field = append(pt.ColFields.Field, &xlsxField{
X: -2,
Expand Down Expand Up @@ -626,13 +626,13 @@ func (f *File) countPivotCache() int {
// getPivotFieldsIndex convert the column of the first row in the data region
// to a sequential index by given fields and pivot option.
func (f *File) getPivotFieldsIndex(fields []PivotTableField, opt *PivotTableOption) ([]int, error) {
pivotFieldsIndex := []int{}
var pivotFieldsIndex []int
orders, err := f.getPivotFieldsOrder(opt)
if err != nil {
return pivotFieldsIndex, err
}
for _, field := range fields {
if pos := inStrSlice(orders, field.Data); pos != -1 {
if pos := inStrSlice(orders, field.Data, true); pos != -1 {
pivotFieldsIndex = append(pivotFieldsIndex, pos)
}
}
Expand Down
8 changes: 4 additions & 4 deletions pivotTable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ func TestAddPivotTable(t *testing.T) {
PivotTableRange: "Sheet1!$G$2:$M$34",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}},
Data: []PivotTableField{{Data: "Sales", Subtotal: "-", Name: strings.Repeat("s", 256)}},
Data: []PivotTableField{{Data: "Sales", Subtotal: "-", Name: strings.Repeat("s", MaxFieldLength+1)}},
}))

// Test adjust range with invalid range
Expand All @@ -235,15 +235,15 @@ func TestAddPivotTable(t *testing.T) {
_, err = f.getPivotFieldsOrder(&PivotTableOption{})
assert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`)
// Test add pivot cache with empty data range
assert.EqualError(t, f.addPivotCache(0, "", &PivotTableOption{}, nil), "parameter 'DataRange' parsing error: parameter is required")
assert.EqualError(t, f.addPivotCache("", &PivotTableOption{}), "parameter 'DataRange' parsing error: parameter is required")
// Test add pivot cache with invalid data range
assert.EqualError(t, f.addPivotCache(0, "", &PivotTableOption{
assert.EqualError(t, f.addPivotCache("", &PivotTableOption{
DataRange: "$A$1:$E$31",
PivotTableRange: "Sheet1!$U$34:$O$2",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}},
Data: []PivotTableField{{Data: "Sales"}},
}, nil), "parameter 'DataRange' parsing error: parameter is invalid")
}), "parameter 'DataRange' parsing error: parameter is invalid")
// Test add pivot table with empty options
assert.EqualError(t, f.addPivotTable(0, 0, "", &PivotTableOption{}), "parameter 'PivotTableRange' parsing error: parameter is required")
// Test add pivot table with invalid data range
Expand Down
255 changes: 127 additions & 128 deletions rows.go

Large diffs are not rendered by default.

59 changes: 36 additions & 23 deletions rows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package excelize

import (
"bytes"
"encoding/xml"
"fmt"
"path/filepath"
"testing"
Expand Down Expand Up @@ -44,13 +45,6 @@ func TestRows(t *testing.T) {
}
assert.NoError(t, f.Close())

f = NewFile()
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<worksheet><sheetData><row r="1"><c r="A1" t="s"><v>1</v></c></row><row r="A"><c r="2" t="str"><v>B</v></c></row></sheetData></worksheet>`))
f.Sheet.Delete("xl/worksheets/sheet1.xml")
delete(f.checked, "xl/worksheets/sheet1.xml")
_, err = f.Rows("Sheet1")
assert.EqualError(t, err, `strconv.Atoi: parsing "A": invalid syntax`)

f.Pkg.Store("xl/worksheets/sheet1.xml", nil)
_, err = f.Rows("Sheet1")
assert.NoError(t, err)
Expand Down Expand Up @@ -81,8 +75,6 @@ func TestRowsIterator(t *testing.T) {

for rows.Next() {
rowCount++
assert.Equal(t, rowCount, rows.CurrentRow())
assert.Equal(t, expectedNumRow, rows.TotalRows())
require.True(t, rowCount <= expectedNumRow, "rowCount is greater than expected")
}
assert.Equal(t, expectedNumRow, rowCount)
Expand Down Expand Up @@ -186,16 +178,16 @@ func TestColumns(t *testing.T) {
assert.NoError(t, err)

rows.decoder = f.xmlNewDecoder(bytes.NewReader([]byte(`<worksheet><sheetData><row r="A"><c r="A1" t="s"><v>1</v></c></row><row r="A"><c r="2" t="str"><v>B</v></c></row></sheetData></worksheet>`)))
rows.stashRow, rows.curRow = 0, 1
assert.True(t, rows.Next())
_, err = rows.Columns()
assert.EqualError(t, err, `strconv.Atoi: parsing "A": invalid syntax`)

rows.decoder = f.xmlNewDecoder(bytes.NewReader([]byte(`<worksheet><sheetData><row r="1"><c r="A1" t="s"><v>1</v></c></row><row r="A"><c r="2" t="str"><v>B</v></c></row></sheetData></worksheet>`)))
_, err = rows.Columns()
assert.NoError(t, err)

rows.curRow = 3
rows.decoder = f.xmlNewDecoder(bytes.NewReader([]byte(`<worksheet><sheetData><row r="1"><c r="A" t="s"><v>1</v></c></row></sheetData></worksheet>`)))
assert.True(t, rows.Next())
_, err = rows.Columns()
assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())

Expand All @@ -207,7 +199,7 @@ func TestColumns(t *testing.T) {

func TestSharedStringsReader(t *testing.T) {
f := NewFile()
f.Pkg.Store(dafaultXMLPathSharedStrings, MacintoshCyrillicCharset)
f.Pkg.Store(defaultXMLPathSharedStrings, MacintoshCyrillicCharset)
f.sharedStringsReader()
si := xlsxSI{}
assert.EqualValues(t, "", si.String())
Expand All @@ -221,16 +213,16 @@ func TestRowVisibility(t *testing.T) {
f.NewSheet("Sheet3")
assert.NoError(t, f.SetRowVisible("Sheet3", 2, false))
assert.NoError(t, f.SetRowVisible("Sheet3", 2, true))
visiable, err := f.GetRowVisible("Sheet3", 2)
assert.Equal(t, true, visiable)
visible, err := f.GetRowVisible("Sheet3", 2)
assert.Equal(t, true, visible)
assert.NoError(t, err)
visiable, err = f.GetRowVisible("Sheet3", 25)
assert.Equal(t, false, visiable)
visible, err = f.GetRowVisible("Sheet3", 25)
assert.Equal(t, false, visible)
assert.NoError(t, err)
assert.EqualError(t, f.SetRowVisible("Sheet3", 0, true), newInvalidRowNumberError(0).Error())
assert.EqualError(t, f.SetRowVisible("SheetN", 2, false), "sheet SheetN is not exist")

visible, err := f.GetRowVisible("Sheet3", 0)
visible, err = f.GetRowVisible("Sheet3", 0)
assert.Equal(t, false, visible)
assert.EqualError(t, err, newInvalidRowNumberError(0).Error())
_, err = f.GetRowVisible("SheetN", 1)
Expand Down Expand Up @@ -330,8 +322,9 @@ func TestInsertRow(t *testing.T) {
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestInsertRow.xlsx")))
}

// Testing internal sructure state after insert operations.
// It is important for insert workflow to be constant to avoid side effect with functions related to internal structure.
// Testing internal structure state after insert operations. It is important
// for insert workflow to be constant to avoid side effect with functions
// related to internal structure.
func TestInsertRowInEmptyFile(t *testing.T) {
f := NewFile()
sheet1 := f.GetSheetName(0)
Expand Down Expand Up @@ -668,6 +661,7 @@ func TestDuplicateRowInsertBefore(t *testing.T) {
f := newFileWithDefaults()

assert.NoError(t, f.DuplicateRowTo(sheet, 2, 1))
assert.NoError(t, f.DuplicateRowTo(sheet, 10, 4))

if !assert.NoError(t, f.SaveAs(fmt.Sprintf(outFile, "InsertBefore"))) {
t.FailNow()
Expand All @@ -677,7 +671,7 @@ func TestDuplicateRowInsertBefore(t *testing.T) {
"A1": cells["A2"], "B1": cells["B2"],
"A2": cells["A1"], "B2": cells["B1"],
"A3": cells["A2"], "B3": cells["B2"],
"A4": cells["A3"], "B4": cells["B3"],
"A5": cells["A3"], "B5": cells["B3"],
}
for cell, val := range expect {
v, err := f.GetCellValue(sheet, cell)
Expand Down Expand Up @@ -845,7 +839,19 @@ func TestDuplicateRowInvalidRowNum(t *testing.T) {
}

func TestDuplicateRowTo(t *testing.T) {
f := File{}
f, sheetName := NewFile(), "Sheet1"
// Test duplicate row with invalid target row number
assert.Equal(t, nil, f.DuplicateRowTo(sheetName, 1, 0))
// Test duplicate row with equal source and target row number
assert.Equal(t, nil, f.DuplicateRowTo(sheetName, 1, 1))
// Test duplicate row on the blank worksheet
assert.Equal(t, nil, f.DuplicateRowTo(sheetName, 1, 2))
// Test duplicate row on the worksheet with illegal cell coordinates
f.Sheet.Store("xl/worksheets/sheet1.xml", &xlsxWorksheet{
MergeCells: &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:B1"}}},
})
assert.EqualError(t, f.DuplicateRowTo(sheetName, 1, 2), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
// Test duplicate row on not exists worksheet
assert.EqualError(t, f.DuplicateRowTo("SheetN", 1, 2), "sheet SheetN is not exist")
}

Expand Down Expand Up @@ -896,12 +902,12 @@ func TestErrSheetNotExistError(t *testing.T) {

func TestCheckRow(t *testing.T) {
f := NewFile()
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" ><sheetData><row r="2"><c><v>1</v></c><c r="F2"><v>2</v></c><c><v>3</v></c><c><v>4</v></c><c r="M2"><v>5</v></c></row></sheetData></worksheet>`))
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(xml.Header+`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" ><sheetData><row r="2"><c><v>1</v></c><c r="F2"><v>2</v></c><c><v>3</v></c><c><v>4</v></c><c r="M2"><v>5</v></c></row></sheetData></worksheet>`))
_, err := f.GetRows("Sheet1")
assert.NoError(t, err)
assert.NoError(t, f.SetCellValue("Sheet1", "A1", false))
f = NewFile()
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" ><sheetData><row r="2"><c><v>1</v></c><c r="-"><v>2</v></c><c><v>3</v></c><c><v>4</v></c><c r="M2"><v>5</v></c></row></sheetData></worksheet>`))
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(xml.Header+`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" ><sheetData><row r="2"><c><v>1</v></c><c r="-"><v>2</v></c><c><v>3</v></c><c><v>4</v></c><c r="M2"><v>5</v></c></row></sheetData></worksheet>`))
f.Sheet.Delete("xl/worksheets/sheet1.xml")
delete(f.checked, "xl/worksheets/sheet1.xml")
assert.EqualError(t, f.SetCellValue("Sheet1", "A1", false), newCellNameToCoordinatesError("-", newInvalidCellNameError("-")).Error())
Expand All @@ -916,6 +922,9 @@ func TestSetRowStyle(t *testing.T) {
assert.EqualError(t, f.SetRowStyle("Sheet1", 1, 1, -1), newInvalidStyleID(-1).Error())
assert.EqualError(t, f.SetRowStyle("SheetN", 1, 1, styleID), "sheet SheetN is not exist")
assert.NoError(t, f.SetRowStyle("Sheet1", 10, 1, styleID))
cellStyleID, err := f.GetCellStyle("Sheet1", "B2")
assert.NoError(t, err)
assert.Equal(t, styleID, cellStyleID)
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetRowStyle.xlsx")))
}

Expand All @@ -941,6 +950,10 @@ func TestNumberFormats(t *testing.T) {
assert.NoError(t, f.Close())
}

func TestRoundPrecision(t *testing.T) {
assert.Equal(t, "text", roundPrecision("text", 0))
}

func BenchmarkRows(b *testing.B) {
f, _ := OpenFile(filepath.Join("test", "Book1.xlsx"))
for i := 0; i < b.N; i++ {
Expand Down
19 changes: 9 additions & 10 deletions shape.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

Expand Down Expand Up @@ -351,9 +351,8 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *format
width := int(float64(formatSet.Width) * formatSet.Format.XScale)
height := int(float64(formatSet.Height) * formatSet.Format.YScale)

colStart, rowStart, colEnd, rowEnd, x2, y2 :=
f.positionObjectPixels(sheet, colIdx, rowIdx, formatSet.Format.OffsetX, formatSet.Format.OffsetY,
width, height)
colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, colIdx, rowIdx, formatSet.Format.OffsetX, formatSet.Format.OffsetY,
width, height)
content, cNvPrID := f.drawingParser(drawingXML)
twoCellAnchor := xdrCellAnchor{}
twoCellAnchor.EditAs = formatSet.Format.Positioning
Expand Down
205 changes: 90 additions & 115 deletions sheet.go

Large diffs are not rendered by default.

23 changes: 19 additions & 4 deletions sheet_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package excelize

import (
"encoding/xml"
"fmt"
"path/filepath"
"strconv"
Expand Down Expand Up @@ -123,7 +124,6 @@ func TestPageLayoutOption(t *testing.T) {

for i, test := range testData {
t.Run(fmt.Sprintf("TestData%d", i), func(t *testing.T) {

opt := test.nonDefault
t.Logf("option %T", opt)

Expand Down Expand Up @@ -276,7 +276,7 @@ func TestDefinedName(t *testing.T) {
Name: "Amount",
RefersTo: "Sheet1!$A$2:$D$5",
Comment: "defined name comment",
}), ErrDefinedNameduplicate.Error())
}), ErrDefinedNameDuplicate.Error())
assert.EqualError(t, f.DeleteDefinedName(&DefinedName{
Name: "No Exist Defined Name",
}), ErrDefinedNameScope.Error())
Expand Down Expand Up @@ -400,11 +400,25 @@ func TestSetActiveSheet(t *testing.T) {

func TestSetSheetName(t *testing.T) {
f := NewFile()
// Test set workksheet with the same name.
// Test set worksheet with the same name.
f.SetSheetName("Sheet1", "Sheet1")
assert.Equal(t, "Sheet1", f.GetSheetName(0))
}

func TestWorksheetWriter(t *testing.T) {
f := NewFile()
// Test set cell value with alternate content
f.Sheet.Delete("xl/worksheets/sheet1.xml")
worksheet := xml.Header + `<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheetData><row r="1"><c r="A1"><v>%d</v></c></row></sheetData><mc:AlternateContent xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"><mc:Choice xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" Requires="a14"><xdr:twoCellAnchor editAs="oneCell"></xdr:twoCellAnchor></mc:Choice><mc:Fallback/></mc:AlternateContent></worksheet>`
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(worksheet, 1)))
f.checked = nil
assert.NoError(t, f.SetCellValue("Sheet1", "A1", 2))
f.workSheetWriter()
value, ok := f.Pkg.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
assert.Equal(t, fmt.Sprintf(worksheet, 2), string(value.([]byte)))
}

func TestGetWorkbookPath(t *testing.T) {
f := NewFile()
f.Pkg.Delete("_rels/.rels")
Expand All @@ -414,7 +428,7 @@ func TestGetWorkbookPath(t *testing.T) {
func TestGetWorkbookRelsPath(t *testing.T) {
f := NewFile()
f.Pkg.Delete("xl/_rels/.rels")
f.Pkg.Store("_rels/.rels", []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument" Target="/workbook.xml"/></Relationships>`))
f.Pkg.Store("_rels/.rels", []byte(xml.Header+`<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument" Target="/workbook.xml"/></Relationships>`))
assert.Equal(t, "_rels/workbook.xml.rels", f.getWorkbookRelsPath())
}

Expand Down Expand Up @@ -452,6 +466,7 @@ func BenchmarkNewSheet(b *testing.B) {
}
})
}

func newSheetWithSet() {
file := NewFile()
file.NewSheet("sheet1")
Expand Down
16 changes: 8 additions & 8 deletions sheetpr.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

Expand All @@ -25,7 +25,7 @@ type SheetPrOptionPtr interface {
}

type (
// CodeName is a SheetPrOption
// CodeName is an option used for SheetPrOption and WorkbookPrOption
CodeName string
// EnableFormatConditionsCalculation is a SheetPrOption
EnableFormatConditionsCalculation bool
Expand Down
7 changes: 2 additions & 5 deletions sheetpr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ func TestSheetPrOptions(t *testing.T) {

for i, test := range testData {
t.Run(fmt.Sprintf("TestData%d", i), func(t *testing.T) {

opt := test.nonDefault
t.Logf("option %T", opt)

Expand Down Expand Up @@ -153,10 +152,10 @@ func TestSheetPrOptions(t *testing.T) {
}
}

func TestSetSheetrOptions(t *testing.T) {
func TestSetSheetPrOptions(t *testing.T) {
f := NewFile()
assert.NoError(t, f.SetSheetPrOptions("Sheet1", TabColor("")))
// Test SetSheetrOptions on not exists worksheet.
// Test SetSheetPrOptions on not exists worksheet.
assert.EqualError(t, f.SetSheetPrOptions("SheetN"), "sheet SheetN is not exist")
}

Expand Down Expand Up @@ -258,7 +257,6 @@ func TestPageMarginsOption(t *testing.T) {

for i, test := range testData {
t.Run(fmt.Sprintf("TestData%d", i), func(t *testing.T) {

opt := test.nonDefault
t.Logf("option %T", opt)

Expand Down Expand Up @@ -395,7 +393,6 @@ func TestSheetFormatPrOptions(t *testing.T) {

for i, test := range testData {
t.Run(fmt.Sprintf("TestData%d", i), func(t *testing.T) {

opt := test.nonDefault
t.Logf("option %T", opt)

Expand Down
154 changes: 88 additions & 66 deletions sheetview.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

Expand All @@ -27,59 +27,49 @@ type SheetViewOptionPtr interface {
}

type (
// DefaultGridColor is a SheetViewOption. It specifies a flag indicating that
// the consuming application should use the default grid lines color (system
// dependent). Overrides any color specified in colorId.
// DefaultGridColor is a SheetViewOption. It specifies a flag indicating
// that the consuming application should use the default grid lines color
// (system dependent). Overrides any color specified in colorId.
DefaultGridColor bool
// RightToLeft is a SheetViewOption. It specifies a flag indicating whether
// the sheet is in 'right to left' display mode. When in this mode, Column A
// is on the far right, Column B ;is one column left of Column A, and so on.
// Also, information in cells is displayed in the Right to Left format.
RightToLeft bool
// ShowFormulas is a SheetViewOption. It specifies a flag indicating whether
// this sheet should display formulas.
// ShowFormulas is a SheetViewOption. It specifies a flag indicating
// whether this sheet should display formulas.
ShowFormulas bool
// ShowGridLines is a SheetViewOption. It specifies a flag indicating whether
// this sheet should display gridlines.
// ShowGridLines is a SheetViewOption. It specifies a flag indicating
// whether this sheet should display gridlines.
ShowGridLines bool
// ShowRowColHeaders is a SheetViewOption. It specifies a flag indicating
// whether the sheet should display row and column headings.
ShowRowColHeaders bool
// ZoomScale is a SheetViewOption. It specifies a window zoom magnification
// for current view representing percent values. This attribute is restricted
// to values ranging from 10 to 400. Horizontal & Vertical scale together.
ZoomScale float64
// TopLeftCell is a SheetViewOption. It specifies a location of the top left
// visible cell Location of the top left visible cell in the bottom right
// pane (when in Left-to-Right mode).
TopLeftCell string
// ShowZeros is a SheetViewOption. It specifies a flag indicating
// whether to "show a zero in cells that have zero value".
// When using a formula to reference another cell which is empty, the referenced value becomes 0
// ShowZeros is a SheetViewOption. It specifies a flag indicating whether
// to "show a zero in cells that have zero value". When using a formula to
// reference another cell which is empty, the referenced value becomes 0
// when the flag is true. (Default setting is true.)
ShowZeros bool

/* TODO
// ShowWhiteSpace is a SheetViewOption. It specifies a flag indicating
// whether page layout view shall display margins. False means do not display
// left, right, top (header), and bottom (footer) margins (even when there is
// data in the header or footer).
ShowWhiteSpace bool
// WindowProtection is a SheetViewOption.
WindowProtection bool
*/
// RightToLeft is a SheetViewOption. It specifies a flag indicating whether
// the sheet is in 'right to left' display mode. When in this mode, Column
// A is on the far right, Column B ;is one column left of Column A, and so
// on. Also, information in cells is displayed in the Right to Left format.
RightToLeft bool
// ShowRuler is a SheetViewOption. It specifies a flag indicating this
// sheet should display ruler.
ShowRuler bool
// View is a SheetViewOption. It specifies a flag indicating how sheet is
// displayed, by default it uses empty string available options: normal,
// pageLayout, pageBreakPreview
View string
// TopLeftCell is a SheetViewOption. It specifies a location of the top
// left visible cell Location of the top left visible cell in the bottom
// right pane (when in Left-to-Right mode).
TopLeftCell string
// ZoomScale is a SheetViewOption. It specifies a window zoom magnification
// for current view representing percent values. This attribute is
// restricted to values ranging from 10 to 400. Horizontal & Vertical
// scale together.
ZoomScale float64
)

// Defaults for each option are described in XML schema for CT_SheetView

func (o TopLeftCell) setSheetViewOption(view *xlsxSheetView) {
view.TopLeftCell = string(o)
}

func (o *TopLeftCell) getSheetViewOption(view *xlsxSheetView) {
*o = TopLeftCell(string(view.TopLeftCell))
}

func (o DefaultGridColor) setSheetViewOption(view *xlsxSheetView) {
view.DefaultGridColor = boolPtr(bool(o))
}
Expand All @@ -88,14 +78,6 @@ func (o *DefaultGridColor) getSheetViewOption(view *xlsxSheetView) {
*o = DefaultGridColor(defaultTrue(view.DefaultGridColor)) // Excel default: true
}

func (o RightToLeft) setSheetViewOption(view *xlsxSheetView) {
view.RightToLeft = bool(o) // Excel default: false
}

func (o *RightToLeft) getSheetViewOption(view *xlsxSheetView) {
*o = RightToLeft(view.RightToLeft)
}

func (o ShowFormulas) setSheetViewOption(view *xlsxSheetView) {
view.ShowFormulas = bool(o) // Excel default: false
}
Expand All @@ -112,6 +94,14 @@ func (o *ShowGridLines) getSheetViewOption(view *xlsxSheetView) {
*o = ShowGridLines(defaultTrue(view.ShowGridLines)) // Excel default: true
}

func (o ShowRowColHeaders) setSheetViewOption(view *xlsxSheetView) {
view.ShowRowColHeaders = boolPtr(bool(o))
}

func (o *ShowRowColHeaders) getSheetViewOption(view *xlsxSheetView) {
*o = ShowRowColHeaders(defaultTrue(view.ShowRowColHeaders)) // Excel default: true
}

func (o ShowZeros) setSheetViewOption(view *xlsxSheetView) {
view.ShowZeros = boolPtr(bool(o))
}
Expand All @@ -120,12 +110,40 @@ func (o *ShowZeros) getSheetViewOption(view *xlsxSheetView) {
*o = ShowZeros(defaultTrue(view.ShowZeros)) // Excel default: true
}

func (o ShowRowColHeaders) setSheetViewOption(view *xlsxSheetView) {
view.ShowRowColHeaders = boolPtr(bool(o))
func (o RightToLeft) setSheetViewOption(view *xlsxSheetView) {
view.RightToLeft = bool(o) // Excel default: false
}

func (o *ShowRowColHeaders) getSheetViewOption(view *xlsxSheetView) {
*o = ShowRowColHeaders(defaultTrue(view.ShowRowColHeaders)) // Excel default: true
func (o *RightToLeft) getSheetViewOption(view *xlsxSheetView) {
*o = RightToLeft(view.RightToLeft)
}

func (o ShowRuler) setSheetViewOption(view *xlsxSheetView) {
view.ShowRuler = boolPtr(bool(o))
}

func (o *ShowRuler) getSheetViewOption(view *xlsxSheetView) {
*o = ShowRuler(defaultTrue(view.ShowRuler)) // Excel default: true
}

func (o View) setSheetViewOption(view *xlsxSheetView) {
view.View = string(o)
}

func (o *View) getSheetViewOption(view *xlsxSheetView) {
if view.View != "" {
*o = View(view.View)
return
}
*o = "normal"
}

func (o TopLeftCell) setSheetViewOption(view *xlsxSheetView) {
view.TopLeftCell = string(o)
}

func (o *TopLeftCell) getSheetViewOption(view *xlsxSheetView) {
*o = TopLeftCell(view.TopLeftCell)
}

func (o ZoomScale) setSheetViewOption(view *xlsxSheetView) {
Expand Down Expand Up @@ -163,13 +181,15 @@ func (f *File) getSheetView(sheet string, viewIndex int) (*xlsxSheetView, error)
// Available options:
//
// DefaultGridColor(bool)
// RightToLeft(bool)
// ShowFormulas(bool)
// ShowGridLines(bool)
// ShowRowColHeaders(bool)
// ZoomScale(float64)
// TopLeftCell(string)
// ShowZeros(bool)
// RightToLeft(bool)
// ShowRuler(bool)
// View(string)
// TopLeftCell(string)
// ZoomScale(float64)
//
// Example:
//
Expand All @@ -193,13 +213,15 @@ func (f *File) SetSheetViewOptions(name string, viewIndex int, opts ...SheetView
// Available options:
//
// DefaultGridColor(bool)
// RightToLeft(bool)
// ShowFormulas(bool)
// ShowGridLines(bool)
// ShowRowColHeaders(bool)
// ZoomScale(float64)
// TopLeftCell(string)
// ShowZeros(bool)
// RightToLeft(bool)
// ShowRuler(bool)
// View(string)
// TopLeftCell(string)
// ZoomScale(float64)
//
// Example:
//
Expand Down
79 changes: 55 additions & 24 deletions sheetview_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,39 @@ import (

var _ = []SheetViewOption{
DefaultGridColor(true),
RightToLeft(false),
ShowFormulas(false),
ShowGridLines(true),
ShowRowColHeaders(true),
ShowZeros(true),
RightToLeft(false),
ShowRuler(false),
View("pageLayout"),
TopLeftCell("B2"),
ZoomScale(100),
// SheetViewOptionPtr are also SheetViewOption
new(DefaultGridColor),
new(RightToLeft),
new(ShowFormulas),
new(ShowGridLines),
new(ShowRowColHeaders),
new(ShowZeros),
new(RightToLeft),
new(ShowRuler),
new(View),
new(TopLeftCell),
new(ZoomScale),
}

var _ = []SheetViewOptionPtr{
(*DefaultGridColor)(nil),
(*RightToLeft)(nil),
(*ShowFormulas)(nil),
(*ShowGridLines)(nil),
(*ShowRowColHeaders)(nil),
(*ShowZeros)(nil),
(*RightToLeft)(nil),
(*ShowRuler)(nil),
(*View)(nil),
(*TopLeftCell)(nil),
(*ZoomScale)(nil),
}

func ExampleFile_SetSheetViewOptions() {
Expand All @@ -38,12 +50,14 @@ func ExampleFile_SetSheetViewOptions() {

if err := f.SetSheetViewOptions(sheet, 0,
DefaultGridColor(false),
RightToLeft(false),
ShowFormulas(true),
ShowGridLines(true),
ShowRowColHeaders(true),
ZoomScale(80),
RightToLeft(false),
ShowRuler(false),
View("pageLayout"),
TopLeftCell("C3"),
ZoomScale(80),
); err != nil {
fmt.Println(err)
}
Expand Down Expand Up @@ -81,7 +95,6 @@ func ExampleFile_SetSheetViewOptions() {
// - zoomScale: 80
// Used correct value:
// - zoomScale: 123

}

func ExampleFile_GetSheetViewOptions() {
Expand All @@ -90,80 +103,98 @@ func ExampleFile_GetSheetViewOptions() {

var (
defaultGridColor DefaultGridColor
rightToLeft RightToLeft
showFormulas ShowFormulas
showGridLines ShowGridLines
showZeros ShowZeros
showRowColHeaders ShowRowColHeaders
zoomScale ZoomScale
showZeros ShowZeros
rightToLeft RightToLeft
showRuler ShowRuler
view View
topLeftCell TopLeftCell
zoomScale ZoomScale
)

if err := f.GetSheetViewOptions(sheet, 0,
&defaultGridColor,
&rightToLeft,
&showFormulas,
&showGridLines,
&showZeros,
&showRowColHeaders,
&zoomScale,
&showZeros,
&rightToLeft,
&showRuler,
&view,
&topLeftCell,
&zoomScale,
); err != nil {
fmt.Println(err)
}

fmt.Println("Default:")
fmt.Println("- defaultGridColor:", defaultGridColor)
fmt.Println("- rightToLeft:", rightToLeft)
fmt.Println("- showFormulas:", showFormulas)
fmt.Println("- showGridLines:", showGridLines)
fmt.Println("- showZeros:", showZeros)
fmt.Println("- showRowColHeaders:", showRowColHeaders)
fmt.Println("- zoomScale:", zoomScale)
fmt.Println("- showZeros:", showZeros)
fmt.Println("- rightToLeft:", rightToLeft)
fmt.Println("- showRuler:", showRuler)
fmt.Println("- view:", view)
fmt.Println("- topLeftCell:", `"`+topLeftCell+`"`)
fmt.Println("- zoomScale:", zoomScale)

if err := f.SetSheetViewOptions(sheet, 0, TopLeftCell("B2")); err != nil {
if err := f.SetSheetViewOptions(sheet, 0, ShowGridLines(false)); err != nil {
fmt.Println(err)
}

if err := f.GetSheetViewOptions(sheet, 0, &topLeftCell); err != nil {
if err := f.GetSheetViewOptions(sheet, 0, &showGridLines); err != nil {
fmt.Println(err)
}

if err := f.SetSheetViewOptions(sheet, 0, ShowGridLines(false)); err != nil {
if err := f.SetSheetViewOptions(sheet, 0, ShowZeros(false)); err != nil {
fmt.Println(err)
}

if err := f.GetSheetViewOptions(sheet, 0, &showGridLines); err != nil {
if err := f.GetSheetViewOptions(sheet, 0, &showZeros); err != nil {
fmt.Println(err)
}

if err := f.SetSheetViewOptions(sheet, 0, ShowZeros(false)); err != nil {
if err := f.SetSheetViewOptions(sheet, 0, View("pageLayout")); err != nil {
fmt.Println(err)
}

if err := f.GetSheetViewOptions(sheet, 0, &showZeros); err != nil {
if err := f.GetSheetViewOptions(sheet, 0, &view); err != nil {
fmt.Println(err)
}

if err := f.SetSheetViewOptions(sheet, 0, TopLeftCell("B2")); err != nil {
fmt.Println(err)
}

if err := f.GetSheetViewOptions(sheet, 0, &topLeftCell); err != nil {
fmt.Println(err)
}

fmt.Println("After change:")
fmt.Println("- showGridLines:", showGridLines)
fmt.Println("- showZeros:", showZeros)
fmt.Println("- view:", view)
fmt.Println("- topLeftCell:", topLeftCell)

// Output:
// Default:
// - defaultGridColor: true
// - rightToLeft: false
// - showFormulas: false
// - showGridLines: true
// - showZeros: true
// - showRowColHeaders: true
// - zoomScale: 0
// - showZeros: true
// - rightToLeft: false
// - showRuler: true
// - view: normal
// - topLeftCell: ""
// - zoomScale: 0
// After change:
// - showGridLines: false
// - showZeros: false
// - view: pageLayout
// - topLeftCell: B2
}

Expand Down
16 changes: 8 additions & 8 deletions sparkline.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

Expand Down Expand Up @@ -362,7 +362,7 @@ func (f *File) addSparklineGroupByStyle(ID int) *xlsxX14SparklineGroup {
// given formatting options. Sparklines are small charts that fit in a single
// cell and are used to show trends in data. Sparklines are a feature of Excel
// 2010 and later only. You can write them to an XLSX file that can be read by
// Excel 2007 but they won't be displayed. For example, add a grouped
// Excel 2007, but they won't be displayed. For example, add a grouped
// sparkline. Changes are applied to all three:
//
// err := f.AddSparkline("Sheet1", &excelize.SparklineOption{
Expand Down
44 changes: 22 additions & 22 deletions stream.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

Expand Down Expand Up @@ -52,7 +52,7 @@ type StreamWriter struct {
// if err != nil {
// fmt.Println(err)
// }
// styleID, err := file.NewStyle(`{"font":{"color":"#777777"}}`)
// styleID, err := file.NewStyle(&excelize.Style{Font: &excelize.Font{Color: "#777777"}})
// if err != nil {
// fmt.Println(err)
// }
Expand Down Expand Up @@ -136,17 +136,17 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
// Note that the table must be at least two lines including the header. The
// header cells must contain strings and must be unique.
//
// Currently only one table is allowed for a StreamWriter. AddTable must be
// Currently, only one table is allowed for a StreamWriter. AddTable must be
// called after the rows are written but before Flush.
//
// See File.AddTable for details on the table format.
func (sw *StreamWriter) AddTable(hcell, vcell, format string) error {
func (sw *StreamWriter) AddTable(hCell, vCell, format string) error {
formatSet, err := parseFormatTableSet(format)
if err != nil {
return err
}

coordinates, err := areaRangeToCoordinates(hcell, vcell)
coordinates, err := areaRangeToCoordinates(hCell, vCell)
if err != nil {
return err
}
Expand Down Expand Up @@ -223,8 +223,8 @@ func (sw *StreamWriter) AddTable(hcell, vcell, format string) error {
}

// Extract values from a row in the StreamWriter.
func (sw *StreamWriter) getRowValues(hrow, hcol, vcol int) (res []string, err error) {
res = make([]string, vcol-hcol+1)
func (sw *StreamWriter) getRowValues(hRow, hCol, vCol int) (res []string, err error) {
res = make([]string, vCol-hCol+1)

r, err := sw.rawData.Reader()
if err != nil {
Expand All @@ -240,7 +240,7 @@ func (sw *StreamWriter) getRowValues(hrow, hcol, vcol int) (res []string, err er
if err != nil {
return nil, err
}
startElement, ok := getRowElement(token, hrow)
startElement, ok := getRowElement(token, hRow)
if !ok {
continue
}
Expand All @@ -254,17 +254,17 @@ func (sw *StreamWriter) getRowValues(hrow, hcol, vcol int) (res []string, err er
if err != nil {
return nil, err
}
if col < hcol || col > vcol {
if col < hCol || col > vCol {
continue
}
res[col-hcol] = c.V
res[col-hCol] = c.V
}
return res, nil
}
}

// Check if the token is an XLSX row with the matching row number.
func getRowElement(token xml.Token, hrow int) (startElement xml.StartElement, ok bool) {
func getRowElement(token xml.Token, hRow int) (startElement xml.StartElement, ok bool) {
startElement, ok = token.(xml.StartElement)
if !ok {
return
Expand All @@ -279,7 +279,7 @@ func getRowElement(token xml.Token, hrow int) (startElement xml.StartElement, ok
continue
}
row, _ := strconv.Atoi(attr.Value)
if row == hrow {
if row == hRow {
ok = true
return
}
Expand Down Expand Up @@ -406,13 +406,13 @@ func (sw *StreamWriter) SetColWidth(min, max int, width float64) error {
// MergeCell provides a function to merge cells by a given coordinate area for
// the StreamWriter. Don't create a merged cell that overlaps with another
// existing merged cell.
func (sw *StreamWriter) MergeCell(hcell, vcell string) error {
_, err := areaRangeToCoordinates(hcell, vcell)
func (sw *StreamWriter) MergeCell(hCell, vCell string) error {
_, err := areaRangeToCoordinates(hCell, vCell)
if err != nil {
return err
}
sw.mergeCellsCount++
sw.mergeCells += fmt.Sprintf(`<mergeCell ref="%s:%s"/>`, hcell, vcell)
sw.mergeCells += fmt.Sprintf(`<mergeCell ref="%s:%s"/>`, hCell, vCell)
return nil
}

Expand Down Expand Up @@ -563,7 +563,7 @@ func (bw *bufferedWriter) Write(p []byte) (n int, err error) {
return bw.buf.Write(p)
}

// WriteString wites to the in-memory buffer. The err is always nil.
// WriteString wite to the in-memory buffer. The err is always nil.
func (bw *bufferedWriter) WriteString(p string) (n int, err error) {
return bw.buf.WriteString(p)
}
Expand Down
4 changes: 2 additions & 2 deletions stream_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestStreamWriter(t *testing.T) {
assert.NoError(t, streamWriter.SetRow("A3", row))

// Test set cell with style.
styleID, err := file.NewStyle(`{"font":{"color":"#777777"}}`)
styleID, err := file.NewStyle(&Style{Font: &Font{Color: "#777777"}})
assert.NoError(t, err)
assert.NoError(t, streamWriter.SetRow("A4", []interface{}{Cell{StyleID: styleID}, Cell{Formula: "SUM(A10,B10)"}}), RowOpts{Height: 45, StyleID: styleID})
assert.NoError(t, streamWriter.SetRow("A5", []interface{}{&Cell{StyleID: styleID, Value: "cell"}, &Cell{Formula: "SUM(A10,B10)"}}))
Expand Down Expand Up @@ -223,7 +223,7 @@ func TestSetCellValFunc(t *testing.T) {
assert.NoError(t, sw.setCellValFunc(c, uint32(4294967295)))
assert.NoError(t, sw.setCellValFunc(c, uint64(18446744073709551615)))
assert.NoError(t, sw.setCellValFunc(c, float32(100.1588)))
assert.NoError(t, sw.setCellValFunc(c, float64(100.1588)))
assert.NoError(t, sw.setCellValFunc(c, 100.1588))
assert.NoError(t, sw.setCellValFunc(c, " Hello"))
assert.NoError(t, sw.setCellValFunc(c, []byte(" Hello")))
assert.NoError(t, sw.setCellValFunc(c, time.Now().UTC()))
Expand Down
306 changes: 98 additions & 208 deletions styles.go

Large diffs are not rendered by default.

24 changes: 2 additions & 22 deletions styles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,10 @@ func TestNewStyle(t *testing.T) {
assert.EqualError(t, err, ErrFontSize.Error())

// new numeric custom style
fmt := "####;####"
numFmt := "####;####"
f.Styles.NumFmts = nil
styleID, err = f.NewStyle(&Style{
CustomNumFmt: &fmt,
CustomNumFmt: &numFmt,
})
assert.NoError(t, err)
assert.Equal(t, 2, styleID)
Expand Down Expand Up @@ -325,26 +325,6 @@ func TestGetFillID(t *testing.T) {
assert.Equal(t, -1, getFillID(NewFile().stylesReader(), &Style{Fill: Fill{Type: "unknown"}}))
}

func TestParseTime(t *testing.T) {
assert.Equal(t, "2019", parseTime("43528", "YYYY"))
assert.Equal(t, "43528", parseTime("43528", ""))

assert.Equal(t, "2019-03-04 05:05:42", parseTime("43528.2123", "YYYY-MM-DD hh:mm:ss"))
assert.Equal(t, "2019-03-04 05:05:42", parseTime("43528.2123", "YYYY-MM-DD hh:mm:ss;YYYY-MM-DD hh:mm:ss"))
assert.Equal(t, "3/4/2019 5:5:42", parseTime("43528.2123", "M/D/YYYY h:m:s"))
assert.Equal(t, "3/4/2019 0:5:42", parseTime("43528.003958333335", "m/d/yyyy h:m:s"))
assert.Equal(t, "3/4/2019 0:05:42", parseTime("43528.003958333335", "M/D/YYYY h:mm:s"))
assert.Equal(t, "3:30:00 PM", parseTime("0.64583333333333337", "h:mm:ss am/pm"))
assert.Equal(t, "0:05", parseTime("43528.003958333335", "h:mm"))
assert.Equal(t, "0:0", parseTime("6.9444444444444444E-5", "h:m"))
assert.Equal(t, "0:00", parseTime("6.9444444444444444E-5", "h:mm"))
assert.Equal(t, "0:0", parseTime("6.9444444444444444E-5", "h:m"))
assert.Equal(t, "12:1", parseTime("0.50070601851851848", "h:m"))
assert.Equal(t, "23:30", parseTime("0.97952546296296295", "h:m"))
assert.Equal(t, "March", parseTime("43528", "mmmm"))
assert.Equal(t, "Monday", parseTime("43528", "dddd"))
}

func TestThemeColor(t *testing.T) {
for _, clr := range [][]string{
{"FF000000", ThemeColor("000000", -0.1)},
Expand Down
66 changes: 33 additions & 33 deletions table.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

Expand Down Expand Up @@ -61,27 +61,27 @@ func parseFormatTableSet(formatSet string) (*formatTable, error) {
// TableStyleMedium1 - TableStyleMedium28
// TableStyleDark1 - TableStyleDark11
//
func (f *File) AddTable(sheet, hcell, vcell, format string) error {
func (f *File) AddTable(sheet, hCell, vCell, format string) error {
formatSet, err := parseFormatTableSet(format)
if err != nil {
return err
}
// Coordinate conversion, convert C1:B3 to 2,0,1,2.
hcol, hrow, err := CellNameToCoordinates(hcell)
hCol, hRow, err := CellNameToCoordinates(hCell)
if err != nil {
return err
}
vcol, vrow, err := CellNameToCoordinates(vcell)
vCol, vRow, err := CellNameToCoordinates(vCell)
if err != nil {
return err
}

if vcol < hcol {
vcol, hcol = hcol, vcol
if vCol < hCol {
vCol, hCol = hCol, vCol
}

if vrow < hrow {
vrow, hrow = hrow, vrow
if vRow < hRow {
vRow, hRow = hRow, vRow
}

tableID := f.countTables() + 1
Expand All @@ -94,7 +94,7 @@ func (f *File) AddTable(sheet, hcell, vcell, format string) error {
return err
}
f.addSheetNameSpace(sheet, SourceRelationship)
if err = f.addTable(sheet, tableXML, hcol, hrow, vcol, vrow, tableID, formatSet); err != nil {
if err = f.addTable(sheet, tableXML, hCol, hRow, vCol, vRow, tableID, formatSet); err != nil {
return err
}
f.addContentTypePart(tableID, "table")
Expand Down Expand Up @@ -257,9 +257,9 @@ func parseAutoFilterSet(formatSet string) (*formatAutoFilter, error) {
// Excel also allows some simple string matching operations:
//
// x == b* // begins with b
// x != b* // doesnt begin with b
// x != b* // doesn't begin with b
// x == *b // ends with b
// x != *b // doesnt end with b
// x != *b // doesn't end with b
// x == *b* // contains b
// x != *b* // doesn't contains b
//
Expand All @@ -276,31 +276,31 @@ func parseAutoFilterSet(formatSet string) (*formatAutoFilter, error) {
// col < 2000
// Price < 2000
//
func (f *File) AutoFilter(sheet, hcell, vcell, format string) error {
hcol, hrow, err := CellNameToCoordinates(hcell)
func (f *File) AutoFilter(sheet, hCell, vCell, format string) error {
hCol, hRow, err := CellNameToCoordinates(hCell)
if err != nil {
return err
}
vcol, vrow, err := CellNameToCoordinates(vcell)
vCol, vRow, err := CellNameToCoordinates(vCell)
if err != nil {
return err
}

if vcol < hcol {
vcol, hcol = hcol, vcol
if vCol < hCol {
vCol, hCol = hCol, vCol
}

if vrow < hrow {
vrow, hrow = hrow, vrow
if vRow < hRow {
vRow, hRow = hRow, vRow
}

formatSet, _ := parseAutoFilterSet(format)
cellStart, _ := CoordinatesToCellName(hcol, hrow, true)
cellEnd, _ := CoordinatesToCellName(vcol, vrow, true)
cellStart, _ := CoordinatesToCellName(hCol, hRow, true)
cellEnd, _ := CoordinatesToCellName(vCol, vRow, true)
ref, filterDB := cellStart+":"+cellEnd, "_xlnm._FilterDatabase"
wb := f.workbookReader()
sheetID := f.GetSheetIndex(sheet)
filterRange := fmt.Sprintf("%s!%s", sheet, ref)
filterRange := fmt.Sprintf("'%s'!%s", sheet, ref)
d := xlsxDefinedName{
Name: filterDB,
Hidden: true,
Expand All @@ -324,8 +324,8 @@ func (f *File) AutoFilter(sheet, hcell, vcell, format string) error {
wb.DefinedNames.DefinedName = append(wb.DefinedNames.DefinedName, d)
}
}
refRange := vcol - hcol
return f.autoFilter(sheet, ref, refRange, hcol, formatSet)
refRange := vCol - hCol
return f.autoFilter(sheet, ref, refRange, hCol, formatSet)
}

// autoFilter provides a function to extract the tokens from the filter
Expand Down Expand Up @@ -383,7 +383,7 @@ func (f *File) writeAutoFilter(filter *xlsxAutoFilter, exp []int, tokens []strin
filter.FilterColumn[0].Filters = &xlsxFilters{Filter: filters}
} else if len(exp) == 3 && exp[0] == 2 && exp[1] == 1 && exp[2] == 2 {
// Double equality with "or" operator.
filters := []*xlsxFilter{}
var filters []*xlsxFilter
for _, v := range tokens {
filters = append(filters, &xlsxFilter{Val: v})
}
Expand Down Expand Up @@ -419,7 +419,7 @@ func (f *File) writeCustomFilter(filter *xlsxAutoFilter, operator int, val strin
if filter.FilterColumn[0].CustomFilters != nil {
filter.FilterColumn[0].CustomFilters.CustomFilter = append(filter.FilterColumn[0].CustomFilters.CustomFilter, &customFilter)
} else {
customFilters := []*xlsxCustomFilter{}
var customFilters []*xlsxCustomFilter
customFilters = append(customFilters, &customFilter)
filter.FilterColumn[0].CustomFilters = &xlsxCustomFilters{CustomFilter: customFilters}
}
Expand All @@ -435,8 +435,8 @@ func (f *File) writeCustomFilter(filter *xlsxAutoFilter, operator int, val strin
// ('x', '>', 2000, 'and', 'x', '<', 5000) -> exp1 and exp2
//
func (f *File) parseFilterExpression(expression string, tokens []string) ([]int, []string, error) {
expressions := []int{}
t := []string{}
var expressions []int
var t []string
if len(tokens) == 7 {
// The number of tokens will be either 3 (for 1 expression) or 7 (for 2
// expressions).
Expand Down
31 changes: 12 additions & 19 deletions templates.go
Original file line number Diff line number Diff line change
@@ -1,35 +1,28 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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.
//
// This file contains default templates for XML files we don't yet populated
// based on content.

package excelize

import "encoding/xml"

var (
// XMLHeaderByte define an XML declaration can also contain a standalone
// declaration.
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"
defaultXMLPathDocPropsApp = "docProps/app.xml"
defaultXMLPathDocPropsCore = "docProps/core.xml"
defaultXMLPathCalcChain = "xl/calcChain.xml"
defaultXMLPathSharedStrings = "xl/sharedStrings.xml"
defaultXMLPathStyles = "xl/styles.xml"
defaultXMLPathWorkbook = "xl/workbook.xml"
defaultTempFileSST = "sharedStrings"
)

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>`
Expand Down
14 changes: 7 additions & 7 deletions vmlDrawing.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

Expand Down
176 changes: 176 additions & 0 deletions workbook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

import (
"bytes"
"encoding/xml"
"io"
"log"
"path/filepath"
"strconv"
"strings"
)

// WorkbookPrOption is an option of a view of a workbook. See SetWorkbookPrOptions().
type WorkbookPrOption interface {
setWorkbookPrOption(pr *xlsxWorkbookPr)
}

// WorkbookPrOptionPtr is a writable WorkbookPrOption. See GetWorkbookPrOptions().
type WorkbookPrOptionPtr interface {
WorkbookPrOption
getWorkbookPrOption(pr *xlsxWorkbookPr)
}

type (
// FilterPrivacy is an option used for WorkbookPrOption
FilterPrivacy bool
)

// setWorkbook update workbook property of the spreadsheet. Maximum 31
// characters are allowed in sheet title.
func (f *File) setWorkbook(name string, sheetID, rid int) {
content := f.workbookReader()
content.Sheets.Sheet = append(content.Sheets.Sheet, xlsxSheet{
Name: trimSheetName(name),
SheetID: sheetID,
ID: "rId" + strconv.Itoa(rid),
})
}

// getWorkbookPath provides a function to get the path of the workbook.xml in
// the spreadsheet.
func (f *File) getWorkbookPath() (path string) {
if rels := f.relsReader("_rels/.rels"); rels != nil {
rels.Lock()
defer rels.Unlock()
for _, rel := range rels.Relationships {
if rel.Type == SourceRelationshipOfficeDocument {
path = strings.TrimPrefix(rel.Target, "/")
return
}
}
}
return
}

// getWorkbookRelsPath provides a function to get the path of the workbook.xml.rels
// in the spreadsheet.
func (f *File) getWorkbookRelsPath() (path string) {
wbPath := f.getWorkbookPath()
wbDir := filepath.Dir(wbPath)
if wbDir == "." {
path = "_rels/" + filepath.Base(wbPath) + ".rels"
return
}
path = strings.TrimPrefix(filepath.Dir(wbPath)+"/_rels/"+filepath.Base(wbPath)+".rels", "/")
return
}

// workbookReader provides a function to get the pointer to the workbook.xml
// structure after deserialization.
func (f *File) workbookReader() *xlsxWorkbook {
var err error
if f.WorkBook == nil {
wbPath := f.getWorkbookPath()
f.WorkBook = new(xlsxWorkbook)
if _, ok := f.xmlAttr[wbPath]; !ok {
d := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(wbPath))))
f.xmlAttr[wbPath] = append(f.xmlAttr[wbPath], getRootElement(d)...)
f.addNameSpaces(wbPath, SourceRelationship)
}
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(wbPath)))).
Decode(f.WorkBook); err != nil && err != io.EOF {
log.Printf("xml decode error: %s", err)
}
}
return f.WorkBook
}

// workBookWriter provides a function to save workbook.xml after serialize
// structure.
func (f *File) workBookWriter() {
if f.WorkBook != nil {
if f.WorkBook.DecodeAlternateContent != nil {
f.WorkBook.AlternateContent = &xlsxAlternateContent{
Content: f.WorkBook.DecodeAlternateContent.Content,
XMLNSMC: SourceRelationshipCompatibility.Value,
}
}
f.WorkBook.DecodeAlternateContent = nil
output, _ := xml.Marshal(f.WorkBook)
f.saveFileList(f.getWorkbookPath(), replaceRelationshipsBytes(f.replaceNameSpaceBytes(f.getWorkbookPath(), output)))
}
}

// SetWorkbookPrOptions provides a function to sets workbook properties.
//
// Available options:
// FilterPrivacy(bool)
// CodeName(string)
func (f *File) SetWorkbookPrOptions(opts ...WorkbookPrOption) error {
wb := f.workbookReader()
pr := wb.WorkbookPr
if pr == nil {
pr = new(xlsxWorkbookPr)
wb.WorkbookPr = pr
}
for _, opt := range opts {
opt.setWorkbookPrOption(pr)
}
return nil
}

// setWorkbookPrOption implements the WorkbookPrOption interface.
func (o FilterPrivacy) setWorkbookPrOption(pr *xlsxWorkbookPr) {
pr.FilterPrivacy = bool(o)
}

// setWorkbookPrOption implements the WorkbookPrOption interface.
func (o CodeName) setWorkbookPrOption(pr *xlsxWorkbookPr) {
pr.CodeName = string(o)
}

// GetWorkbookPrOptions provides a function to gets workbook properties.
//
// Available options:
// FilterPrivacy(bool)
// CodeName(string)
func (f *File) GetWorkbookPrOptions(opts ...WorkbookPrOptionPtr) error {
wb := f.workbookReader()
pr := wb.WorkbookPr
for _, opt := range opts {
opt.getWorkbookPrOption(pr)
}
return nil
}

// getWorkbookPrOption implements the WorkbookPrOption interface and get the
// filter privacy of thw workbook.
func (o *FilterPrivacy) getWorkbookPrOption(pr *xlsxWorkbookPr) {
if pr == nil {
*o = false
return
}
*o = FilterPrivacy(pr.FilterPrivacy)
}

// getWorkbookPrOption implements the WorkbookPrOption interface and get the
// code name of thw workbook.
func (o *CodeName) getWorkbookPrOption(pr *xlsxWorkbookPr) {
if pr == nil {
*o = ""
return
}
*o = CodeName(pr.CodeName)
}
57 changes: 57 additions & 0 deletions workbook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package excelize

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
)

func ExampleFile_SetWorkbookPrOptions() {
f := NewFile()
if err := f.SetWorkbookPrOptions(
FilterPrivacy(false),
CodeName("code"),
); err != nil {
fmt.Println(err)
}
// Output:
}

func ExampleFile_GetWorkbookPrOptions() {
f := NewFile()
var (
filterPrivacy FilterPrivacy
codeName CodeName
)
if err := f.GetWorkbookPrOptions(&filterPrivacy); err != nil {
fmt.Println(err)
}
if err := f.GetWorkbookPrOptions(&codeName); err != nil {
fmt.Println(err)
}
fmt.Println("Defaults:")
fmt.Printf("- filterPrivacy: %t\n", filterPrivacy)
fmt.Printf("- codeName: %q\n", codeName)
// Output:
// Defaults:
// - filterPrivacy: true
// - codeName: ""
}

func TestWorkbookPr(t *testing.T) {
f := NewFile()
wb := f.workbookReader()
wb.WorkbookPr = nil
var codeName CodeName
assert.NoError(t, f.GetWorkbookPrOptions(&codeName))
assert.Equal(t, "", string(codeName))
assert.NoError(t, f.SetWorkbookPrOptions(CodeName("code")))
assert.NoError(t, f.GetWorkbookPrOptions(&codeName))
assert.Equal(t, "code", string(codeName))

wb.WorkbookPr = nil
var filterPrivacy FilterPrivacy
assert.NoError(t, f.GetWorkbookPrOptions(&filterPrivacy))
assert.Equal(t, false, bool(filterPrivacy))
}
14 changes: 7 additions & 7 deletions xmlApp.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

Expand Down
18 changes: 9 additions & 9 deletions xmlCalcChain.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

Expand Down Expand Up @@ -66,13 +66,13 @@ type xlsxCalcChain struct {
// | same dependency level. Child chains are series of
// | calculations that can be independently farmed out to
// | other threads or processors.The possible values for
// | this attribute are defined by the W3C XML Schema
// | this attribute is defined by the W3C XML Schema
// | boolean datatype.
// |
// t (New Thread) | A Boolean flag indicating whether the cell's formula
// | starts a new thread. True if the cell's formula starts
// | a new thread, false otherwise.The possible values for
// | this attribute are defined by the W3C XML Schema
// | this attribute is defined by the W3C XML Schema
// | boolean datatype.
//
type xlsxCalcChainC struct {
Expand Down
50 changes: 25 additions & 25 deletions xmlChart.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// 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 providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX 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

Expand Down Expand Up @@ -520,23 +520,23 @@ type cPageMargins struct {

// formatChartAxis directly maps the format settings of the chart axis.
type formatChartAxis struct {
None bool `json:"none"`
Crossing string `json:"crossing"`
MajorGridlines bool `json:"major_grid_lines"`
MinorGridlines bool `json:"minor_grid_lines"`
MajorTickMark string `json:"major_tick_mark"`
MinorTickMark string `json:"minor_tick_mark"`
MinorUnitType string `json:"minor_unit_type"`
MajorUnit float64 `json:"major_unit"`
MajorUnitType string `json:"major_unit_type"`
TickLabelSkip int `json:"tick_label_skip"`
DisplayUnits string `json:"display_units"`
DisplayUnitsVisible bool `json:"display_units_visible"`
DateAxis bool `json:"date_axis"`
ReverseOrder bool `json:"reverse_order"`
Maximum float64 `json:"maximum"`
Minimum float64 `json:"minimum"`
NumFormat string `json:"num_format"`
None bool `json:"none"`
Crossing string `json:"crossing"`
MajorGridlines bool `json:"major_grid_lines"`
MinorGridlines bool `json:"minor_grid_lines"`
MajorTickMark string `json:"major_tick_mark"`
MinorTickMark string `json:"minor_tick_mark"`
MinorUnitType string `json:"minor_unit_type"`
MajorUnit float64 `json:"major_unit"`
MajorUnitType string `json:"major_unit_type"`
TickLabelSkip int `json:"tick_label_skip"`
DisplayUnits string `json:"display_units"`
DisplayUnitsVisible bool `json:"display_units_visible"`
DateAxis bool `json:"date_axis"`
ReverseOrder bool `json:"reverse_order"`
Maximum *float64 `json:"maximum"`
Minimum *float64 `json:"minimum"`
NumFormat string `json:"num_format"`
NumFont struct {
Color string `json:"color"`
Bold bool `json:"bold"`
Expand Down Expand Up @@ -599,7 +599,7 @@ type formatChart struct {
ShowBlanksAs string `json:"show_blanks_as"`
ShowHiddenData bool `json:"show_hidden_data"`
SetRotation int `json:"set_rotation"`
SetHoleSize int `json:"set_hole_size"`
HoleSize int `json:"hole_size"`
order int
}

Expand Down
Loading