diff --git a/excelize_test.go b/excelize_test.go index e4b25484..c7c3aec1 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -182,6 +182,11 @@ func TestSaveAsWrongPath(t *testing.T) { } } +func TestOpenReader(t *testing.T) { + _, err := OpenReader(strings.NewReader("")) + assert.EqualError(t, err, "zip: not a valid zip file") +} + func TestBrokenFile(t *testing.T) { // Test write file with broken file struct. f := File{} @@ -1033,22 +1038,6 @@ func TestHSL(t *testing.T) { t.Log(RGBToHSL(250, 50, 100)) } -func TestSearchSheet(t *testing.T) { - f, err := OpenFile(filepath.Join("test", "SharedStrings.xlsx")) - if !assert.NoError(t, err) { - t.FailNow() - } - - // Test search in a not exists worksheet. - t.Log(f.SearchSheet("Sheet4", "")) - // Test search a not exists value. - t.Log(f.SearchSheet("Sheet1", "X")) - t.Log(f.SearchSheet("Sheet1", "A")) - // Test search the coordinates where the numerical value in the range of - // "0-9" of Sheet1 is described by regular expression: - t.Log(f.SearchSheet("Sheet1", "[0-9]", true)) -} - func TestProtectSheet(t *testing.T) { f := NewFile() f.ProtectSheet("Sheet1", nil) diff --git a/sheet.go b/sheet.go index d3099fb4..c0eba561 100644 --- a/sheet.go +++ b/sheet.go @@ -704,18 +704,14 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) { var ( regSearch bool result []string - inElement string - r xlsxRow ) for _, r := range reg { regSearch = r } - xlsx, err := f.workSheetReader(sheet) if err != nil { return result, err } - name, ok := f.sheetMap[trimSheetName(sheet)] if !ok { return result, nil @@ -724,6 +720,17 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) { output, _ := xml.Marshal(f.Sheet[name]) f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output)) } + return f.searchSheet(name, value, regSearch) +} + +// searchSheet provides a function to get coordinates by given worksheet name, +// cell value, and regular expression. +func (f *File) searchSheet(name, value string, regSearch bool) ([]string, error) { + var ( + inElement string + result []string + r xlsxRow + ) xml.NewDecoder(bytes.NewReader(f.readXML(name))) d := f.sharedStringsReader() @@ -1213,6 +1220,71 @@ func (f *File) GetPageLayout(sheet string, opts ...PageLayoutOptionPtr) error { return err } +// SetDefinedName provides a function to set the defined names of the workbook +// or worksheet. If not specified scopr, the default scope is workbook. +// For example: +// +// f.SetDefinedName(&excelize.DefinedName{ +// Name: "Amount", +// RefersTo: "Sheet1!$A$2:$D$5", +// Comment: "defined name comment", +// Scope: "Sheet2", +// }) +// +func (f *File) SetDefinedName(definedName *DefinedName) error { + wb := f.workbookReader() + d := xlsxDefinedName{ + Name: definedName.Name, + Comment: definedName.Comment, + Data: definedName.RefersTo, + } + if definedName.Scope != "" { + if sheetID := f.GetSheetIndex(definedName.Scope); sheetID != 0 { + sheetID-- + d.LocalSheetID = &sheetID + } + } + if wb.DefinedNames != nil { + for _, dn := range wb.DefinedNames.DefinedName { + var scope string + if dn.LocalSheetID != nil { + scope = f.GetSheetName(*dn.LocalSheetID + 1) + } + if scope == definedName.Scope && dn.Name == definedName.Name { + return errors.New("the same name already exists on scope") + } + } + wb.DefinedNames.DefinedName = append(wb.DefinedNames.DefinedName, d) + return nil + } + wb.DefinedNames = &xlsxDefinedNames{ + DefinedName: []xlsxDefinedName{d}, + } + return nil +} + +// GetDefinedName provides a function to get the defined names of the workbook +// or worksheet. +func (f *File) GetDefinedName() []DefinedName { + var definedNames []DefinedName + wb := f.workbookReader() + if wb.DefinedNames != nil { + for _, dn := range wb.DefinedNames.DefinedName { + definedName := DefinedName{ + Name: dn.Name, + Comment: dn.Comment, + RefersTo: dn.Data, + Scope: "Workbook", + } + if dn.LocalSheetID != nil { + definedName.Scope = f.GetSheetName(*dn.LocalSheetID + 1) + } + definedNames = append(definedNames, definedName) + } + } + return definedNames +} + // workSheetRelsReader provides a function to get the pointer to the structure // after deserialization of xl/worksheets/_rels/sheet%d.xml.rels. func (f *File) workSheetRelsReader(path string) *xlsxWorkbookRels { diff --git a/sheet_test.go b/sheet_test.go index beee10bb..a7fd9e94 100644 --- a/sheet_test.go +++ b/sheet_test.go @@ -108,6 +108,29 @@ func TestPageLayoutOption(t *testing.T) { } } +func TestSearchSheet(t *testing.T) { + f, err := excelize.OpenFile(filepath.Join("test", "SharedStrings.xlsx")) + if !assert.NoError(t, err) { + t.FailNow() + } + // Test search in a not exists worksheet. + _, err = f.SearchSheet("Sheet4", "") + assert.EqualError(t, err, "sheet Sheet4 is not exist") + var expected []string + // Test search a not exists value. + result, err := f.SearchSheet("Sheet1", "X") + assert.NoError(t, err) + assert.EqualValues(t, expected, result) + result, err = f.SearchSheet("Sheet1", "A") + assert.NoError(t, err) + assert.EqualValues(t, []string{"A1"}, result) + // Test search the coordinates where the numerical value in the range of + // "0-9" of Sheet1 is described by regular expression: + result, err = f.SearchSheet("Sheet1", "[0-9]", true) + assert.NoError(t, err) + assert.EqualValues(t, expected, result) +} + func TestSetPageLayout(t *testing.T) { f := excelize.NewFile() // Test set page layout on not exists worksheet. @@ -142,3 +165,25 @@ func TestSetHeaderFooter(t *testing.T) { })) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetHeaderFooter.xlsx"))) } + +func TestDefinedName(t *testing.T) { + f := excelize.NewFile() + assert.NoError(t, f.SetDefinedName(&excelize.DefinedName{ + Name: "Amount", + RefersTo: "Sheet1!$A$2:$D$5", + Comment: "defined name comment", + Scope: "Sheet1", + })) + assert.NoError(t, f.SetDefinedName(&excelize.DefinedName{ + Name: "Amount", + RefersTo: "Sheet1!$A$2:$D$5", + Comment: "defined name comment", + })) + assert.EqualError(t, f.SetDefinedName(&excelize.DefinedName{ + Name: "Amount", + RefersTo: "Sheet1!$A$2:$D$5", + Comment: "defined name comment", + }), "the same name already exists on scope") + assert.Exactly(t, "Sheet1!$A$2:$D$5", f.GetDefinedName()[1].RefersTo) + assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDefinedName.xlsx"))) +} diff --git a/xmlWorkbook.go b/xmlWorkbook.go index 53849779..01186851 100644 --- a/xmlWorkbook.go +++ b/xmlWorkbook.go @@ -288,3 +288,12 @@ type xlsxCustomWorkbookView struct { XWindow *int `xml:"xWindow,attr"` YWindow *int `xml:"yWindow,attr"` } + +// DefinedName directly maps the name for a cell or cell range on a +// worksheet. +type DefinedName struct { + Name string + Comment string + RefersTo string + Scope string +}