forked from p30928647/excelize
Breaking changes: changed the function signature for 4 exported functions
- Change `func (f *File) AddVBAProject(bin string) error` to `func (f *File) AddVBAProject(file []byte) error` - Change `func (f *File) GetComments() (map[string][]Comment, error)` to `func (f *File) GetComments(sheet string) ([]Comment, error)` - Change `func (f *File) AddTable(sheet, rangeRef string, opts *TableOptions) error` to `func (f *File) AddTable(sheet string, table *Table) error` - Change `func (sw *StreamWriter) AddTable(rangeRef string, opts *TableOptions) error` to `func (sw *StreamWriter) AddTable(table *Table) error` - Rename exported data type `TableOptions` to `Table` - Simplify the assert statements in the unit tests - Update documents for the functions - Update unit tests
This commit is contained in:
parent
5878fbd282
commit
60b9d029a6
|
@ -11,7 +11,7 @@ import (
|
|||
func TestAdjustMergeCells(t *testing.T) {
|
||||
f := NewFile()
|
||||
// Test adjustAutoFilter with illegal cell reference
|
||||
assert.EqualError(t, f.adjustMergeCells(&xlsxWorksheet{
|
||||
assert.Equal(t, f.adjustMergeCells(&xlsxWorksheet{
|
||||
MergeCells: &xlsxMergeCells{
|
||||
Cells: []*xlsxMergeCell{
|
||||
{
|
||||
|
@ -19,8 +19,8 @@ func TestAdjustMergeCells(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
}, rows, 0, 0), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||
assert.EqualError(t, f.adjustMergeCells(&xlsxWorksheet{
|
||||
}, rows, 0, 0), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")))
|
||||
assert.Equal(t, f.adjustMergeCells(&xlsxWorksheet{
|
||||
MergeCells: &xlsxMergeCells{
|
||||
Cells: []*xlsxMergeCell{
|
||||
{
|
||||
|
@ -28,7 +28,7 @@ func TestAdjustMergeCells(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
}, rows, 0, 0), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
||||
}, rows, 0, 0), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")))
|
||||
assert.NoError(t, f.adjustMergeCells(&xlsxWorksheet{
|
||||
MergeCells: &xlsxMergeCells{
|
||||
Cells: []*xlsxMergeCell{
|
||||
|
@ -272,7 +272,7 @@ func TestAdjustMergeCells(t *testing.T) {
|
|||
}
|
||||
for _, c := range cases {
|
||||
assert.NoError(t, f.adjustMergeCells(c.ws, c.dir, c.num, -1))
|
||||
assert.Equal(t, 0, len(c.ws.MergeCells.Cells), c.label)
|
||||
assert.Len(t, c.ws.MergeCells.Cells, 0, c.label)
|
||||
}
|
||||
|
||||
f = NewFile()
|
||||
|
@ -293,22 +293,23 @@ func TestAdjustAutoFilter(t *testing.T) {
|
|||
},
|
||||
}, rows, 1, -1))
|
||||
// Test adjustAutoFilter with illegal cell reference
|
||||
assert.EqualError(t, f.adjustAutoFilter(&xlsxWorksheet{
|
||||
assert.Equal(t, f.adjustAutoFilter(&xlsxWorksheet{
|
||||
AutoFilter: &xlsxAutoFilter{
|
||||
Ref: "A:B1",
|
||||
},
|
||||
}, rows, 0, 0), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||
assert.EqualError(t, f.adjustAutoFilter(&xlsxWorksheet{
|
||||
}, rows, 0, 0), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")))
|
||||
assert.Equal(t, f.adjustAutoFilter(&xlsxWorksheet{
|
||||
AutoFilter: &xlsxAutoFilter{
|
||||
Ref: "A1:B",
|
||||
},
|
||||
}, rows, 0, 0), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
||||
}, rows, 0, 0), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")))
|
||||
}
|
||||
|
||||
func TestAdjustTable(t *testing.T) {
|
||||
f, sheetName := NewFile(), "Sheet1"
|
||||
for idx, reference := range []string{"B2:C3", "E3:F5", "H5:H8", "J5:K9"} {
|
||||
assert.NoError(t, f.AddTable(sheetName, reference, &TableOptions{
|
||||
assert.NoError(t, f.AddTable(sheetName, &Table{
|
||||
Range: reference,
|
||||
Name: fmt.Sprintf("table%d", idx),
|
||||
StyleName: "TableStyleMedium2",
|
||||
ShowFirstColumn: true,
|
||||
|
@ -323,7 +324,7 @@ func TestAdjustTable(t *testing.T) {
|
|||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAdjustTable.xlsx")))
|
||||
|
||||
f = NewFile()
|
||||
assert.NoError(t, f.AddTable(sheetName, "A1:D5", nil))
|
||||
assert.NoError(t, f.AddTable(sheetName, &Table{Range: "A1:D5"}))
|
||||
// Test adjust table with non-table part
|
||||
f.Pkg.Delete("xl/tables/table1.xml")
|
||||
assert.NoError(t, f.RemoveRow(sheetName, 1))
|
||||
|
@ -346,8 +347,8 @@ func TestAdjustHelper(t *testing.T) {
|
|||
AutoFilter: &xlsxAutoFilter{Ref: "A1:B"},
|
||||
})
|
||||
// Test adjustHelper with illegal cell reference
|
||||
assert.EqualError(t, f.adjustHelper("Sheet1", rows, 0, 0), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||
assert.EqualError(t, f.adjustHelper("Sheet2", rows, 0, 0), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
||||
assert.Equal(t, f.adjustHelper("Sheet1", rows, 0, 0), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")))
|
||||
assert.Equal(t, f.adjustHelper("Sheet2", rows, 0, 0), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")))
|
||||
// Test adjustHelper on not exists worksheet
|
||||
assert.EqualError(t, f.adjustHelper("SheetN", rows, 0, 0), "sheet SheetN does not exist")
|
||||
}
|
||||
|
@ -363,7 +364,7 @@ func TestAdjustCalcChain(t *testing.T) {
|
|||
assert.NoError(t, f.InsertRows("Sheet1", 1, 1))
|
||||
|
||||
f.CalcChain.C[1].R = "invalid coordinates"
|
||||
assert.EqualError(t, f.InsertCols("Sheet1", "A", 1), newCellNameToCoordinatesError("invalid coordinates", newInvalidCellNameError("invalid coordinates")).Error())
|
||||
assert.Equal(t, f.InsertCols("Sheet1", "A", 1), newCellNameToCoordinatesError("invalid coordinates", newInvalidCellNameError("invalid coordinates")))
|
||||
f.CalcChain = nil
|
||||
assert.NoError(t, f.InsertCols("Sheet1", "A", 1))
|
||||
}
|
||||
|
|
4
cell.go
4
cell.go
|
@ -694,8 +694,8 @@ type FormulaOpts struct {
|
|||
// return
|
||||
// }
|
||||
// }
|
||||
// if err := f.AddTable("Sheet1", "A1:C2", &excelize.TableOptions{
|
||||
// Name: "Table1", StyleName: "TableStyleMedium2",
|
||||
// if err := f.AddTable("Sheet1", &excelize.Table{
|
||||
// Range: "A1:C2", Name: "Table1", StyleName: "TableStyleMedium2",
|
||||
// }); err != nil {
|
||||
// fmt.Println(err)
|
||||
// return
|
||||
|
|
|
@ -571,7 +571,7 @@ func TestSetCellFormula(t *testing.T) {
|
|||
for idx, row := range [][]interface{}{{"A", "B", "C"}, {1, 2}} {
|
||||
assert.NoError(t, f.SetSheetRow("Sheet1", fmt.Sprintf("A%d", idx+1), &row))
|
||||
}
|
||||
assert.NoError(t, f.AddTable("Sheet1", "A1:C2", &TableOptions{Name: "Table1", StyleName: "TableStyleMedium2"}))
|
||||
assert.NoError(t, f.AddTable("Sheet1", &Table{Range: "A1:C2", Name: "Table1", StyleName: "TableStyleMedium2"}))
|
||||
formulaType = STCellFormulaTypeDataTable
|
||||
assert.NoError(t, f.SetCellFormula("Sheet1", "C2", "=SUM(Table1[[A]:[B]])", FormulaOpts{Type: &formulaType}))
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellFormula6.xlsx")))
|
||||
|
|
75
comment.go
75
comment.go
|
@ -21,46 +21,43 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// GetComments retrieves all comments and returns a map of worksheet name to
|
||||
// the worksheet comments.
|
||||
func (f *File) GetComments() (map[string][]Comment, error) {
|
||||
comments := map[string][]Comment{}
|
||||
for n, path := range f.sheetMap {
|
||||
target := f.getSheetComments(filepath.Base(path))
|
||||
if target == "" {
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(target, "/") {
|
||||
target = "xl" + strings.TrimPrefix(target, "..")
|
||||
}
|
||||
cmts, err := f.commentsReader(strings.TrimPrefix(target, "/"))
|
||||
if err != nil {
|
||||
return comments, err
|
||||
}
|
||||
if cmts != nil {
|
||||
var sheetComments []Comment
|
||||
for _, comment := range cmts.CommentList.Comment {
|
||||
sheetComment := Comment{}
|
||||
if comment.AuthorID < len(cmts.Authors.Author) {
|
||||
sheetComment.Author = cmts.Authors.Author[comment.AuthorID]
|
||||
}
|
||||
sheetComment.Cell = comment.Ref
|
||||
sheetComment.AuthorID = comment.AuthorID
|
||||
if comment.Text.T != nil {
|
||||
sheetComment.Text += *comment.Text.T
|
||||
}
|
||||
for _, text := range comment.Text.R {
|
||||
if text.T != nil {
|
||||
run := RichTextRun{Text: text.T.Val}
|
||||
if text.RPr != nil {
|
||||
run.Font = newFont(text.RPr)
|
||||
}
|
||||
sheetComment.Runs = append(sheetComment.Runs, run)
|
||||
}
|
||||
}
|
||||
sheetComments = append(sheetComments, sheetComment)
|
||||
// GetComments retrieves all comments in a worksheet by given worksheet name.
|
||||
func (f *File) GetComments(sheet string) ([]Comment, error) {
|
||||
var comments []Comment
|
||||
sheetXMLPath, ok := f.getSheetXMLPath(sheet)
|
||||
if !ok {
|
||||
return comments, newNoExistSheetError(sheet)
|
||||
}
|
||||
commentsXML := f.getSheetComments(filepath.Base(sheetXMLPath))
|
||||
if !strings.HasPrefix(commentsXML, "/") {
|
||||
commentsXML = "xl" + strings.TrimPrefix(commentsXML, "..")
|
||||
}
|
||||
commentsXML = strings.TrimPrefix(commentsXML, "/")
|
||||
cmts, err := f.commentsReader(commentsXML)
|
||||
if err != nil {
|
||||
return comments, err
|
||||
}
|
||||
if cmts != nil {
|
||||
for _, cmt := range cmts.CommentList.Comment {
|
||||
comment := Comment{}
|
||||
if cmt.AuthorID < len(cmts.Authors.Author) {
|
||||
comment.Author = cmts.Authors.Author[cmt.AuthorID]
|
||||
}
|
||||
comments[n] = sheetComments
|
||||
comment.Cell = cmt.Ref
|
||||
comment.AuthorID = cmt.AuthorID
|
||||
if cmt.Text.T != nil {
|
||||
comment.Text += *cmt.Text.T
|
||||
}
|
||||
for _, text := range cmt.Text.R {
|
||||
if text.T != nil {
|
||||
run := RichTextRun{Text: text.T.Val}
|
||||
if text.RPr != nil {
|
||||
run.Font = newFont(text.RPr)
|
||||
}
|
||||
comment.Runs = append(comment.Runs, run)
|
||||
}
|
||||
}
|
||||
comments = append(comments, comment)
|
||||
}
|
||||
}
|
||||
return comments, nil
|
||||
|
|
|
@ -34,21 +34,25 @@ func TestAddComment(t *testing.T) {
|
|||
assert.EqualError(t, f.AddComment("SheetN", Comment{Cell: "B7", Author: "Excelize", Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment."}}}), "sheet SheetN does not exist")
|
||||
// Test add comment on with illegal cell reference
|
||||
assert.EqualError(t, f.AddComment("Sheet1", Comment{Cell: "A", Author: "Excelize", Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment."}}}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||
comments, err := f.GetComments()
|
||||
comments, err := f.GetComments("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
if assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddComments.xlsx"))) {
|
||||
assert.Len(t, comments, 2)
|
||||
}
|
||||
assert.Len(t, comments, 2)
|
||||
comments, err = f.GetComments("Sheet2")
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, comments, 1)
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddComments.xlsx")))
|
||||
|
||||
f.Comments["xl/comments2.xml"] = nil
|
||||
f.Pkg.Store("xl/comments2.xml", []byte(xml.Header+`<comments xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><authors><author>Excelize: </author></authors><commentList><comment ref="B7" authorId="0"><text><t>Excelize: </t></text></comment></commentList></comments>`))
|
||||
comments, err = f.GetComments()
|
||||
comments, err = f.GetComments("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 2, len(comments["Sheet1"]))
|
||||
assert.EqualValues(t, 1, len(comments["Sheet2"]))
|
||||
comments, err = NewFile().GetComments()
|
||||
assert.Len(t, comments, 2)
|
||||
comments, err = f.GetComments("Sheet2")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, len(comments), 0)
|
||||
assert.Len(t, comments, 1)
|
||||
comments, err = NewFile().GetComments("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, comments, 0)
|
||||
|
||||
// Test add comments with invalid sheet name
|
||||
assert.EqualError(t, f.AddComment("Sheet:1", Comment{Cell: "A1", Author: "Excelize", Text: "This is a comment."}), ErrSheetNameInvalid.Error())
|
||||
|
@ -56,7 +60,7 @@ func TestAddComment(t *testing.T) {
|
|||
// Test add comments with unsupported charset
|
||||
f.Comments["xl/comments2.xml"] = nil
|
||||
f.Pkg.Store("xl/comments2.xml", MacintoshCyrillicCharset)
|
||||
_, err = f.GetComments()
|
||||
_, err = f.GetComments("Sheet2")
|
||||
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||
|
||||
// Test add comments with unsupported charset
|
||||
|
@ -68,6 +72,11 @@ func TestAddComment(t *testing.T) {
|
|||
f.Styles = nil
|
||||
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
|
||||
assert.EqualError(t, f.AddComment("Sheet2", Comment{Cell: "A30", Text: "Comment"}), "XML syntax error on line 1: invalid UTF-8")
|
||||
|
||||
// Test get comments on not exists worksheet
|
||||
comments, err = f.GetComments("SheetN")
|
||||
assert.Len(t, comments, 0)
|
||||
assert.EqualError(t, err, "sheet SheetN does not exist")
|
||||
}
|
||||
|
||||
func TestDeleteComment(t *testing.T) {
|
||||
|
@ -85,13 +94,13 @@ func TestDeleteComment(t *testing.T) {
|
|||
|
||||
assert.NoError(t, f.DeleteComment("Sheet2", "A40"))
|
||||
|
||||
comments, err := f.GetComments()
|
||||
comments, err := f.GetComments("Sheet2")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 5, len(comments["Sheet2"]))
|
||||
assert.Len(t, comments, 5)
|
||||
|
||||
comments, err = NewFile().GetComments()
|
||||
comments, err = NewFile().GetComments("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, len(comments), 0)
|
||||
assert.Len(t, comments, 0)
|
||||
|
||||
// Test delete comment with invalid sheet name
|
||||
assert.EqualError(t, f.DeleteComment("Sheet:1", "A1"), ErrSheetNameInvalid.Error())
|
||||
|
@ -99,9 +108,9 @@ func TestDeleteComment(t *testing.T) {
|
|||
assert.NoError(t, f.DeleteComment("Sheet2", "A41"))
|
||||
assert.NoError(t, f.DeleteComment("Sheet2", "C41"))
|
||||
assert.NoError(t, f.DeleteComment("Sheet2", "C42"))
|
||||
comments, err = f.GetComments()
|
||||
comments, err = f.GetComments("Sheet2")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 0, len(comments["Sheet2"]))
|
||||
assert.EqualValues(t, 0, len(comments))
|
||||
// Test delete comment on not exists worksheet
|
||||
assert.EqualError(t, f.DeleteComment("SheetN", "A1"), "sheet SheetN does not exist")
|
||||
// Test delete comment with worksheet part
|
||||
|
|
|
@ -35,7 +35,7 @@ func TestDataValidation(t *testing.T) {
|
|||
|
||||
dataValidations, err := f.GetDataValidations("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(dataValidations), 1)
|
||||
assert.Len(t, dataValidations, 1)
|
||||
|
||||
assert.NoError(t, f.SaveAs(resultFile))
|
||||
|
||||
|
@ -47,7 +47,7 @@ func TestDataValidation(t *testing.T) {
|
|||
|
||||
dataValidations, err = f.GetDataValidations("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(dataValidations), 2)
|
||||
assert.Len(t, dataValidations, 2)
|
||||
|
||||
assert.NoError(t, f.SaveAs(resultFile))
|
||||
|
||||
|
@ -62,10 +62,10 @@ func TestDataValidation(t *testing.T) {
|
|||
assert.NoError(t, f.AddDataValidation("Sheet2", dv))
|
||||
dataValidations, err = f.GetDataValidations("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(dataValidations), 2)
|
||||
assert.Len(t, dataValidations, 2)
|
||||
dataValidations, err = f.GetDataValidations("Sheet2")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(dataValidations), 1)
|
||||
assert.Len(t, dataValidations, 1)
|
||||
|
||||
dv = NewDataValidation(true)
|
||||
dv.Sqref = "A5:B6"
|
||||
|
@ -87,7 +87,7 @@ func TestDataValidation(t *testing.T) {
|
|||
|
||||
dataValidations, err = f.GetDataValidations("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(dataValidations), 3)
|
||||
assert.Len(t,dataValidations, 3)
|
||||
|
||||
// Test get data validation on no exists worksheet
|
||||
_, err = f.GetDataValidations("SheetN")
|
||||
|
|
|
@ -129,7 +129,7 @@ var (
|
|||
ErrInvalidFormula = errors.New("formula not valid")
|
||||
// ErrAddVBAProject defined the error message on add the VBA project in
|
||||
// the workbook.
|
||||
ErrAddVBAProject = errors.New("unsupported VBA project extension")
|
||||
ErrAddVBAProject = errors.New("unsupported VBA project")
|
||||
// ErrMaxRows defined the error message on receive a row number exceeds maximum limit.
|
||||
ErrMaxRows = errors.New("row number exceeds maximum limit")
|
||||
// ErrMaxRowHeight defined the error message on receive an invalid row
|
||||
|
|
23
excelize.go
23
excelize.go
|
@ -16,10 +16,8 @@ import (
|
|||
"archive/zip"
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -460,27 +458,33 @@ func (f *File) UpdateLinkedValue() error {
|
|||
}
|
||||
|
||||
// AddVBAProject provides the method to add vbaProject.bin file which contains
|
||||
// functions and/or macros. The file extension should be .xlsm. For example:
|
||||
// functions and/or macros. The file extension should be XLSM or XLTM. For
|
||||
// example:
|
||||
//
|
||||
// codeName := "Sheet1"
|
||||
// if err := f.SetSheetProps("Sheet1", &excelize.SheetPropsOptions{
|
||||
// CodeName: &codeName,
|
||||
// }); err != nil {
|
||||
// fmt.Println(err)
|
||||
// return
|
||||
// }
|
||||
// if err := f.AddVBAProject("vbaProject.bin"); err != nil {
|
||||
// file, err := os.ReadFile("vbaProject.bin")
|
||||
// if err != nil {
|
||||
// fmt.Println(err)
|
||||
// return
|
||||
// }
|
||||
// if err := f.AddVBAProject(file); err != nil {
|
||||
// fmt.Println(err)
|
||||
// return
|
||||
// }
|
||||
// if err := f.SaveAs("macros.xlsm"); err != nil {
|
||||
// fmt.Println(err)
|
||||
// return
|
||||
// }
|
||||
func (f *File) AddVBAProject(bin string) error {
|
||||
func (f *File) AddVBAProject(file []byte) error {
|
||||
var err error
|
||||
// Check vbaProject.bin exists first.
|
||||
if _, err = os.Stat(bin); os.IsNotExist(err) {
|
||||
return fmt.Errorf("stat %s: no such file or directory", bin)
|
||||
}
|
||||
if path.Ext(bin) != ".bin" {
|
||||
if !bytes.Contains(file, oleIdentifier) {
|
||||
return ErrAddVBAProject
|
||||
}
|
||||
rels, err := f.relsReader(f.getWorkbookRelsPath())
|
||||
|
@ -509,7 +513,6 @@ func (f *File) AddVBAProject(bin string) error {
|
|||
Type: SourceRelationshipVBAProject,
|
||||
})
|
||||
}
|
||||
file, _ := os.ReadFile(filepath.Clean(bin))
|
||||
f.Pkg.Store("xl/vbaProject.bin", file)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1319,8 +1319,8 @@ func TestProtectSheet(t *testing.T) {
|
|||
}))
|
||||
ws, err = f.workSheetReader(sheetName)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 24, len(ws.SheetProtection.SaltValue))
|
||||
assert.Equal(t, 88, len(ws.SheetProtection.HashValue))
|
||||
assert.Len(t, ws.SheetProtection.SaltValue, 24)
|
||||
assert.Len(t, ws.SheetProtection.HashValue, 88)
|
||||
assert.Equal(t, int(sheetProtectionSpinCount), ws.SheetProtection.SpinCount)
|
||||
// Test remove sheet protection with an incorrect password
|
||||
assert.EqualError(t, f.UnprotectSheet(sheetName, "wrongPassword"), ErrUnprotectSheetPassword.Error())
|
||||
|
@ -1387,8 +1387,8 @@ func TestProtectWorkbook(t *testing.T) {
|
|||
wb, err := f.workbookReader()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "SHA-512", wb.WorkbookProtection.WorkbookAlgorithmName)
|
||||
assert.Equal(t, 24, len(wb.WorkbookProtection.WorkbookSaltValue))
|
||||
assert.Equal(t, 88, len(wb.WorkbookProtection.WorkbookHashValue))
|
||||
assert.Len(t, wb.WorkbookProtection.WorkbookSaltValue, 24)
|
||||
assert.Len(t, wb.WorkbookProtection.WorkbookHashValue, 88)
|
||||
assert.Equal(t, int(workbookProtectionSpinCount), wb.WorkbookProtection.WorkbookSpinCount)
|
||||
|
||||
// Test protect workbook with password exceeds the limit length
|
||||
|
@ -1447,18 +1447,21 @@ func TestSetDefaultTimeStyle(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAddVBAProject(t *testing.T) {
|
||||
f := NewFile()
|
||||
assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{CodeName: stringPtr("Sheet1")}))
|
||||
assert.EqualError(t, f.AddVBAProject("macros.bin"), "stat macros.bin: no such file or directory")
|
||||
assert.EqualError(t, f.AddVBAProject(filepath.Join("test", "Book1.xlsx")), ErrAddVBAProject.Error())
|
||||
assert.NoError(t, f.AddVBAProject(filepath.Join("test", "vbaProject.bin")))
|
||||
// Test add VBA project twice
|
||||
assert.NoError(t, f.AddVBAProject(filepath.Join("test", "vbaProject.bin")))
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddVBAProject.xlsm")))
|
||||
// Test add VBA with unsupported charset workbook relationships
|
||||
f.Relationships.Delete(defaultXMLPathWorkbookRels)
|
||||
f.Pkg.Store(defaultXMLPathWorkbookRels, MacintoshCyrillicCharset)
|
||||
assert.EqualError(t, f.AddVBAProject(filepath.Join("test", "vbaProject.bin")), "XML syntax error on line 1: invalid UTF-8")
|
||||
f := NewFile()
|
||||
file, err := os.ReadFile(filepath.Join("test", "Book1.xlsx"))
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{CodeName: stringPtr("Sheet1")}))
|
||||
assert.EqualError(t, f.AddVBAProject(file), ErrAddVBAProject.Error())
|
||||
file, err = os.ReadFile(filepath.Join("test", "vbaProject.bin"))
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, f.AddVBAProject(file))
|
||||
// Test add VBA project twice
|
||||
assert.NoError(t, f.AddVBAProject(file))
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddVBAProject.xlsm")))
|
||||
// Test add VBA with unsupported charset workbook relationships
|
||||
f.Relationships.Delete(defaultXMLPathWorkbookRels)
|
||||
f.Pkg.Store(defaultXMLPathWorkbookRels, MacintoshCyrillicCharset)
|
||||
assert.EqualError(t, f.AddVBAProject(file), "XML syntax error on line 1: invalid UTF-8")
|
||||
}
|
||||
|
||||
func TestContentTypesReader(t *testing.T) {
|
||||
|
|
4
file.go
4
file.go
|
@ -39,12 +39,8 @@ func NewFile() *File {
|
|||
f.Pkg.Store(defaultXMLPathContentTypes, []byte(xml.Header+templateContentTypes))
|
||||
f.SheetCount = 1
|
||||
f.CalcChain, _ = f.calcChainReader()
|
||||
f.Comments = make(map[string]*xlsxComments)
|
||||
f.ContentTypes, _ = f.contentTypesReader()
|
||||
f.Drawings = sync.Map{}
|
||||
f.Styles, _ = f.stylesReader()
|
||||
f.DecodeVMLDrawing = make(map[string]*decodeVmlDrawing)
|
||||
f.VMLDrawing = make(map[string]*vmlDrawing)
|
||||
f.WorkBook, _ = f.workbookReader()
|
||||
f.Relationships = sync.Map{}
|
||||
rels, _ := f.relsReader(defaultXMLPathWorkbookRels)
|
||||
|
|
|
@ -274,7 +274,7 @@ func TestBytesReplace(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetRootElement(t *testing.T) {
|
||||
assert.Equal(t, 0, len(getRootElement(xml.NewDecoder(strings.NewReader("")))))
|
||||
assert.Len(t, getRootElement(xml.NewDecoder(strings.NewReader(""))), 0)
|
||||
}
|
||||
|
||||
func TestSetIgnorableNameSpace(t *testing.T) {
|
||||
|
|
|
@ -93,7 +93,7 @@ func TestMergeCellOverlap(t *testing.T) {
|
|||
}
|
||||
mc, err := f.GetMergeCells("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(mc))
|
||||
assert.Len(t, mc, 1)
|
||||
assert.Equal(t, "A1", mc[0].GetStartAxis())
|
||||
assert.Equal(t, "D3", mc[0].GetEndAxis())
|
||||
assert.Equal(t, "", mc[0].GetCellValue())
|
||||
|
|
2
shape.go
2
shape.go
|
@ -56,7 +56,7 @@ func parseShapeOptions(opts *Shape) (*Shape, error) {
|
|||
// &excelize.Shape{
|
||||
// Type: "rect",
|
||||
// Line: excelize.ShapeLine{Color: "4286F4", Width: &lineWidth},
|
||||
// Fill: excelize.Fill{Color: []string{"8EB9FF"}},
|
||||
// Fill: excelize.Fill{Color: []string{"8EB9FF"}, Pattern: 1},
|
||||
// Paragraph: []excelize.RichTextRun{
|
||||
// {
|
||||
// Text: "Rectangle Shape",
|
||||
|
|
|
@ -273,7 +273,7 @@ func TestDefinedName(t *testing.T) {
|
|||
Name: "Amount",
|
||||
}))
|
||||
assert.Exactly(t, "Sheet1!$A$2:$D$5", f.GetDefinedName()[0].RefersTo)
|
||||
assert.Exactly(t, 1, len(f.GetDefinedName()))
|
||||
assert.Len(t, f.GetDefinedName(), 1)
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDefinedName.xlsx")))
|
||||
// Test set defined name with unsupported charset workbook
|
||||
f.WorkBook = nil
|
||||
|
@ -376,7 +376,7 @@ func TestGetSheetMap(t *testing.T) {
|
|||
for idx, name := range sheetMap {
|
||||
assert.Equal(t, expectedMap[idx], name)
|
||||
}
|
||||
assert.Equal(t, len(sheetMap), 2)
|
||||
assert.Len(t, sheetMap, 2)
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
f = NewFile()
|
||||
|
|
15
stream.go
15
stream.go
|
@ -139,12 +139,13 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
|
|||
// AddTable creates an Excel table for the StreamWriter using the given
|
||||
// cell range and format set. For example, create a table of A1:D5:
|
||||
//
|
||||
// err := sw.AddTable("A1:D5", nil)
|
||||
// err := sw.AddTable(&excelize.Table{Range: "A1:D5"})
|
||||
//
|
||||
// Create a table of F2:H6 with format set:
|
||||
//
|
||||
// disable := false
|
||||
// err := sw.AddTable("F2:H6", &excelize.TableOptions{
|
||||
// err := sw.AddTable(&excelize.Table{
|
||||
// Range: "F2:H6",
|
||||
// Name: "table",
|
||||
// StyleName: "TableStyleMedium2",
|
||||
// ShowFirstColumn: true,
|
||||
|
@ -160,12 +161,12 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
|
|||
// called after the rows are written but before Flush.
|
||||
//
|
||||
// See File.AddTable for details on the table format.
|
||||
func (sw *StreamWriter) AddTable(rangeRef string, opts *TableOptions) error {
|
||||
options, err := parseTableOptions(opts)
|
||||
func (sw *StreamWriter) AddTable(table *Table) error {
|
||||
options, err := parseTableOptions(table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
coordinates, err := rangeRefToCoordinates(rangeRef)
|
||||
coordinates, err := rangeRefToCoordinates(options.Range)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -202,7 +203,7 @@ func (sw *StreamWriter) AddTable(rangeRef string, opts *TableOptions) error {
|
|||
name = "Table" + strconv.Itoa(tableID)
|
||||
}
|
||||
|
||||
table := xlsxTable{
|
||||
tbl := xlsxTable{
|
||||
XMLNS: NameSpaceSpreadSheet.Value,
|
||||
ID: tableID,
|
||||
Name: name,
|
||||
|
@ -237,7 +238,7 @@ func (sw *StreamWriter) AddTable(rangeRef string, opts *TableOptions) error {
|
|||
if err = sw.file.addContentTypePart(tableID, "table"); err != nil {
|
||||
return err
|
||||
}
|
||||
b, _ := xml.Marshal(table)
|
||||
b, _ := xml.Marshal(tbl)
|
||||
sw.file.saveFileList(tableXML, b)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -196,7 +196,7 @@ func TestStreamTable(t *testing.T) {
|
|||
streamWriter, err := file.NewStreamWriter("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
// Test add table without table header
|
||||
assert.EqualError(t, streamWriter.AddTable("A1:C2", nil), "XML syntax error on line 2: unexpected EOF")
|
||||
assert.EqualError(t, streamWriter.AddTable(&Table{Range: "A1:C2"}), "XML syntax error on line 2: unexpected EOF")
|
||||
// Write some rows. We want enough rows to force a temp file (>16MB)
|
||||
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"}))
|
||||
row := []interface{}{1, 2, 3}
|
||||
|
@ -205,7 +205,7 @@ func TestStreamTable(t *testing.T) {
|
|||
}
|
||||
|
||||
// Write a table
|
||||
assert.NoError(t, streamWriter.AddTable("A1:C2", nil))
|
||||
assert.NoError(t, streamWriter.AddTable(&Table{Range: "A1:C2"}))
|
||||
assert.NoError(t, streamWriter.Flush())
|
||||
|
||||
// Verify the table has names
|
||||
|
@ -217,17 +217,17 @@ func TestStreamTable(t *testing.T) {
|
|||
assert.Equal(t, "B", table.TableColumns.TableColumn[1].Name)
|
||||
assert.Equal(t, "C", table.TableColumns.TableColumn[2].Name)
|
||||
|
||||
assert.NoError(t, streamWriter.AddTable("A1:C1", nil))
|
||||
assert.NoError(t, streamWriter.AddTable(&Table{Range: "A1:C1"}))
|
||||
|
||||
// Test add table with illegal cell reference
|
||||
assert.EqualError(t, streamWriter.AddTable("A:B1", nil), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||
assert.EqualError(t, streamWriter.AddTable("A1:B", nil), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
||||
assert.EqualError(t, streamWriter.AddTable(&Table{Range: "A:B1"}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||
assert.EqualError(t, streamWriter.AddTable(&Table{Range: "A1:B"}), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
||||
// Test add table with invalid table name
|
||||
assert.EqualError(t, streamWriter.AddTable("A:B1", &TableOptions{Name: "1Table"}), newInvalidTableNameError("1Table").Error())
|
||||
assert.EqualError(t, streamWriter.AddTable(&Table{Range: "A:B1", Name: "1Table"}), newInvalidTableNameError("1Table").Error())
|
||||
// Test add table with unsupported charset content types
|
||||
file.ContentTypes = nil
|
||||
file.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||
assert.EqualError(t, streamWriter.AddTable("A1:C2", nil), "XML syntax error on line 1: invalid UTF-8")
|
||||
assert.EqualError(t, streamWriter.AddTable(&Table{Range: "A1:C2"}), "XML syntax error on line 1: invalid UTF-8")
|
||||
}
|
||||
|
||||
func TestStreamMergeCells(t *testing.T) {
|
||||
|
|
17
table.go
17
table.go
|
@ -23,10 +23,10 @@ import (
|
|||
|
||||
// parseTableOptions provides a function to parse the format settings of the
|
||||
// table with default value.
|
||||
func parseTableOptions(opts *TableOptions) (*TableOptions, error) {
|
||||
func parseTableOptions(opts *Table) (*Table, error) {
|
||||
var err error
|
||||
if opts == nil {
|
||||
return &TableOptions{ShowRowStripes: boolPtr(true)}, err
|
||||
return &Table{ShowRowStripes: boolPtr(true)}, err
|
||||
}
|
||||
if opts.ShowRowStripes == nil {
|
||||
opts.ShowRowStripes = boolPtr(true)
|
||||
|
@ -41,12 +41,13 @@ func parseTableOptions(opts *TableOptions) (*TableOptions, error) {
|
|||
// name, range reference and format set. For example, create a table of A1:D5
|
||||
// on Sheet1:
|
||||
//
|
||||
// err := f.AddTable("Sheet1", "A1:D5", nil)
|
||||
// err := f.AddTable("Sheet1", &excelize.Table{Range: "A1:D5"})
|
||||
//
|
||||
// Create a table of F2:H6 on Sheet2 with format set:
|
||||
//
|
||||
// disable := false
|
||||
// err := f.AddTable("Sheet2", "F2:H6", &excelize.TableOptions{
|
||||
// err := f.AddTable("Sheet2", &excelize.Table{
|
||||
// Range: "F2:H6",
|
||||
// Name: "table",
|
||||
// StyleName: "TableStyleMedium2",
|
||||
// ShowFirstColumn: true,
|
||||
|
@ -69,13 +70,13 @@ func parseTableOptions(opts *TableOptions) (*TableOptions, error) {
|
|||
// TableStyleLight1 - TableStyleLight21
|
||||
// TableStyleMedium1 - TableStyleMedium28
|
||||
// TableStyleDark1 - TableStyleDark11
|
||||
func (f *File) AddTable(sheet, rangeRef string, opts *TableOptions) error {
|
||||
options, err := parseTableOptions(opts)
|
||||
func (f *File) AddTable(sheet string, table *Table) error {
|
||||
options, err := parseTableOptions(table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Coordinate conversion, convert C1:B3 to 2,0,1,2.
|
||||
coordinates, err := rangeRefToCoordinates(rangeRef)
|
||||
coordinates, err := rangeRefToCoordinates(options.Range)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -187,7 +188,7 @@ func checkTableName(name string) error {
|
|||
|
||||
// addTable provides a function to add table by given worksheet name,
|
||||
// range reference and format set.
|
||||
func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, opts *TableOptions) error {
|
||||
func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, opts *Table) error {
|
||||
// Correct the minimum number of rows, the table at least two lines.
|
||||
if y1 == y2 {
|
||||
y2++
|
||||
|
|
|
@ -12,8 +12,9 @@ import (
|
|||
func TestAddTable(t *testing.T) {
|
||||
f, err := prepareTestBook1()
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, f.AddTable("Sheet1", "B26:A21", nil))
|
||||
assert.NoError(t, f.AddTable("Sheet2", "A2:B5", &TableOptions{
|
||||
assert.NoError(t, f.AddTable("Sheet1", &Table{Range: "B26:A21"}))
|
||||
assert.NoError(t, f.AddTable("Sheet2", &Table{
|
||||
Range: "A2:B5",
|
||||
Name: "table",
|
||||
StyleName: "TableStyleMedium2",
|
||||
ShowColumnStripes: true,
|
||||
|
@ -21,21 +22,24 @@ func TestAddTable(t *testing.T) {
|
|||
ShowLastColumn: true,
|
||||
ShowRowStripes: boolPtr(true),
|
||||
}))
|
||||
assert.NoError(t, f.AddTable("Sheet2", "D1:D11", &TableOptions{
|
||||
assert.NoError(t, f.AddTable("Sheet2", &Table{
|
||||
Range: "D1:D11",
|
||||
ShowHeaderRow: boolPtr(false),
|
||||
}))
|
||||
assert.NoError(t, f.AddTable("Sheet2", "F1:F1", &TableOptions{StyleName: "TableStyleMedium8"}))
|
||||
assert.NoError(t, f.AddTable("Sheet2", &Table{Range: "F1:F1", StyleName: "TableStyleMedium8"}))
|
||||
|
||||
// Test add table with invalid table options
|
||||
assert.Equal(t, f.AddTable("Sheet1", nil), ErrParameterInvalid)
|
||||
// Test add table in not exist worksheet
|
||||
assert.EqualError(t, f.AddTable("SheetN", "B26:A21", nil), "sheet SheetN does not exist")
|
||||
assert.EqualError(t, f.AddTable("SheetN", &Table{Range: "B26:A21"}), "sheet SheetN does not exist")
|
||||
// Test add table with illegal cell reference
|
||||
assert.EqualError(t, f.AddTable("Sheet1", "A:B1", nil), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
||||
assert.EqualError(t, f.AddTable("Sheet1", "A1:B", nil), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
||||
assert.Equal(t, f.AddTable("Sheet1", &Table{Range: "A:B1"}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")))
|
||||
assert.Equal(t, f.AddTable("Sheet1", &Table{Range: "A1:B"}), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")))
|
||||
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddTable.xlsx")))
|
||||
|
||||
// Test add table with invalid sheet name
|
||||
assert.EqualError(t, f.AddTable("Sheet:1", "B26:A21", nil), ErrSheetNameInvalid.Error())
|
||||
assert.EqualError(t, f.AddTable("Sheet:1", &Table{Range: "B26:A21"}), ErrSheetNameInvalid.Error())
|
||||
// Test addTable with illegal cell reference
|
||||
f = NewFile()
|
||||
assert.EqualError(t, f.addTable("sheet1", "", 0, 0, 0, 0, 0, nil), "invalid cell reference [0, 0]")
|
||||
|
@ -54,8 +58,9 @@ func TestAddTable(t *testing.T) {
|
|||
{name: "\u0f5f\u0fb3\u0f0b\u0f21", err: newInvalidTableNameError("\u0f5f\u0fb3\u0f0b\u0f21")},
|
||||
{name: strings.Repeat("c", MaxFieldLength+1), err: ErrTableNameLength},
|
||||
} {
|
||||
assert.EqualError(t, f.AddTable("Sheet1", "A1:B2", &TableOptions{
|
||||
Name: cases.name,
|
||||
assert.EqualError(t, f.AddTable("Sheet1", &Table{
|
||||
Range: "A1:B2",
|
||||
Name: cases.name,
|
||||
}), cases.err.Error())
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -196,8 +196,9 @@ type xlsxTableStyleInfo struct {
|
|||
ShowColumnStripes bool `xml:"showColumnStripes,attr"`
|
||||
}
|
||||
|
||||
// TableOptions directly maps the format settings of the table.
|
||||
type TableOptions struct {
|
||||
// Table directly maps the format settings of the table.
|
||||
type Table struct {
|
||||
Range string
|
||||
Name string
|
||||
StyleName string
|
||||
ShowColumnStripes bool
|
||||
|
|
Loading…
Reference in New Issue