From 01a418bda8502890e89ab20a2b41220372877bae Mon Sep 17 00:00:00 2001 From: xuri Date: Sat, 27 Apr 2019 23:40:57 +0800 Subject: Resolve #392, compatible with strict relations name space inspection --- sheet.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 32d12d1..5e9fcae 100644 --- a/sheet.go +++ b/sheet.go @@ -92,7 +92,7 @@ func (f *File) workbookReader() *xlsxWorkbook { func (f *File) workBookWriter() { if f.WorkBook != nil { output, _ := xml.Marshal(f.WorkBook) - f.saveFileList("xl/workbook.xml", replaceRelationshipsNameSpaceBytes(output)) + f.saveFileList("xl/workbook.xml", replaceRelationshipsBytes(replaceRelationshipsNameSpaceBytes(output))) } } @@ -105,7 +105,7 @@ func (f *File) workSheetWriter() { f.Sheet[p].SheetData.Row[k].C = trimCell(v.C) } output, _ := xml.Marshal(sheet) - f.saveFileList(p, replaceWorkSheetsRelationshipsNameSpaceBytes(output)) + f.saveFileList(p, replaceRelationshipsBytes(replaceWorkSheetsRelationshipsNameSpaceBytes(output))) ok := f.checked[p] if ok { f.checked[p] = false @@ -211,6 +211,15 @@ func (f *File) setAppXML() { f.saveFileList("docProps/app.xml", []byte(templateDocpropsApp)) } +// replaceRelationshipsBytes; Some tools that read XLSX files have very strict +// requirements about the structure of the input XML. This function is a +// horrible hack to fix that after the XML marshalling is completed. +func replaceRelationshipsBytes(content []byte) []byte { + oldXmlns := []byte(`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships`) + newXmlns := []byte("r") + return bytes.Replace(content, oldXmlns, newXmlns, -1) +} + // replaceRelationshipsNameSpaceBytes; Some tools that read XLSX files have // very strict requirements about the structure of the input XML. In // particular both Numbers on the Mac and SAS dislike inline XML namespace -- cgit v1.2.1 From 69b38ddcd60f7cf4c158c706ddbbeb89a8ff2108 Mon Sep 17 00:00:00 2001 From: xuri Date: Sun, 5 May 2019 16:25:57 +0800 Subject: Resolve #394, init set header and footer support --- sheet.go | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 2 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 5e9fcae..b22592d 100644 --- a/sheet.go +++ b/sheet.go @@ -14,9 +14,11 @@ import ( "encoding/json" "encoding/xml" "errors" + "fmt" "io/ioutil" "os" "path" + "reflect" "regexp" "strconv" "strings" @@ -466,7 +468,7 @@ func (f *File) CopySheet(from, to int) error { // copySheet provides a function to duplicate a worksheet by gave source and // target worksheet name. func (f *File) copySheet(from, to int) error { - sheet, err := f.workSheetReader("sheet" + strconv.Itoa(from)) + sheet, err := f.workSheetReader(f.GetSheetName(from)) if err != nil { return err } @@ -761,6 +763,155 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) { return result, nil } +// SetHeaderFooter provides a function to set headers and footers by given +// worksheet name and the control characters. +// +// Headers and footers are specified using the following settings fields: +// +// Fields | Description +// ------------------+----------------------------------------------------------- +// AlignWithMargins | Align header footer margins with page margins +// DifferentFirst | Different first-page header and footer indicator +// DifferentOddEven | Different odd and even page headers and footers indicator +// ScaleWithDoc | Scale header and footer with document scaling +// OddFooter | Odd Page Footer +// OddHeader | Odd Header +// EvenFooter | Even Page Footer +// EvenHeader | Even Page Header +// FirstFooter | First Page Footer +// FirstHeader | First Page Header +// +// The following formatting codes can be used in 6 string type fields: +// OddHeader, OddFooter, EvenHeader, EvenFooter, FirstFooter, FirstHeader +// +// Formatting Code | Description +// ------------------------+------------------------------------------------------------------------- +// && | The character "&" +// | +// &font-size | Size of the text font, where font-size is a decimal font size in points +// | +// &"font name,font type" | A text font-name string, font name, and a text font-type string, +// | font type +// | +// &"-,Regular" | Regular text format. Toggles bold and italic modes to off +// | +// &A | Current worksheet's tab name +// | +// &B or &"-,Bold" | Bold text format, from off to on, or vice versa. The default mode is off +// | +// &D | Current date +// | +// &C | Center section +// | +// &E | Double-underline text format +// | +// &F | Current workbook's file name +// | +// &G | Drawing object as background +// | +// &H | Shadow text format +// | +// &I or &"-,Italic" | Italic text format +// | +// &K | Text font color +// | +// | An RGB Color is specified as RRGGBB +// | +// | A Theme Color is specified as TTSNNN where TT is the theme color Id, +// | S is either "+" or "-" of the tint/shade value, and NNN is the +// | tint/shade value +// | +// &L | Left section +// | +// &N | Total number of pages +// | +// &O | Outline text format +// | +// &P[[+|-]n] | Without the optional suffix, the current page number in decimal +// | +// &R | Right section +// | +// &S | Strikethrough text format +// | +// &T | Current time +// | +// &U | Single-underline text format. If double-underline mode is on, the next +// | occurrence in a section specifier toggles double-underline mode to off; +// | otherwise, it toggles single-underline mode, from off to on, or vice +// | versa. The default mode is off +// | +// &X | Superscript text format +// | +// &Y | Subscript text format +// | +// &Z | Current workbook's file path +// +// For example: +// +// err := f.SetHeaderFooter("Sheet1", &excelize.FormatHeaderFooter{ +// DifferentFirst: true, +// DifferentOddEven: true, +// OddHeader: "&R&P", +// OddFooter: "&C&F", +// EvenHeader: "&L&P", +// EvenFooter: "&L&D&R&T", +// FirstHeader: `&CCenter &"-,Bold"Bold&"-,Regular"HeaderU+000A&D`, +// }) +// +// This example shows: +// +// - The first page has its own header and footer +// +// - Odd and even-numbered pages have different headers and footers +// +// - Current page number in the right section of odd-page headers +// +// - Current workbook's file name in the center section of odd-page footers +// +// - Current page number in the left section of even-page headers +// +// - Current date in the left section and the current time in the right section +// of even-page footers +// +// - The text "Center Bold Header" on the first line of the center section of +// the first page, and the date on the second line of the center section of +// that same page +// +// - No footer on the first page +// +func (f *File) SetHeaderFooter(sheet string, settings *FormatHeaderFooter) error { + xlsx, err := f.workSheetReader(sheet) + if err != nil { + return err + } + if settings == nil { + xlsx.HeaderFooter = nil + return err + } + + v := reflect.ValueOf(*settings) + // Check 6 string type fields: OddHeader, OddFooter, EvenHeader, EvenFooter, + // FirstFooter, FirstHeader + for i := 4; i < v.NumField()-1; i++ { + if v.Field(i).Len() >= 255 { + return fmt.Errorf("field %s must be less than 255 characters", v.Type().Field(i).Name) + } + } + xlsx.HeaderFooter = &xlsxHeaderFooter{ + AlignWithMargins: settings.AlignWithMargins, + DifferentFirst: settings.DifferentFirst, + DifferentOddEven: settings.DifferentOddEven, + ScaleWithDoc: settings.ScaleWithDoc, + OddHeader: settings.OddHeader, + OddFooter: settings.OddFooter, + EvenHeader: settings.EvenHeader, + EvenFooter: settings.EvenFooter, + FirstFooter: settings.FirstFooter, + FirstHeader: settings.FirstHeader, + } + return err +} + // ProtectSheet provides a function to prevent other users from accidentally // or deliberately changing, moving, or deleting data in a worksheet. For // example, protect Sheet1 with protection settings: @@ -898,7 +1049,7 @@ func (p *PageLayoutPaperSize) getPageLayout(ps *xlsxPageSetUp) { // // Available options: // PageLayoutOrientation(string) -// PageLayoutPaperSize(int) +// PageLayoutPaperSize(int) // // The following shows the paper size sorted by excelize index number: // -- cgit v1.2.1 From f91f548614a7182ce66d55d10ed311e9b7e08a2a Mon Sep 17 00:00:00 2001 From: xuri Date: Fri, 17 May 2019 22:58:12 +0800 Subject: Resolve #404, get sheet map by target rels. --- sheet.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index b22592d..e873118 100644 --- a/sheet.go +++ b/sheet.go @@ -369,12 +369,18 @@ func (f *File) GetSheetMap() map[int]string { return sheetMap } -// getSheetMap provides a function to get worksheet name and XML file path map of -// XLSX. +// getSheetMap provides a function to get worksheet name and XML file path map +// of XLSX. func (f *File) getSheetMap() map[string]string { - maps := make(map[string]string) - for idx, name := range f.GetSheetMap() { - maps[name] = "xl/worksheets/sheet" + strconv.Itoa(idx) + ".xml" + content := f.workbookReader() + rels := f.workbookRelsReader() + maps := map[string]string{} + for _, v := range content.Sheets.Sheet { + for _, rel := range rels.Relationships { + if rel.ID == v.ID { + maps[v.Name] = fmt.Sprintf("xl/%s", rel.Target) + } + } } return maps } -- cgit v1.2.1 From 421f945f51f254054991127758db0520cf0f5456 Mon Sep 17 00:00:00 2001 From: xuri Date: Sat, 8 Jun 2019 00:00:55 +0800 Subject: Fixed #418, #420, #421, init adjust calculation chain support Update testing case --- sheet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index e873118..d3099fb 100644 --- a/sheet.go +++ b/sheet.go @@ -527,7 +527,7 @@ func (f *File) SetSheetVisible(name string, visible bool) error { } } for k, v := range content.Sheets.Sheet { - xlsx, err := f.workSheetReader(f.GetSheetMap()[k]) + xlsx, err := f.workSheetReader(v.Name) if err != nil { return err } -- cgit v1.2.1 From a335be7e4e6824e65f3d8a34b7b45ffa8d78fe4b Mon Sep 17 00:00:00 2001 From: xuri Date: Tue, 18 Jun 2019 23:07:44 +0800 Subject: New functions: SetDefinedName and GetDefinedName added --- sheet.go | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 4 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index d3099fb..c0eba56 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 { -- cgit v1.2.1 From dc8210d4a7d18f6425f6f18dc383b26778883715 Mon Sep 17 00:00:00 2001 From: xuri Date: Sun, 30 Jun 2019 19:50:47 +0800 Subject: Update GoDoc and typo fixed --- sheet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index c0eba56..a43ca6b 100644 --- a/sheet.go +++ b/sheet.go @@ -1221,7 +1221,7 @@ func (f *File) GetPageLayout(sheet string, opts ...PageLayoutOptionPtr) error { } // SetDefinedName provides a function to set the defined names of the workbook -// or worksheet. If not specified scopr, the default scope is workbook. +// or worksheet. If not specified scope, the default scope is workbook. // For example: // // f.SetDefinedName(&excelize.DefinedName{ -- cgit v1.2.1 From 8b2d4cb697420e5daeb85e5d9593c563bd77db53 Mon Sep 17 00:00:00 2001 From: xuri Date: Wed, 3 Jul 2019 00:50:10 +0800 Subject: New feature: group and ungroup sheets support New functions `GroupSheets` and `UngroupSheets` added Refactor sheet index calculation --- sheet.go | 155 ++++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 109 insertions(+), 46 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index a43ca6b..d579c6a 100644 --- a/sheet.go +++ b/sheet.go @@ -275,13 +275,20 @@ func (f *File) SetActiveSheet(index int) { // GetActiveSheetIndex provides a function to get active sheet index of the // XLSX. If not found the active sheet will be return integer 0. func (f *File) GetActiveSheetIndex() int { - for idx, name := range f.GetSheetMap() { - xlsx, _ := f.workSheetReader(name) - for _, sheetView := range xlsx.SheetViews.SheetView { - if sheetView.TabSelected { - return idx + wb := f.workbookReader() + if wb != nil { + view := wb.BookViews.WorkBookView + sheets := wb.Sheets.Sheet + var activeTab int + if len(view) > 0 { + activeTab = view[0].ActiveTab + if len(sheets) > activeTab && sheets[activeTab].SheetID != 0 { + return sheets[activeTab].SheetID } } + if len(wb.Sheets.Sheet) == 1 { + return wb.Sheets.Sheet[0].SheetID + } } return 0 } @@ -308,34 +315,26 @@ func (f *File) SetSheetName(oldName, newName string) { // worksheet index. If given sheet index is invalid, will return an empty // string. func (f *File) GetSheetName(index int) string { - content := f.workbookReader() - rels := f.workbookRelsReader() - for _, rel := range rels.Relationships { - rID, _ := strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(rel.Target, "worksheets/sheet"), ".xml")) - if rID == index { - for _, v := range content.Sheets.Sheet { - if v.ID == rel.ID { - return v.Name - } + wb := f.workbookReader() + if wb != nil { + for _, sheet := range wb.Sheets.Sheet { + if sheet.SheetID == index { + return sheet.Name } } } return "" } -// GetSheetIndex provides a function to get worksheet index of XLSX by given sheet -// name. If given worksheet name is invalid, will return an integer type value -// 0. +// GetSheetIndex provides a function to get worksheet index of XLSX by given +// sheet name. If given worksheet name is invalid, will return an integer type +// value 0. func (f *File) GetSheetIndex(name string) int { - content := f.workbookReader() - rels := f.workbookRelsReader() - for _, v := range content.Sheets.Sheet { - if v.Name == name { - for _, rel := range rels.Relationships { - if v.ID == rel.ID { - rID, _ := strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(rel.Target, "worksheets/sheet"), ".xml")) - return rID - } + wb := f.workbookReader() + if wb != nil { + for _, sheet := range wb.Sheets.Sheet { + if sheet.Name == trimSheetName(name) { + return sheet.SheetID } } } @@ -354,16 +353,11 @@ func (f *File) GetSheetIndex(name string) int { // } // func (f *File) GetSheetMap() map[int]string { - content := f.workbookReader() - rels := f.workbookRelsReader() + wb := f.workbookReader() sheetMap := map[int]string{} - for _, v := range content.Sheets.Sheet { - for _, rel := range rels.Relationships { - relStr := strings.SplitN(rel.Target, "worksheets/sheet", 2) - if rel.ID == v.ID && len(relStr) == 2 { - rID, _ := strconv.Atoi(strings.TrimSuffix(relStr[1], ".xml")) - sheetMap[rID] = v.Name - } + if wb != nil { + for _, sheet := range wb.Sheets.Sheet { + sheetMap[sheet.SheetID] = sheet.Name } } return sheetMap @@ -411,19 +405,31 @@ func (f *File) SetSheetBackground(sheet, picture string) error { // value of the deleted worksheet, it will cause a file error when you open it. // This function will be invalid when only the one worksheet is left. func (f *File) DeleteSheet(name string) { - content := f.workbookReader() - for k, v := range content.Sheets.Sheet { - if v.Name == trimSheetName(name) && len(content.Sheets.Sheet) > 1 { - content.Sheets.Sheet = append(content.Sheets.Sheet[:k], content.Sheets.Sheet[k+1:]...) - sheet := "xl/worksheets/sheet" + strconv.Itoa(v.SheetID) + ".xml" - rels := "xl/worksheets/_rels/sheet" + strconv.Itoa(v.SheetID) + ".xml.rels" - target := f.deleteSheetFromWorkbookRels(v.ID) + if f.SheetCount == 1 || f.GetSheetIndex(name) == 0 { + return + } + sheetName := trimSheetName(name) + wb := f.workbookReader() + wbRels := f.workbookRelsReader() + for idx, sheet := range wb.Sheets.Sheet { + if sheet.Name == sheetName { + wb.Sheets.Sheet = append(wb.Sheets.Sheet[:idx], wb.Sheets.Sheet[idx+1:]...) + var sheetXML, rels string + if wbRels != nil { + for _, rel := range wbRels.Relationships { + if rel.ID == sheet.ID { + sheetXML = fmt.Sprintf("xl/%s", rel.Target) + rels = strings.Replace(fmt.Sprintf("xl/%s.rels", rel.Target), "xl/worksheets/", "xl/worksheets/_rels/", -1) + } + } + } + target := f.deleteSheetFromWorkbookRels(sheet.ID) f.deleteSheetFromContentTypes(target) - f.deleteCalcChain(v.SheetID, "") // Delete CalcChain - delete(f.sheetMap, name) - delete(f.XLSX, sheet) + f.deleteCalcChain(sheet.SheetID, "") // Delete CalcChain + delete(f.sheetMap, sheetName) + delete(f.XLSX, sheetXML) delete(f.XLSX, rels) - delete(f.Sheet, sheet) + delete(f.Sheet, sheetXML) f.SheetCount-- } } @@ -1285,6 +1291,63 @@ func (f *File) GetDefinedName() []DefinedName { return definedNames } +// GroupSheets provides a function to group worksheets by given worksheets +// name. Group worksheets must contain an active worksheet. +func (f *File) GroupSheets(sheets []string) error { + // check an active worksheet in group worksheets + var inActiveSheet bool + activeSheet := f.GetActiveSheetIndex() + sheetMap := f.GetSheetMap() + for idx, sheetName := range sheetMap { + for _, s := range sheets { + if s == sheetName && idx == activeSheet { + inActiveSheet = true + } + } + } + if !inActiveSheet { + return errors.New("group worksheet must contain an active worksheet") + } + // check worksheet exists + ws := []*xlsxWorksheet{} + for _, sheet := range sheets { + xlsx, err := f.workSheetReader(sheet) + if err != nil { + return err + } + ws = append(ws, xlsx) + } + for _, s := range ws { + sheetViews := s.SheetViews.SheetView + if len(sheetViews) > 0 { + for idx := range sheetViews { + s.SheetViews.SheetView[idx].TabSelected = true + } + continue + } + } + return nil +} + +// UngroupSheets provides a function to ungroup worksheets. +func (f *File) UngroupSheets() error { + activeSheet := f.GetActiveSheetIndex() + sheetMap := f.GetSheetMap() + for sheetID, sheet := range sheetMap { + if activeSheet == sheetID { + continue + } + xlsx, _ := f.workSheetReader(sheet) + sheetViews := xlsx.SheetViews.SheetView + if len(sheetViews) > 0 { + for idx := range sheetViews { + xlsx.SheetViews.SheetView[idx].TabSelected = false + } + } + } + return nil +} + // 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 { -- cgit v1.2.1 From e14d2febc880f5dc0a8352f9f57af5ac3a9d37f5 Mon Sep 17 00:00:00 2001 From: xuri Date: Sat, 6 Jul 2019 15:11:51 +0800 Subject: Resolve #432, supplement the function of SetPageLayout SetPageLayout support to set fit to width and height --- sheet.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index d579c6a..1c19e86 100644 --- a/sheet.go +++ b/sheet.go @@ -1025,6 +1025,10 @@ type ( PageLayoutOrientation string // PageLayoutPaperSize defines the paper size of the worksheet PageLayoutPaperSize int + // FitToHeight specified number of vertical pages to fit on + FitToHeight int + // FitToWidth specified number of horizontal pages to fit on + FitToWidth int ) const ( @@ -1064,6 +1068,38 @@ func (p *PageLayoutPaperSize) getPageLayout(ps *xlsxPageSetUp) { *p = PageLayoutPaperSize(ps.PaperSize) } +// setPageLayout provides a method to set the fit to height for the worksheet. +func (p FitToHeight) setPageLayout(ps *xlsxPageSetUp) { + if int(p) > 0 { + ps.FitToHeight = int(p) + } +} + +// getPageLayout provides a method to get the fit to height for the worksheet. +func (p *FitToHeight) getPageLayout(ps *xlsxPageSetUp) { + if ps == nil || ps.FitToHeight == 0 { + *p = 1 + return + } + *p = FitToHeight(ps.FitToHeight) +} + +// setPageLayout provides a method to set the fit to width for the worksheet. +func (p FitToWidth) setPageLayout(ps *xlsxPageSetUp) { + if int(p) > 0 { + ps.FitToWidth = int(p) + } +} + +// getPageLayout provides a method to get the fit to width for the worksheet. +func (p *FitToWidth) getPageLayout(ps *xlsxPageSetUp) { + if ps == nil || ps.FitToWidth == 0 { + *p = 1 + return + } + *p = FitToWidth(ps.FitToWidth) +} + // SetPageLayout provides a function to sets worksheet page layout. // // Available options: @@ -1213,6 +1249,8 @@ func (f *File) SetPageLayout(sheet string, opts ...PageLayoutOption) error { // Available options: // PageLayoutOrientation(string) // PageLayoutPaperSize(int) +// FitToHeight(int) +// FitToWidth(int) func (f *File) GetPageLayout(sheet string, opts ...PageLayoutOptionPtr) error { s, err := f.workSheetReader(sheet) if err != nil { -- cgit v1.2.1 From 53e653f28ef38e8b2175fdb88de72156eab14ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=91=A3=E5=87=AF?= <13122321216@163.com> Date: Thu, 25 Jul 2019 20:27:03 +0800 Subject: Fix #443 --- sheet.go | 1 + 1 file changed, 1 insertion(+) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 1c19e86..9c288b0 100644 --- a/sheet.go +++ b/sheet.go @@ -110,6 +110,7 @@ func (f *File) workSheetWriter() { f.saveFileList(p, replaceRelationshipsBytes(replaceWorkSheetsRelationshipsNameSpaceBytes(output))) ok := f.checked[p] if ok { + delete(f.Sheet, p) f.checked[p] = false } } -- cgit v1.2.1 From 0c9e5137e35b32e6046d25604edcb9a33f8353a2 Mon Sep 17 00:00:00 2001 From: Sustainedhhh <15829307082_pp@sina.cn> Date: Thu, 25 Jul 2019 20:31:21 +0800 Subject: Fix #442 --- sheet.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 1c19e86..32fc351 100644 --- a/sheet.go +++ b/sheet.go @@ -372,7 +372,12 @@ func (f *File) getSheetMap() map[string]string { for _, v := range content.Sheets.Sheet { for _, rel := range rels.Relationships { if rel.ID == v.ID { - maps[v.Name] = fmt.Sprintf("xl/%s", rel.Target) + // Construct a target XML as xl/worksheets/sheet%d by split path, compatible with different types of relative paths in workbook.xml.rels, for example: worksheets/sheet%d.xml and /xl/worksheets/sheet%d.xml + pathInfo := strings.Split(rel.Target, "/") + pathInfoLen := len(pathInfo) + if pathInfoLen > 0 { + maps[v.Name] = fmt.Sprintf("xl/worksheets/%s", pathInfo[pathInfoLen-1]) + } } } } -- cgit v1.2.1 From cbe919fdf6c00733513494680b89171b8b1b41a1 Mon Sep 17 00:00:00 2001 From: xuri Date: Sun, 4 Aug 2019 20:24:59 +0800 Subject: New feature: sparkline supported --- sheet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 347f255..e02782a 100644 --- a/sheet.go +++ b/sheet.go @@ -232,7 +232,7 @@ func replaceRelationshipsBytes(content []byte) []byte { // a horrible hack to fix that after the XML marshalling is completed. func replaceRelationshipsNameSpaceBytes(workbookMarshal []byte) []byte { oldXmlns := []byte(``) - newXmlns := []byte(``) + newXmlns := []byte(``) return bytes.Replace(workbookMarshal, oldXmlns, newXmlns, -1) } -- cgit v1.2.1 From faaaa52cb862499454a7f893b92e8430d00172a5 Mon Sep 17 00:00:00 2001 From: Harris Date: Wed, 7 Aug 2019 08:53:37 -0500 Subject: Get sheet names based on index SheetID only seems to indicate the file name for the sheet. Check the sheets list based on index instead. Reordering sheets in Excel changes the order they appear in that list. Fixes #457 --- sheet.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index e02782a..935deac 100644 --- a/sheet.go +++ b/sheet.go @@ -317,14 +317,11 @@ func (f *File) SetSheetName(oldName, newName string) { // string. func (f *File) GetSheetName(index int) string { wb := f.workbookReader() - if wb != nil { - for _, sheet := range wb.Sheets.Sheet { - if sheet.SheetID == index { - return sheet.Name - } - } + realIdx := index - 1 // sheets are 1 based index, but we're checking against an array + if wb == nil || realIdx < 0 || realIdx >= len(wb.Sheets.Sheet) { + return "" } - return "" + return wb.Sheets.Sheet[realIdx].Name } // GetSheetIndex provides a function to get worksheet index of XLSX by given @@ -357,8 +354,8 @@ func (f *File) GetSheetMap() map[int]string { wb := f.workbookReader() sheetMap := map[int]string{} if wb != nil { - for _, sheet := range wb.Sheets.Sheet { - sheetMap[sheet.SheetID] = sheet.Name + for i, sheet := range wb.Sheets.Sheet { + sheetMap[i+1] = sheet.Name } } return sheetMap -- cgit v1.2.1 From 9c70d0ac868f66badf2663cc7b4b3c46d5411131 Mon Sep 17 00:00:00 2001 From: xuri Date: Sun, 11 Aug 2019 00:36:14 +0800 Subject: Documentation updated, Go 1.10+ required --- sheet.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 935deac..ed6d888 100644 --- a/sheet.go +++ b/sheet.go @@ -5,7 +5,7 @@ // Package excelize providing a set of functions that allow you to write to // and read from XLSX files. Support reads and writes XLSX file generated by // Microsoft Excelâ„¢ 2007 and later. Support save file without losing original -// charts of XLSX. This library needs Go version 1.8 or later. +// charts of XLSX. This library needs Go version 1.10 or later. package excelize @@ -740,9 +740,7 @@ func (f *File) searchSheet(name, value string, regSearch bool) ([]string, error) result []string r xlsxRow ) - xml.NewDecoder(bytes.NewReader(f.readXML(name))) d := f.sharedStringsReader() - decoder := xml.NewDecoder(bytes.NewReader(f.readXML(name))) for { token, _ := decoder.Token() -- cgit v1.2.1 From 8922f659788187afa6d0a5d3248e999c2c1bb846 Mon Sep 17 00:00:00 2001 From: xuri Date: Mon, 16 Sep 2019 01:17:35 +0800 Subject: Combine functions: workBookRelsWriter, drawingRelsWriter into relsWriter; drawingRelsReader, workbookRelsReader, workSheetRelsReader into relsReader; addDrawingRelationships, addSheetRelationships into addRels --- sheet.go | 85 +++++++++++++++++----------------------------------------------- 1 file changed, 22 insertions(+), 63 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index ed6d888..951baf9 100644 --- a/sheet.go +++ b/sheet.go @@ -52,7 +52,7 @@ func (f *File) NewSheet(name string) int { // Create new sheet /xl/worksheets/sheet%d.xml f.setSheet(sheetID, name) // Update xl/_rels/workbook.xml.rels - rID := f.addXlsxWorkbookRels(sheetID) + rID := f.addRels("xl/_rels/workbook.xml.rels", SourceRelationshipWorkSheet, fmt.Sprintf("worksheets/sheet%d.xml", sheetID), "") // Update xl/workbook.xml f.setWorkbook(name, sheetID, rID) return sheetID @@ -163,50 +163,18 @@ func (f *File) setWorkbook(name string, sheetID, rid int) { }) } -// workbookRelsReader provides a function to read and unmarshal workbook -// relationships of XLSX file. -func (f *File) workbookRelsReader() *xlsxWorkbookRels { - if f.WorkBookRels == nil { - var content xlsxWorkbookRels - _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML("xl/_rels/workbook.xml.rels")), &content) - f.WorkBookRels = &content - } - return f.WorkBookRels -} - -// workBookRelsWriter provides a function to save xl/_rels/workbook.xml.rels after +// relsWriter provides a function to save relationships after // serialize structure. -func (f *File) workBookRelsWriter() { - if f.WorkBookRels != nil { - output, _ := xml.Marshal(f.WorkBookRels) - f.saveFileList("xl/_rels/workbook.xml.rels", output) - } -} - -// addXlsxWorkbookRels update workbook relationships property of XLSX. -func (f *File) addXlsxWorkbookRels(sheet int) int { - content := f.workbookRelsReader() - rID := 0 - for _, v := range content.Relationships { - t, _ := strconv.Atoi(strings.TrimPrefix(v.ID, "rId")) - if t > rID { - rID = t +func (f *File) relsWriter() { + for path, rel := range f.Relationships { + if rel != nil { + output, _ := xml.Marshal(rel) + if strings.HasPrefix(path, "xl/worksheets/sheet/rels/sheet") { + output = replaceWorkSheetsRelationshipsNameSpaceBytes(output) + } + f.saveFileList(path, replaceRelationshipsBytes(output)) } } - rID++ - ID := bytes.Buffer{} - ID.WriteString("rId") - ID.WriteString(strconv.Itoa(rID)) - target := bytes.Buffer{} - target.WriteString("worksheets/sheet") - target.WriteString(strconv.Itoa(sheet)) - target.WriteString(".xml") - content.Relationships = append(content.Relationships, xlsxWorkbookRelation{ - ID: ID.String(), - Target: target.String(), - Type: SourceRelationshipWorkSheet, - }) - return rID } // setAppXML update docProps/app.xml file of XML. @@ -365,7 +333,7 @@ func (f *File) GetSheetMap() map[int]string { // of XLSX. func (f *File) getSheetMap() map[string]string { content := f.workbookReader() - rels := f.workbookRelsReader() + rels := f.relsReader("xl/_rels/workbook.xml.rels") maps := map[string]string{} for _, v := range content.Sheets.Sheet { for _, rel := range rels.Relationships { @@ -396,7 +364,9 @@ func (f *File) SetSheetBackground(sheet, picture string) error { } file, _ := ioutil.ReadFile(picture) name := f.addMedia(file, ext) - rID := f.addSheetRelationships(sheet, SourceRelationshipImage, strings.Replace(name, "xl", "..", 1), "") + sheetPath, _ := f.sheetMap[trimSheetName(sheet)] + sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels" + rID := f.addRels(sheetRels, SourceRelationshipImage, strings.Replace(name, "xl", "..", 1), "") f.addSheetPicture(sheet, rID) f.setContentTypePartImageExtensions() return err @@ -413,7 +383,7 @@ func (f *File) DeleteSheet(name string) { } sheetName := trimSheetName(name) wb := f.workbookReader() - wbRels := f.workbookRelsReader() + wbRels := f.relsReader("xl/_rels/workbook.xml.rels") for idx, sheet := range wb.Sheets.Sheet { if sheet.Name == sheetName { wb.Sheets.Sheet = append(wb.Sheets.Sheet[:idx], wb.Sheets.Sheet[idx+1:]...) @@ -443,7 +413,7 @@ func (f *File) DeleteSheet(name string) { // relationships by given relationships ID in the file // xl/_rels/workbook.xml.rels. func (f *File) deleteSheetFromWorkbookRels(rID string) string { - content := f.workbookRelsReader() + content := f.relsReader("xl/_rels/workbook.xml.rels") for k, v := range content.Relationships { if v.ID == rID { content.Relationships = append(content.Relationships[:k], content.Relationships[k+1:]...) @@ -1387,29 +1357,18 @@ func (f *File) UngroupSheets() error { return nil } -// workSheetRelsReader provides a function to get the pointer to the structure +// relsReader 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 { - if f.WorkSheetRels[path] == nil { +func (f *File) relsReader(path string) *xlsxRelationships { + if f.Relationships[path] == nil { _, ok := f.XLSX[path] if ok { - c := xlsxWorkbookRels{} + c := xlsxRelationships{} _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(path)), &c) - f.WorkSheetRels[path] = &c - } - } - return f.WorkSheetRels[path] -} - -// workSheetRelsWriter provides a function to save -// xl/worksheets/_rels/sheet%d.xml.rels after serialize structure. -func (f *File) workSheetRelsWriter() { - for p, r := range f.WorkSheetRels { - if r != nil { - v, _ := xml.Marshal(r) - f.saveFileList(p, v) + f.Relationships[path] = &c } } + return f.Relationships[path] } // fillSheetData ensures there are enough rows, and columns in the chosen -- cgit v1.2.1 From 2d21b5b50f30ae9868b2f8b1f7299ceefcf87fd2 Mon Sep 17 00:00:00 2001 From: streboryaj Date: Tue, 15 Oct 2019 09:26:08 -0500 Subject: Added accessors for Getting/Setting Page Margins (#497) * Added accessors for Getting/Setting Page Margins * Added test cases --- sheet.go | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 951baf9..ce3e645 100644 --- a/sheet.go +++ b/sheet.go @@ -1401,3 +1401,162 @@ func makeContiguousColumns(xlsx *xlsxWorksheet, fromRow, toRow, colCount int) { fillColumns(rowData, colCount, fromRow) } } + +type ( + PageMarginBottom float64 + PageMarginFooter float64 + PageMarginHeader float64 + PageMarginLeft float64 + PageMarginRight float64 + PageMarginTop float64 +) + +// setPageMargins provides a method to set the bottom margin for the worksheet. +func (p PageMarginBottom) setPageMargins(ps *xlsxPageMargins) { + ps.Bottom = float64(p) +} + +// setPageMargins provides a method to get the bottom margin for the worksheet. +func (o *PageMarginBottom) getPageMargins(ps *xlsxPageMargins) { + // Excel default: portrait + if ps == nil || ps.Bottom == 0 { + *o = 0.75 + return + } + *o = PageMarginBottom(ps.Bottom) +} + +// setPageMargins provides a method to set the Footer margin for the worksheet. +func (p PageMarginFooter) setPageMargins(ps *xlsxPageMargins) { + ps.Footer = float64(p) +} + +// setPageMargins provides a method to get the Footer margin for the worksheet. +func (o *PageMarginFooter) getPageMargins(ps *xlsxPageMargins) { + // Excel default: portrait + if ps == nil || ps.Footer == 0 { + *o = 0.3 + return + } + *o = PageMarginFooter(ps.Footer) +} + +// setPageMargins provides a method to set the Header margin for the worksheet. +func (p PageMarginHeader) setPageMargins(ps *xlsxPageMargins) { + ps.Header = float64(p) +} + +// setPageMargins provides a method to get the Header margin for the worksheet. +func (o *PageMarginHeader) getPageMargins(ps *xlsxPageMargins) { + // Excel default: portrait + if ps == nil || ps.Header == 0 { + *o = 0.3 + return + } + *o = PageMarginHeader(ps.Header) +} + +// setPageMargins provides a method to set the left margin for the worksheet. +func (p PageMarginLeft) setPageMargins(ps *xlsxPageMargins) { + ps.Left = float64(p) +} + +// setPageMargins provides a method to get the left margin for the worksheet. +func (o *PageMarginLeft) getPageMargins(ps *xlsxPageMargins) { + // Excel default: portrait + if ps == nil || ps.Left == 0 { + *o = 0.7 + return + } + *o = PageMarginLeft(ps.Left) +} + +// setPageMargins provides a method to set the right margin for the worksheet. +func (p PageMarginRight) setPageMargins(ps *xlsxPageMargins) { + ps.Right = float64(p) +} + +// setPageMargins provides a method to get the right margin for the worksheet. +func (o *PageMarginRight) getPageMargins(ps *xlsxPageMargins) { + // Excel default: portrait + if ps == nil || ps.Right == 0 { + *o = 0.7 + return + } + *o = PageMarginRight(ps.Right) +} + +// setPageMargins provides a method to set the top margin for the worksheet. +func (p PageMarginTop) setPageMargins(ps *xlsxPageMargins) { + ps.Top = float64(p) +} + +// setPageMargins provides a method to get the top margin for the worksheet. +func (o *PageMarginTop) getPageMargins(ps *xlsxPageMargins) { + // Excel default: portrait + if ps == nil || ps.Top == 0 { + *o = 0.75 + return + } + *o = PageMarginTop(ps.Top) +} + +// PageMarginsOptions is an option of a page margin of a worksheet. See +// SetPageMargins(). +type PageMarginsOptions interface { + setPageMargins(layout *xlsxPageMargins) +} + +// PageMarginsOptionsPtr is a writable PageMarginsOptions. See GetPageMargins(). +type PageMarginsOptionsPtr interface { + PageMarginsOptions + getPageMargins(layout *xlsxPageMargins) +} + +// SetPageMargins provides a function to set worksheet page lmargins. +// +// Available options: +// PageMarginBotom(float64) +// PageMarginFooter(float64) +// PageMarginHeader(float64) +// PageMarginLeft(float64) +// PageMarginRightfloat64) +// PageMarginTop(float64) +func (f *File) SetPageMargins(sheet string, opts ...PageMarginsOptions) error { + s, err := f.workSheetReader(sheet) + if err != nil { + return err + } + ps := s.PageMargins + if ps == nil { + ps = new(xlsxPageMargins) + s.PageMargins = ps + } + + for _, opt := range opts { + opt.setPageMargins(ps) + } + return err +} + +// GetPageMargins provides a function to get worksheet page margins. +// +// Available options: +// PageMarginBotom(float64) +// PageMarginFooter(float64) +// PageMarginHeader(float64) +// PageMarginLeft(float64) +// PageMarginRightfloat64) +// PageMarginTop(float64) +func (f *File) GetPageMargins(sheet string, opts ...PageMarginsOptionsPtr) error { + s, err := f.workSheetReader(sheet) + if err != nil { + return err + } + ps := s.PageMargins + + for _, opt := range opts { + opt.getPageMargins(ps) + } + return err +} -- cgit v1.2.1 From 2e791fa433def282ee2e7a5049a46fc4a76796cf Mon Sep 17 00:00:00 2001 From: xuri Date: Wed, 16 Oct 2019 01:03:29 +0800 Subject: Optimize code of Getting/Setting Page Margins --- sheet.go | 159 --------------------------------------------------------------- 1 file changed, 159 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index ce3e645..951baf9 100644 --- a/sheet.go +++ b/sheet.go @@ -1401,162 +1401,3 @@ func makeContiguousColumns(xlsx *xlsxWorksheet, fromRow, toRow, colCount int) { fillColumns(rowData, colCount, fromRow) } } - -type ( - PageMarginBottom float64 - PageMarginFooter float64 - PageMarginHeader float64 - PageMarginLeft float64 - PageMarginRight float64 - PageMarginTop float64 -) - -// setPageMargins provides a method to set the bottom margin for the worksheet. -func (p PageMarginBottom) setPageMargins(ps *xlsxPageMargins) { - ps.Bottom = float64(p) -} - -// setPageMargins provides a method to get the bottom margin for the worksheet. -func (o *PageMarginBottom) getPageMargins(ps *xlsxPageMargins) { - // Excel default: portrait - if ps == nil || ps.Bottom == 0 { - *o = 0.75 - return - } - *o = PageMarginBottom(ps.Bottom) -} - -// setPageMargins provides a method to set the Footer margin for the worksheet. -func (p PageMarginFooter) setPageMargins(ps *xlsxPageMargins) { - ps.Footer = float64(p) -} - -// setPageMargins provides a method to get the Footer margin for the worksheet. -func (o *PageMarginFooter) getPageMargins(ps *xlsxPageMargins) { - // Excel default: portrait - if ps == nil || ps.Footer == 0 { - *o = 0.3 - return - } - *o = PageMarginFooter(ps.Footer) -} - -// setPageMargins provides a method to set the Header margin for the worksheet. -func (p PageMarginHeader) setPageMargins(ps *xlsxPageMargins) { - ps.Header = float64(p) -} - -// setPageMargins provides a method to get the Header margin for the worksheet. -func (o *PageMarginHeader) getPageMargins(ps *xlsxPageMargins) { - // Excel default: portrait - if ps == nil || ps.Header == 0 { - *o = 0.3 - return - } - *o = PageMarginHeader(ps.Header) -} - -// setPageMargins provides a method to set the left margin for the worksheet. -func (p PageMarginLeft) setPageMargins(ps *xlsxPageMargins) { - ps.Left = float64(p) -} - -// setPageMargins provides a method to get the left margin for the worksheet. -func (o *PageMarginLeft) getPageMargins(ps *xlsxPageMargins) { - // Excel default: portrait - if ps == nil || ps.Left == 0 { - *o = 0.7 - return - } - *o = PageMarginLeft(ps.Left) -} - -// setPageMargins provides a method to set the right margin for the worksheet. -func (p PageMarginRight) setPageMargins(ps *xlsxPageMargins) { - ps.Right = float64(p) -} - -// setPageMargins provides a method to get the right margin for the worksheet. -func (o *PageMarginRight) getPageMargins(ps *xlsxPageMargins) { - // Excel default: portrait - if ps == nil || ps.Right == 0 { - *o = 0.7 - return - } - *o = PageMarginRight(ps.Right) -} - -// setPageMargins provides a method to set the top margin for the worksheet. -func (p PageMarginTop) setPageMargins(ps *xlsxPageMargins) { - ps.Top = float64(p) -} - -// setPageMargins provides a method to get the top margin for the worksheet. -func (o *PageMarginTop) getPageMargins(ps *xlsxPageMargins) { - // Excel default: portrait - if ps == nil || ps.Top == 0 { - *o = 0.75 - return - } - *o = PageMarginTop(ps.Top) -} - -// PageMarginsOptions is an option of a page margin of a worksheet. See -// SetPageMargins(). -type PageMarginsOptions interface { - setPageMargins(layout *xlsxPageMargins) -} - -// PageMarginsOptionsPtr is a writable PageMarginsOptions. See GetPageMargins(). -type PageMarginsOptionsPtr interface { - PageMarginsOptions - getPageMargins(layout *xlsxPageMargins) -} - -// SetPageMargins provides a function to set worksheet page lmargins. -// -// Available options: -// PageMarginBotom(float64) -// PageMarginFooter(float64) -// PageMarginHeader(float64) -// PageMarginLeft(float64) -// PageMarginRightfloat64) -// PageMarginTop(float64) -func (f *File) SetPageMargins(sheet string, opts ...PageMarginsOptions) error { - s, err := f.workSheetReader(sheet) - if err != nil { - return err - } - ps := s.PageMargins - if ps == nil { - ps = new(xlsxPageMargins) - s.PageMargins = ps - } - - for _, opt := range opts { - opt.setPageMargins(ps) - } - return err -} - -// GetPageMargins provides a function to get worksheet page margins. -// -// Available options: -// PageMarginBotom(float64) -// PageMarginFooter(float64) -// PageMarginHeader(float64) -// PageMarginLeft(float64) -// PageMarginRightfloat64) -// PageMarginTop(float64) -func (f *File) GetPageMargins(sheet string, opts ...PageMarginsOptionsPtr) error { - s, err := f.workSheetReader(sheet) - if err != nil { - return err - } - ps := s.PageMargins - - for _, opt := range opts { - opt.getPageMargins(ps) - } - return err -} -- cgit v1.2.1 From e7581ebf3e14f096b6e2d56ed34d381b4af6d310 Mon Sep 17 00:00:00 2001 From: xuri Date: Wed, 23 Oct 2019 10:08:29 +0800 Subject: Fix corrupted Excel file issue #413 --- sheet.go | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 951baf9..9e8d504 100644 --- a/sheet.go +++ b/sheet.go @@ -406,6 +406,11 @@ func (f *File) DeleteSheet(name string) { f.SheetCount-- } } + for idx, bookView := range wb.BookViews.WorkBookView { + if bookView.ActiveTab >= f.SheetCount { + wb.BookViews.WorkBookView[idx].ActiveTab-- + } + } f.SetActiveSheet(len(f.GetSheetMap())) } -- cgit v1.2.1 From 5e418ebd665f38d1211b27d7157ec7e5868451bc Mon Sep 17 00:00:00 2001 From: xuri Date: Sat, 26 Oct 2019 20:55:24 +0800 Subject: Resolve #507, add the new function `DeleteDefinedName` --- sheet.go | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 9e8d504..335c4fc 100644 --- a/sheet.go +++ b/sheet.go @@ -1271,7 +1271,7 @@ func (f *File) SetDefinedName(definedName *DefinedName) error { scope = f.GetSheetName(*dn.LocalSheetID + 1) } if scope == definedName.Scope && dn.Name == definedName.Name { - return errors.New("the same name already exists on scope") + return errors.New("the same name already exists on the scope") } } wb.DefinedNames.DefinedName = append(wb.DefinedNames.DefinedName, d) @@ -1283,6 +1283,32 @@ func (f *File) SetDefinedName(definedName *DefinedName) error { return nil } +// DeleteDefinedName provides a function to delete the defined names of the +// workbook or worksheet. If not specified scope, the default scope is +// workbook. For example: +// +// f.DeleteDefinedName(&excelize.DefinedName{ +// Name: "Amount", +// Scope: "Sheet2", +// }) +// +func (f *File) DeleteDefinedName(definedName *DefinedName) error { + wb := f.workbookReader() + if wb.DefinedNames != nil { + for idx, 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 { + wb.DefinedNames.DefinedName = append(wb.DefinedNames.DefinedName[:idx], wb.DefinedNames.DefinedName[idx+1:]...) + return nil + } + } + } + return errors.New("no defined name on the scope") +} + // GetDefinedName provides a function to get the defined names of the workbook // or worksheet. func (f *File) GetDefinedName() []DefinedName { -- cgit v1.2.1 From bf9a8355494eac18812f3caf6d469962824f627f Mon Sep 17 00:00:00 2001 From: Harris Date: Mon, 28 Oct 2019 10:34:21 -0500 Subject: Reduce allocations when writing Fix #494 If a row is full, don't bother allocating a new one, just return it. Use the last populated row as a hint for the size of new rows. Simplify checkSheet to remove row map --- sheet.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 335c4fc..43c7cc0 100644 --- a/sheet.go +++ b/sheet.go @@ -117,12 +117,19 @@ func (f *File) workSheetWriter() { } } -// trimCell provides a function to trim blank cells which created by completeCol. +// trimCell provides a function to trim blank cells which created by fillColumns. func trimCell(column []xlsxC) []xlsxC { + rowFull := true + for i := range column { + rowFull = column[i].hasValue() && rowFull + } + if rowFull { + return column + } col := make([]xlsxC, len(column)) i := 0 for _, c := range column { - if c.S != 0 || c.V != "" || c.F != nil || c.T != "" { + if c.hasValue() { col[i] = c i++ } @@ -1404,12 +1411,17 @@ func (f *File) relsReader(path string) *xlsxRelationships { // fillSheetData ensures there are enough rows, and columns in the chosen // row to accept data. Missing rows are backfilled and given their row number +// Uses the last populated row as a hint for the size of the next row to add func prepareSheetXML(xlsx *xlsxWorksheet, col int, row int) { rowCount := len(xlsx.SheetData.Row) + sizeHint := 0 + if rowCount > 0 { + sizeHint = len(xlsx.SheetData.Row[rowCount-1].C) + } if rowCount < row { // append missing rows for rowIdx := rowCount; rowIdx < row; rowIdx++ { - xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{R: rowIdx + 1}) + xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{R: rowIdx + 1, C: make([]xlsxC, 0, sizeHint)}) } } rowData := &xlsx.SheetData.Row[row-1] -- cgit v1.2.1 From 7965e1231b736f8507f93f6383b76332eb15ff5f Mon Sep 17 00:00:00 2001 From: xuri Date: Sat, 23 Nov 2019 04:13:59 +0800 Subject: Resolve #146, make the GetRow function read data as streaming. Ref: #382, #515 --- sheet.go | 63 ++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 29 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 43c7cc0..c2e6bf6 100644 --- a/sheet.go +++ b/sheet.go @@ -699,15 +699,12 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) { 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 + return result, ErrSheetNotExist{sheet} } - if xlsx != nil { + if f.Sheet[name] != nil { + // flush data output, _ := xml.Marshal(f.Sheet[name]) f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output)) } @@ -718,9 +715,10 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) { // cell value, and regular expression. func (f *File) searchSheet(name, value string, regSearch bool) ([]string, error) { var ( - inElement string - result []string - r xlsxRow + err error + cellName, inElement string + result []string + cellCol, row int ) d := f.sharedStringsReader() decoder := xml.NewDecoder(bytes.NewReader(f.readXML(name))) @@ -733,31 +731,38 @@ func (f *File) searchSheet(name, value string, regSearch bool) ([]string, error) case xml.StartElement: inElement = startElement.Name.Local if inElement == "row" { - r = xlsxRow{} - _ = decoder.DecodeElement(&r, &startElement) - for _, colCell := range r.C { - val, _ := colCell.getValueFrom(f, d) - if regSearch { - regex := regexp.MustCompile(value) - if !regex.MatchString(val) { - continue - } - } else { - if val != value { - continue + for _, attr := range startElement.Attr { + if attr.Name.Local == "r" { + row, err = strconv.Atoi(attr.Value) + if err != nil { + return result, err } } - - cellCol, _, err := CellNameToCoordinates(colCell.R) - if err != nil { - return result, err + } + } + if inElement == "c" { + colCell := xlsxC{} + _ = decoder.DecodeElement(&colCell, &startElement) + val, _ := colCell.getValueFrom(f, d) + if regSearch { + regex := regexp.MustCompile(value) + if !regex.MatchString(val) { + continue } - cellName, err := CoordinatesToCellName(cellCol, r.R) - if err != nil { - return result, err + } else { + if val != value { + continue } - result = append(result, cellName) } + cellCol, _, err = CellNameToCoordinates(colCell.R) + if err != nil { + return result, err + } + cellName, err = CoordinatesToCellName(cellCol, row) + if err != nil { + return result, err + } + result = append(result, cellName) } default: } -- cgit v1.2.1 From 8d6e431dcd8d96dc51f74308e49b5d4a5b2b9d2e Mon Sep 17 00:00:00 2001 From: xuri Date: Thu, 28 Nov 2019 21:53:50 +0800 Subject: Resolve #521, fix missing elements when parsing --- sheet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index c2e6bf6..566e6e7 100644 --- a/sheet.go +++ b/sheet.go @@ -207,7 +207,7 @@ func replaceRelationshipsBytes(content []byte) []byte { // a horrible hack to fix that after the XML marshalling is completed. func replaceRelationshipsNameSpaceBytes(workbookMarshal []byte) []byte { oldXmlns := []byte(``) - newXmlns := []byte(``) + newXmlns := []byte(``) return bytes.Replace(workbookMarshal, oldXmlns, newXmlns, -1) } -- cgit v1.2.1 From 402ad2f62b04d44f1ab866b32b9e7314a713e5f0 Mon Sep 17 00:00:00 2001 From: xuri Date: Sat, 30 Nov 2019 00:06:36 +0800 Subject: Update XML namespace --- sheet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 566e6e7..1ce85b4 100644 --- a/sheet.go +++ b/sheet.go @@ -207,7 +207,7 @@ func replaceRelationshipsBytes(content []byte) []byte { // a horrible hack to fix that after the XML marshalling is completed. func replaceRelationshipsNameSpaceBytes(workbookMarshal []byte) []byte { oldXmlns := []byte(``) - newXmlns := []byte(``) + newXmlns := []byte(` Date: Mon, 16 Dec 2019 08:32:04 +0800 Subject: Fix #426, handle empty workbook view --- sheet.go | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 1ce85b4..e261935 100644 --- a/sheet.go +++ b/sheet.go @@ -149,11 +149,12 @@ func (f *File) setContentTypes(index int) { // setSheet provides a function to update sheet property by given index. func (f *File) setSheet(index int, name string) { - var xlsx xlsxWorksheet - xlsx.Dimension.Ref = "A1" - xlsx.SheetViews.SheetView = append(xlsx.SheetViews.SheetView, xlsxSheetView{ - WorkbookViewID: 0, - }) + xlsx := xlsxWorksheet{ + Dimension: &xlsxDimension{Ref: "A1"}, + SheetViews: xlsxSheetViews{ + SheetView: []xlsxSheetView{{WorkbookViewID: 0}}, + }, + } path := "xl/worksheets/sheet" + strconv.Itoa(index) + ".xml" f.sheetMap[trimSheetName(name)] = path f.Sheet[path] = &xlsx @@ -222,6 +223,9 @@ func (f *File) SetActiveSheet(index int) { wb := f.workbookReader() for activeTab, sheet := range wb.Sheets.Sheet { if sheet.SheetID == index { + if wb.BookViews == nil { + wb.BookViews = &xlsxBookViews{} + } if len(wb.BookViews.WorkBookView) > 0 { wb.BookViews.WorkBookView[0].ActiveTab = activeTab } else { @@ -253,16 +257,13 @@ func (f *File) SetActiveSheet(index int) { func (f *File) GetActiveSheetIndex() int { wb := f.workbookReader() if wb != nil { - view := wb.BookViews.WorkBookView - sheets := wb.Sheets.Sheet - var activeTab int - if len(view) > 0 { - activeTab = view[0].ActiveTab - if len(sheets) > activeTab && sheets[activeTab].SheetID != 0 { - return sheets[activeTab].SheetID + if wb.BookViews != nil && len(wb.BookViews.WorkBookView) > 0 { + activeTab := wb.BookViews.WorkBookView[0].ActiveTab + if len(wb.Sheets.Sheet) > activeTab && wb.Sheets.Sheet[activeTab].SheetID != 0 { + return wb.Sheets.Sheet[activeTab].SheetID } } - if len(wb.Sheets.Sheet) == 1 { + if len(wb.Sheets.Sheet) >= 1 { return wb.Sheets.Sheet[0].SheetID } } @@ -413,9 +414,11 @@ func (f *File) DeleteSheet(name string) { f.SheetCount-- } } - for idx, bookView := range wb.BookViews.WorkBookView { - if bookView.ActiveTab >= f.SheetCount { - wb.BookViews.WorkBookView[idx].ActiveTab-- + if wb.BookViews != nil { + for idx, bookView := range wb.BookViews.WorkBookView { + if bookView.ActiveTab >= f.SheetCount { + wb.BookViews.WorkBookView[idx].ActiveTab-- + } } } f.SetActiveSheet(len(f.GetSheetMap())) -- cgit v1.2.1 From b1b3c0d15158abc71267da5893de020f047c3872 Mon Sep 17 00:00:00 2001 From: Alex Geer Date: Thu, 19 Dec 2019 19:30:48 +0300 Subject: =?UTF-8?q?Fix=20#539=20Fixed=20error=20opening=20excel=20file=20c?= =?UTF-8?q?reated=20in=20encoding=20d=E2=80=A6=20(#540)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed issue #539 Fixed error opening excel file created in encoding different from UTF-8, added logging of possible errors when decoding XML if the function does not provide exit with an error * Added test for CharsetReader * Fixed #discussion_r359397878 Discussion: https://github.com/360EntSecGroup-Skylar/excelize/pull/540#discussion_r359397878 * Fixed go fmt * go mod tidy and removed unused imports * The code has been refactored --- sheet.go | 78 +++++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 25 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 951baf9..42fd6b3 100644 --- a/sheet.go +++ b/sheet.go @@ -15,7 +15,9 @@ import ( "encoding/xml" "errors" "fmt" + "io" "io/ioutil" + "log" "os" "path" "reflect" @@ -61,11 +63,16 @@ func (f *File) NewSheet(name string) int { // contentTypesReader provides a function to get the pointer to the // [Content_Types].xml structure after deserialization. func (f *File) contentTypesReader() *xlsxTypes { + var err error + if f.ContentTypes == nil { - var content xlsxTypes - _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML("[Content_Types].xml")), &content) - f.ContentTypes = &content + f.ContentTypes = new(xlsxTypes) + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("[Content_Types].xml")))). + Decode(f.ContentTypes); err != nil && err != io.EOF { + log.Printf("xml decode error: %s", err) + } } + return f.ContentTypes } @@ -81,11 +88,16 @@ func (f *File) contentTypesWriter() { // workbookReader provides a function to get the pointer to the xl/workbook.xml // structure after deserialization. func (f *File) workbookReader() *xlsxWorkbook { + var err error + if f.WorkBook == nil { - var content xlsxWorkbook - _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML("xl/workbook.xml")), &content) - f.WorkBook = &content + f.WorkBook = new(xlsxWorkbook) + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("xl/workbook.xml")))). + Decode(f.WorkBook); err != nil && err != io.EOF { + log.Printf("xml decode error: %s", err) + } } + return f.WorkBook } @@ -679,42 +691,51 @@ func (f *File) GetSheetVisible(name string) bool { // // result, err := f.SearchSheet("Sheet1", "[0-9]", true) // -func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) { +func (f *File) SearchSheet(sheet, value string, reg ...bool) (result []string, err error) { var ( - regSearch bool - result []string + xlsx *xlsxWorksheet + regSearch, r, ok bool + name string + output []byte ) - for _, r := range reg { + + for _, r = range reg { regSearch = r } - xlsx, err := f.workSheetReader(sheet) - if err != nil { - return result, err + if xlsx, err = f.workSheetReader(sheet); err != nil { + return } - name, ok := f.sheetMap[trimSheetName(sheet)] - if !ok { - return result, nil + if name, ok = f.sheetMap[trimSheetName(sheet)]; !ok { + return } if xlsx != nil { - output, _ := xml.Marshal(f.Sheet[name]) + if output, err = xml.Marshal(f.Sheet[name]); err != nil { + return + } 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) { +func (f *File) searchSheet(name, value string, regSearch bool) (result []string, err error) { var ( + d *xlsxSST + decoder *xml.Decoder inElement string - result []string r xlsxRow + token xml.Token ) - d := f.sharedStringsReader() - decoder := xml.NewDecoder(bytes.NewReader(f.readXML(name))) + + d = f.sharedStringsReader() + decoder = f.xmlNewDecoder(bytes.NewReader(f.readXML(name))) for { - token, _ := decoder.Token() - if token == nil { + if token, err = decoder.Token(); err != nil || token == nil { + if err == io.EOF { + err = nil + } break } switch startElement := token.(type) { @@ -750,7 +771,8 @@ func (f *File) searchSheet(name, value string, regSearch bool) ([]string, error) default: } } - return result, nil + + return } // SetHeaderFooter provides a function to set headers and footers by given @@ -1360,14 +1382,20 @@ func (f *File) UngroupSheets() error { // relsReader provides a function to get the pointer to the structure // after deserialization of xl/worksheets/_rels/sheet%d.xml.rels. func (f *File) relsReader(path string) *xlsxRelationships { + var err error + if f.Relationships[path] == nil { _, ok := f.XLSX[path] if ok { c := xlsxRelationships{} - _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(path)), &c) + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))). + Decode(&c); err != nil && err != io.EOF { + log.Printf("xml decode error: %s", err) + } f.Relationships[path] = &c } } + return f.Relationships[path] } -- cgit v1.2.1 From 7358dca436f6ca5948a3f2865b14e828863d86a9 Mon Sep 17 00:00:00 2001 From: match-meng <54879059+match-meng@users.noreply.github.com> Date: Fri, 20 Dec 2019 22:22:56 +0800 Subject: Update comments for the xmlNewDecoder (#542) --- sheet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 3b22a0e..6ef7c6e 100644 --- a/sheet.go +++ b/sheet.go @@ -163,7 +163,7 @@ func (f *File) setContentTypes(index int) { func (f *File) setSheet(index int, name string) { xlsx := xlsxWorksheet{ Dimension: &xlsxDimension{Ref: "A1"}, - SheetViews: xlsxSheetViews{ + SheetViews: &xlsxSheetViews{ SheetView: []xlsxSheetView{{WorkbookViewID: 0}}, }, } -- cgit v1.2.1 From ae2865d9237cfd27d7bc4fbef3870b3361597be8 Mon Sep 17 00:00:00 2001 From: xuri Date: Sun, 22 Dec 2019 00:02:09 +0800 Subject: Improve code coverage unit tests --- sheet.go | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 6ef7c6e..7412fce 100644 --- a/sheet.go +++ b/sheet.go @@ -505,7 +505,7 @@ func (f *File) copySheet(from, to int) error { // SetSheetVisible provides a function to set worksheet visible by given worksheet // name. A workbook must contain at least one visible worksheet. If the given // worksheet has been activated, this setting will be invalidated. Sheet state -// values as defined by http://msdn.microsoft.com/en-us/library/office/documentformat.openxml.spreadsheet.sheetstatevalues.aspx +// values as defined by https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.sheetstatevalues // // visible // hidden @@ -738,7 +738,8 @@ func (f *File) searchSheet(name, value string, regSearch bool) (result []string, d = f.sharedStringsReader() decoder := f.xmlNewDecoder(bytes.NewReader(f.readXML(name))) for { - token, err := decoder.Token() + var token xml.Token + token, err = decoder.Token() if err != nil || token == nil { if err == io.EOF { err = nil @@ -749,13 +750,9 @@ func (f *File) searchSheet(name, value string, regSearch bool) (result []string, case xml.StartElement: inElement = startElement.Name.Local if inElement == "row" { - for _, attr := range startElement.Attr { - if attr.Name.Local == "r" { - row, err = strconv.Atoi(attr.Value) - if err != nil { - return result, err - } - } + row, err = attrValToInt("r", startElement.Attr) + if err != nil { + return } } if inElement == "c" { @@ -785,7 +782,20 @@ func (f *File) searchSheet(name, value string, regSearch bool) (result []string, default: } } + return +} +// attrValToInt provides a function to convert the local names to an integer +// by given XML attributes and specified names. +func attrValToInt(name string, attrs []xml.Attr) (val int, err error) { + for _, attr := range attrs { + if attr.Name.Local == name { + val, err = strconv.Atoi(attr.Value) + if err != nil { + return + } + } + } return } -- cgit v1.2.1 From 4e4a5b9b3e052d1694442515492792fb1aa74c5a Mon Sep 17 00:00:00 2001 From: xuri Date: Mon, 23 Dec 2019 00:07:40 +0800 Subject: Improve compatibility, fix workbook's rels ID calc error --- sheet.go | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 7412fce..954de5b 100644 --- a/sheet.go +++ b/sheet.go @@ -249,6 +249,11 @@ func (f *File) SetActiveSheet(index int) { } for idx, name := range f.GetSheetMap() { xlsx, _ := f.workSheetReader(name) + if xlsx.SheetViews == nil { + xlsx.SheetViews = &xlsxSheetViews{ + SheetView: []xlsxSheetView{{WorkbookViewID: 0}}, + } + } if len(xlsx.SheetViews.SheetView) > 0 { xlsx.SheetViews.SheetView[0].TabSelected = false } @@ -305,11 +310,15 @@ func (f *File) SetSheetName(oldName, newName string) { // string. func (f *File) GetSheetName(index int) string { wb := f.workbookReader() - realIdx := index - 1 // sheets are 1 based index, but we're checking against an array - if wb == nil || realIdx < 0 || realIdx >= len(wb.Sheets.Sheet) { + if wb == nil || index < 1 { return "" } - return wb.Sheets.Sheet[realIdx].Name + for _, sheet := range wb.Sheets.Sheet { + if index == sheet.SheetID { + return sheet.Name + } + } + return "" } // GetSheetIndex provides a function to get worksheet index of XLSX by given @@ -342,8 +351,8 @@ func (f *File) GetSheetMap() map[int]string { wb := f.workbookReader() sheetMap := map[int]string{} if wb != nil { - for i, sheet := range wb.Sheets.Sheet { - sheetMap[i+1] = sheet.Name + for _, sheet := range wb.Sheets.Sheet { + sheetMap[sheet.SheetID] = sheet.Name } } return sheetMap @@ -384,8 +393,7 @@ func (f *File) SetSheetBackground(sheet, picture string) error { } file, _ := ioutil.ReadFile(picture) name := f.addMedia(file, ext) - sheetPath, _ := f.sheetMap[trimSheetName(sheet)] - sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels" + sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels" rID := f.addRels(sheetRels, SourceRelationshipImage, strings.Replace(name, "xl", "..", 1), "") f.addSheetPicture(sheet, rID) f.setContentTypePartImageExtensions() -- cgit v1.2.1 From 09485b3f9f0aefc58d51462aed65c2416205c591 Mon Sep 17 00:00:00 2001 From: xuri Date: Sun, 29 Dec 2019 16:02:31 +0800 Subject: Improve code coverage unit tests --- sheet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 954de5b..2654b8f 100644 --- a/sheet.go +++ b/sheet.go @@ -1,4 +1,4 @@ -// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of +// Copyright 2016 - 2020 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. // -- cgit v1.2.1 From 5ca7231ed408ac264f509ff52b5d28ff4fbda757 Mon Sep 17 00:00:00 2001 From: xuri Date: Fri, 3 Jan 2020 23:57:25 +0800 Subject: optimize code and comments: use println errors instead of panic --- sheet.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 2654b8f..19b90c6 100644 --- a/sheet.go +++ b/sheet.go @@ -339,12 +339,12 @@ func (f *File) GetSheetIndex(name string) int { // GetSheetMap provides a function to get worksheet name and index map of XLSX. // For example: // -// f, err := excelize.OpenFile("./Book1.xlsx") +// f, err := excelize.OpenFile("Book1.xlsx") // if err != nil { // return // } // for index, name := range f.GetSheetMap() { -// fmt.Println(index, name) +// println(index, name) // } // func (f *File) GetSheetMap() map[int]string { -- cgit v1.2.1 From e51aff2d9562bbfb290ef76a948facb6d4660eff Mon Sep 17 00:00:00 2001 From: xuri Date: Fri, 7 Feb 2020 00:25:01 +0800 Subject: Resolve #570, flat columns for the column's operation --- sheet.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 19b90c6..a6ff2a1 100644 --- a/sheet.go +++ b/sheet.go @@ -287,8 +287,8 @@ func (f *File) GetActiveSheetIndex() int { return 0 } -// SetSheetName provides a function to set the worksheet name be given old and -// new worksheet name. Maximum 31 characters are allowed in sheet title and +// SetSheetName provides a function to set the worksheet name by given old and +// new worksheet names. Maximum 31 characters are allowed in sheet title and // this function only changes the name of the sheet and will not update the // sheet name in the formula or reference associated with the cell. So there // may be problem formula error or reference missing. -- cgit v1.2.1 From ad883caa0f77dfc016ae99bd5fbb606953eb99a0 Mon Sep 17 00:00:00 2001 From: xuri Date: Wed, 19 Feb 2020 00:08:10 +0800 Subject: Resolve #580, revert commit https://github.com/360EntSecGroup-Skylar/excelize/commit/5ca7231ed408ac264f509ff52b5d28ff4fbda757 --- sheet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index a6ff2a1..48671c0 100644 --- a/sheet.go +++ b/sheet.go @@ -344,7 +344,7 @@ func (f *File) GetSheetIndex(name string) int { // return // } // for index, name := range f.GetSheetMap() { -// println(index, name) +// fmt.Println(index, name) // } // func (f *File) GetSheetMap() map[int]string { -- cgit v1.2.1 From 1d87da57ecf5e13203b6441dd97160885981545e Mon Sep 17 00:00:00 2001 From: xuri Date: Sun, 1 Mar 2020 00:34:41 +0800 Subject: Resolve #492, init support for insert and remove page break --- sheet.go | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 48671c0..08b0e96 100644 --- a/sheet.go +++ b/sheet.go @@ -1437,6 +1437,115 @@ func (f *File) UngroupSheets() error { return nil } +// InsertPageBreak create a page break to determine where the printed page +// ends and where begins the next one by given worksheet name and axis, so the +// content before the page break will be printed on one page and after the +// page break on another. +func (f *File) InsertPageBreak(sheet, cell string) (err error) { + var ws *xlsxWorksheet + var row, col int + var rowBrk, colBrk = -1, -1 + if ws, err = f.workSheetReader(sheet); err != nil { + return + } + if col, row, err = CellNameToCoordinates(cell); err != nil { + return + } + col-- + row-- + if col == row && col == 0 { + return + } + if ws.RowBreaks == nil { + ws.RowBreaks = &xlsxBreaks{} + } + if ws.ColBreaks == nil { + ws.ColBreaks = &xlsxBreaks{} + } + + for idx, brk := range ws.RowBreaks.Brk { + if brk.ID == row { + rowBrk = idx + } + } + for idx, brk := range ws.ColBreaks.Brk { + if brk.ID == col { + colBrk = idx + } + } + + if row != 0 && rowBrk == -1 { + ws.RowBreaks.Brk = append(ws.RowBreaks.Brk, &xlsxBrk{ + ID: row, + Max: 16383, + Man: true, + }) + ws.RowBreaks.ManualBreakCount++ + } + if col != 0 && colBrk == -1 { + ws.ColBreaks.Brk = append(ws.ColBreaks.Brk, &xlsxBrk{ + ID: col, + Max: 1048575, + Man: true, + }) + ws.ColBreaks.ManualBreakCount++ + } + ws.RowBreaks.Count = len(ws.RowBreaks.Brk) + ws.ColBreaks.Count = len(ws.ColBreaks.Brk) + return +} + +// RemovePageBreak remove a page break by given worksheet name and axis. +func (f *File) RemovePageBreak(sheet, cell string) (err error) { + var ws *xlsxWorksheet + var row, col int + if ws, err = f.workSheetReader(sheet); err != nil { + return + } + if col, row, err = CellNameToCoordinates(cell); err != nil { + return + } + col-- + row-- + if col == row && col == 0 { + return + } + removeBrk := func(ID int, brks []*xlsxBrk) []*xlsxBrk { + for i, brk := range brks { + if brk.ID == ID { + brks = append(brks[:i], brks[i+1:]...) + } + } + return brks + } + if ws.RowBreaks == nil || ws.ColBreaks == nil { + return + } + rowBrks := len(ws.RowBreaks.Brk) + colBrks := len(ws.ColBreaks.Brk) + if rowBrks > 0 && rowBrks == colBrks { + ws.RowBreaks.Brk = removeBrk(row, ws.RowBreaks.Brk) + ws.ColBreaks.Brk = removeBrk(col, ws.ColBreaks.Brk) + ws.RowBreaks.Count = len(ws.RowBreaks.Brk) + ws.ColBreaks.Count = len(ws.ColBreaks.Brk) + ws.RowBreaks.ManualBreakCount-- + ws.ColBreaks.ManualBreakCount-- + return + } + if rowBrks > 0 && rowBrks > colBrks { + ws.RowBreaks.Brk = removeBrk(row, ws.RowBreaks.Brk) + ws.RowBreaks.Count = len(ws.RowBreaks.Brk) + ws.RowBreaks.ManualBreakCount-- + return + } + if colBrks > 0 && colBrks > rowBrks { + ws.ColBreaks.Brk = removeBrk(col, ws.ColBreaks.Brk) + ws.ColBreaks.Count = len(ws.ColBreaks.Brk) + ws.ColBreaks.ManualBreakCount-- + } + return +} + // relsReader provides a function to get the pointer to the structure // after deserialization of xl/worksheets/_rels/sheet%d.xml.rels. func (f *File) relsReader(path string) *xlsxRelationships { -- cgit v1.2.1 From 6afc468a025984aa1b265b0228f032c5ed881a3b Mon Sep 17 00:00:00 2001 From: xuri Date: Sat, 28 Mar 2020 23:47:26 +0800 Subject: Resolve #451, support create chart sheet --- sheet.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 08b0e96..11f56d9 100644 --- a/sheet.go +++ b/sheet.go @@ -50,7 +50,7 @@ func (f *File) NewSheet(name string) int { // Update docProps/app.xml f.setAppXML() // Update [Content_Types].xml - f.setContentTypes(sheetID) + f.setContentTypes("/xl/worksheets/sheet"+strconv.Itoa(sheetID)+".xml", ContentTypeSpreadSheetMLWorksheet) // Create new sheet /xl/worksheets/sheet%d.xml f.setSheet(sheetID, name) // Update xl/_rels/workbook.xml.rels @@ -151,11 +151,11 @@ func trimCell(column []xlsxC) []xlsxC { // setContentTypes provides a function to read and update property of contents // type of XLSX. -func (f *File) setContentTypes(index int) { +func (f *File) setContentTypes(partName, contentType string) { content := f.contentTypesReader() content.Overrides = append(content.Overrides, xlsxOverride{ - PartName: "/xl/worksheets/sheet" + strconv.Itoa(index) + ".xml", - ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml", + PartName: partName, + ContentType: contentType, }) } @@ -336,8 +336,8 @@ func (f *File) GetSheetIndex(name string) int { return 0 } -// GetSheetMap provides a function to get worksheet name and index map of XLSX. -// For example: +// GetSheetMap provides a function to get worksheet and chartsheet name and +// index map of XLSX. For example: // // f, err := excelize.OpenFile("Book1.xlsx") // if err != nil { @@ -358,8 +358,8 @@ func (f *File) GetSheetMap() map[int]string { return sheetMap } -// getSheetMap provides a function to get worksheet name and XML file path map -// of XLSX. +// getSheetMap provides a function to get worksheet and chartsheet name and +// XML file path map of XLSX. func (f *File) getSheetMap() map[string]string { content := f.workbookReader() rels := f.relsReader("xl/_rels/workbook.xml.rels") -- cgit v1.2.1 From 3f89c6e9799c9c82af1305f080416c53d19e64c1 Mon Sep 17 00:00:00 2001 From: xuri Date: Sun, 29 Mar 2020 18:44:24 +0800 Subject: remove ineffectual variable assignments and simplify code --- sheet.go | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 11f56d9..6ddd629 100644 --- a/sheet.go +++ b/sheet.go @@ -119,7 +119,7 @@ func (f *File) workSheetWriter() { f.Sheet[p].SheetData.Row[k].C = trimCell(v.C) } output, _ := xml.Marshal(sheet) - f.saveFileList(p, replaceRelationshipsBytes(replaceWorkSheetsRelationshipsNameSpaceBytes(output))) + f.saveFileList(p, replaceRelationshipsBytes(replaceRelationshipsNameSpaceBytes(output))) ok := f.checked[p] if ok { delete(f.Sheet, p) @@ -190,7 +190,7 @@ func (f *File) relsWriter() { if rel != nil { output, _ := xml.Marshal(rel) if strings.HasPrefix(path, "xl/worksheets/sheet/rels/sheet") { - output = replaceWorkSheetsRelationshipsNameSpaceBytes(output) + output = replaceRelationshipsNameSpaceBytes(output) } f.saveFileList(path, replaceRelationshipsBytes(output)) } @@ -211,19 +211,6 @@ func replaceRelationshipsBytes(content []byte) []byte { return bytes.Replace(content, oldXmlns, newXmlns, -1) } -// replaceRelationshipsNameSpaceBytes; Some tools that read XLSX files have -// very strict requirements about the structure of the input XML. In -// particular both Numbers on the Mac and SAS dislike inline XML namespace -// declarations, or namespace prefixes that don't match the ones that Excel -// itself uses. This is a problem because the Go XML library doesn't multiple -// namespace declarations in a single element of a document. This function is -// a horrible hack to fix that after the XML marshalling is completed. -func replaceRelationshipsNameSpaceBytes(workbookMarshal []byte) []byte { - oldXmlns := []byte(``) - newXmlns := []byte(` 0 { - maps[v.Name] = fmt.Sprintf("xl/worksheets/%s", pathInfo[pathInfoLen-1]) + if pathInfoLen > 1 { + maps[v.Name] = fmt.Sprintf("xl/%s", strings.Join(pathInfo[pathInfoLen-2:], "/")) } } } @@ -420,7 +411,10 @@ func (f *File) DeleteSheet(name string) { for _, rel := range wbRels.Relationships { if rel.ID == sheet.ID { sheetXML = fmt.Sprintf("xl/%s", rel.Target) - rels = strings.Replace(fmt.Sprintf("xl/%s.rels", rel.Target), "xl/worksheets/", "xl/worksheets/_rels/", -1) + pathInfo := strings.Split(rel.Target, "/") + if len(pathInfo) == 2 { + rels = fmt.Sprintf("xl/%s/_rels/%s.rels", pathInfo[0], pathInfo[1]) + } } } } @@ -430,6 +424,7 @@ func (f *File) DeleteSheet(name string) { delete(f.sheetMap, sheetName) delete(f.XLSX, sheetXML) delete(f.XLSX, rels) + delete(f.Relationships, rels) delete(f.Sheet, sheetXML) f.SheetCount-- } @@ -729,7 +724,7 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) { if f.Sheet[name] != nil { // flush data output, _ := xml.Marshal(f.Sheet[name]) - f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output)) + f.saveFileList(name, replaceRelationshipsNameSpaceBytes(output)) } return f.searchSheet(name, value, regSearch) } -- cgit v1.2.1 From 0f2a9053246c3ae45e6c7ba911a1fb135664abdf Mon Sep 17 00:00:00 2001 From: xuri Date: Thu, 2 Apr 2020 00:41:14 +0800 Subject: Performance improvements --- sheet.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 6ddd629..a3276c2 100644 --- a/sheet.go +++ b/sheet.go @@ -206,9 +206,9 @@ func (f *File) setAppXML() { // requirements about the structure of the input XML. This function is a // horrible hack to fix that after the XML marshalling is completed. func replaceRelationshipsBytes(content []byte) []byte { - oldXmlns := []byte(`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships`) - newXmlns := []byte("r") - return bytes.Replace(content, oldXmlns, newXmlns, -1) + oldXmlns := stringToBytes(`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships`) + newXmlns := stringToBytes("r") + return bytesReplace(content, oldXmlns, newXmlns, -1) } // SetActiveSheet provides function to set default active worksheet of XLSX by -- cgit v1.2.1 From 1fe660df648422a53eef0c735657cb2f5237ef7b Mon Sep 17 00:00:00 2001 From: xuri Date: Thu, 23 Apr 2020 02:01:14 +0800 Subject: - Resolve #485 use sheet index instead of ID - added 3 internal function: getSheetID, getActiveSheetID, getSheetNameByID --- sheet.go | 137 +++++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 94 insertions(+), 43 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index a3276c2..50081e8 100644 --- a/sheet.go +++ b/sheet.go @@ -34,7 +34,7 @@ import ( // the number of sheets in the workbook (file) after appending the new sheet. func (f *File) NewSheet(name string) int { // Check if the worksheet already exists - if f.GetSheetIndex(name) != 0 { + if f.GetSheetIndex(name) != -1 { return f.SheetCount } f.DeleteSheet(name) @@ -57,7 +57,7 @@ func (f *File) NewSheet(name string) int { rID := f.addRels("xl/_rels/workbook.xml.rels", SourceRelationshipWorkSheet, fmt.Sprintf("worksheets/sheet%d.xml", sheetID), "") // Update xl/workbook.xml f.setWorkbook(name, sheetID, rID) - return sheetID + return f.GetSheetIndex(name) } // contentTypesReader provides a function to get the pointer to the @@ -213,15 +213,15 @@ func replaceRelationshipsBytes(content []byte) []byte { // SetActiveSheet provides function to set default active worksheet of XLSX by // given index. Note that active index is different from the index returned by -// function GetSheetMap(). It should be greater than 0 and less than total -// worksheet numbers. +// function GetSheetMap(). It should be greater or equal to 0 and less than +// total worksheet numbers. func (f *File) SetActiveSheet(index int) { - if index < 1 { - index = 1 + if index < 0 { + index = 0 } wb := f.workbookReader() - for activeTab, sheet := range wb.Sheets.Sheet { - if sheet.SheetID == index { + for activeTab := range wb.Sheets.Sheet { + if activeTab == index { if wb.BookViews == nil { wb.BookViews = &xlsxBookViews{} } @@ -234,7 +234,7 @@ func (f *File) SetActiveSheet(index int) { } } } - for idx, name := range f.GetSheetMap() { + for idx, name := range f.GetSheetList() { xlsx, err := f.workSheetReader(name) if err != nil { // Chartsheet @@ -262,7 +262,22 @@ func (f *File) SetActiveSheet(index int) { // GetActiveSheetIndex provides a function to get active sheet index of the // XLSX. If not found the active sheet will be return integer 0. -func (f *File) GetActiveSheetIndex() int { +func (f *File) GetActiveSheetIndex() (index int) { + var sheetID = f.getActiveSheetID() + wb := f.workbookReader() + if wb != nil { + for idx, sheet := range wb.Sheets.Sheet { + if sheet.SheetID == sheetID { + index = idx + } + } + } + return +} + +// getActiveSheetID provides a function to get active sheet index of the +// XLSX. If not found the active sheet will be return integer 0. +func (f *File) getActiveSheetID() int { wb := f.workbookReader() if wb != nil { if wb.BookViews != nil && len(wb.BookViews.WorkBookView) > 0 { @@ -296,39 +311,62 @@ func (f *File) SetSheetName(oldName, newName string) { } } -// GetSheetName provides a function to get worksheet name of XLSX by given -// worksheet index. If given sheet index is invalid, will return an empty +// getSheetNameByID provides a function to get worksheet name of XLSX by given +// worksheet ID. If given sheet ID is invalid, will return an empty // string. -func (f *File) GetSheetName(index int) string { +func (f *File) getSheetNameByID(ID int) string { wb := f.workbookReader() - if wb == nil || index < 1 { + if wb == nil || ID < 1 { return "" } for _, sheet := range wb.Sheets.Sheet { - if index == sheet.SheetID { + if ID == sheet.SheetID { return sheet.Name } } return "" } +// GetSheetName provides a function to get worksheet name of XLSX by given +// worksheet index. If given sheet index is invalid, will return an empty +// string. +func (f *File) GetSheetName(index int) (name string) { + for idx, sheet := range f.GetSheetList() { + if idx == index { + name = sheet + } + } + return +} + +// getSheetID provides a function to get worksheet ID of XLSX by given +// sheet name. If given worksheet name is invalid, will return an integer type +// value -1. +func (f *File) getSheetID(name string) int { + var ID = -1 + for sheetID, sheet := range f.GetSheetMap() { + if sheet == trimSheetName(name) { + ID = sheetID + } + } + return ID +} + // GetSheetIndex provides a function to get worksheet index of XLSX by given // sheet name. If given worksheet name is invalid, will return an integer type -// value 0. +// value -1. func (f *File) GetSheetIndex(name string) int { - wb := f.workbookReader() - if wb != nil { - for _, sheet := range wb.Sheets.Sheet { - if sheet.Name == trimSheetName(name) { - return sheet.SheetID - } + var idx = -1 + for index, sheet := range f.GetSheetList() { + if sheet == trimSheetName(name) { + idx = index } } - return 0 + return idx } // GetSheetMap provides a function to get worksheet and chartsheet name and -// index map of XLSX. For example: +// ID map of XLSX. For example: // // f, err := excelize.OpenFile("Book1.xlsx") // if err != nil { @@ -349,8 +387,20 @@ func (f *File) GetSheetMap() map[int]string { return sheetMap } -// getSheetMap provides a function to get worksheet and chartsheet name and -// XML file path map of XLSX. +// GetSheetList provides a function to get worksheet and chartsheet name list +// of workbook. +func (f *File) GetSheetList() (list []string) { + wb := f.workbookReader() + if wb != nil { + for _, sheet := range wb.Sheets.Sheet { + list = append(list, sheet.Name) + } + } + return +} + +// getSheetMap provides a function to get worksheet name and XML file path map +// of XLSX. func (f *File) getSheetMap() map[string]string { content := f.workbookReader() rels := f.relsReader("xl/_rels/workbook.xml.rels") @@ -397,7 +447,7 @@ func (f *File) SetSheetBackground(sheet, picture string) error { // value of the deleted worksheet, it will cause a file error when you open it. // This function will be invalid when only the one worksheet is left. func (f *File) DeleteSheet(name string) { - if f.SheetCount == 1 || f.GetSheetIndex(name) == 0 { + if f.SheetCount == 1 || f.GetSheetIndex(name) == -1 { return } sheetName := trimSheetName(name) @@ -474,7 +524,7 @@ func (f *File) deleteSheetFromContentTypes(target string) { // return err // func (f *File) CopySheet(from, to int) error { - if from < 1 || to < 1 || from == to || f.GetSheetName(from) == "" || f.GetSheetName(to) == "" { + if from < 0 || to < 0 || from == to || f.GetSheetName(from) == "" || f.GetSheetName(to) == "" { return errors.New("invalid worksheet index") } return f.copySheet(from, to) @@ -483,12 +533,14 @@ func (f *File) CopySheet(from, to int) error { // copySheet provides a function to duplicate a worksheet by gave source and // target worksheet name. func (f *File) copySheet(from, to int) error { - sheet, err := f.workSheetReader(f.GetSheetName(from)) + fromSheet := f.GetSheetName(from) + sheet, err := f.workSheetReader(fromSheet) if err != nil { return err } worksheet := deepcopy.Copy(sheet).(*xlsxWorksheet) - path := "xl/worksheets/sheet" + strconv.Itoa(to) + ".xml" + toSheetID := strconv.Itoa(f.getSheetID(f.GetSheetName(to))) + path := "xl/worksheets/sheet" + toSheetID + ".xml" if len(worksheet.SheetViews.SheetView) > 0 { worksheet.SheetViews.SheetView[0].TabSelected = false } @@ -496,8 +548,8 @@ func (f *File) copySheet(from, to int) error { worksheet.TableParts = nil worksheet.PageSetUp = nil f.Sheet[path] = worksheet - toRels := "xl/worksheets/_rels/sheet" + strconv.Itoa(to) + ".xml.rels" - fromRels := "xl/worksheets/_rels/sheet" + strconv.Itoa(from) + ".xml.rels" + toRels := "xl/worksheets/_rels/sheet" + toSheetID + ".xml.rels" + fromRels := "xl/worksheets/_rels/sheet" + strconv.Itoa(f.getSheetID(fromSheet)) + ".xml.rels" _, ok := f.XLSX[fromRels] if ok { f.XLSX[toRels] = f.XLSX[fromRels] @@ -1303,7 +1355,7 @@ func (f *File) SetDefinedName(definedName *DefinedName) error { Data: definedName.RefersTo, } if definedName.Scope != "" { - if sheetID := f.GetSheetIndex(definedName.Scope); sheetID != 0 { + if sheetID := f.getSheetID(definedName.Scope); sheetID != 0 { sheetID-- d.LocalSheetID = &sheetID } @@ -1312,7 +1364,7 @@ func (f *File) SetDefinedName(definedName *DefinedName) error { for _, dn := range wb.DefinedNames.DefinedName { var scope string if dn.LocalSheetID != nil { - scope = f.GetSheetName(*dn.LocalSheetID + 1) + scope = f.getSheetNameByID(*dn.LocalSheetID + 1) } if scope == definedName.Scope && dn.Name == definedName.Name { return errors.New("the same name already exists on the scope") @@ -1342,7 +1394,7 @@ func (f *File) DeleteDefinedName(definedName *DefinedName) error { for idx, dn := range wb.DefinedNames.DefinedName { var scope string if dn.LocalSheetID != nil { - scope = f.GetSheetName(*dn.LocalSheetID + 1) + scope = f.getSheetNameByID(*dn.LocalSheetID + 1) } if scope == definedName.Scope && dn.Name == definedName.Name { wb.DefinedNames.DefinedName = append(wb.DefinedNames.DefinedName[:idx], wb.DefinedNames.DefinedName[idx+1:]...) @@ -1367,7 +1419,7 @@ func (f *File) GetDefinedName() []DefinedName { Scope: "Workbook", } if dn.LocalSheetID != nil { - definedName.Scope = f.GetSheetName(*dn.LocalSheetID + 1) + definedName.Scope = f.getSheetNameByID(*dn.LocalSheetID + 1) } definedNames = append(definedNames, definedName) } @@ -1381,7 +1433,7 @@ func (f *File) GroupSheets(sheets []string) error { // check an active worksheet in group worksheets var inActiveSheet bool activeSheet := f.GetActiveSheetIndex() - sheetMap := f.GetSheetMap() + sheetMap := f.GetSheetList() for idx, sheetName := range sheetMap { for _, s := range sheets { if s == sheetName && idx == activeSheet { @@ -1416,16 +1468,15 @@ func (f *File) GroupSheets(sheets []string) error { // UngroupSheets provides a function to ungroup worksheets. func (f *File) UngroupSheets() error { activeSheet := f.GetActiveSheetIndex() - sheetMap := f.GetSheetMap() - for sheetID, sheet := range sheetMap { - if activeSheet == sheetID { + for index, sheet := range f.GetSheetList() { + if activeSheet == index { continue } - xlsx, _ := f.workSheetReader(sheet) - sheetViews := xlsx.SheetViews.SheetView + ws, _ := f.workSheetReader(sheet) + sheetViews := ws.SheetViews.SheetView if len(sheetViews) > 0 { for idx := range sheetViews { - xlsx.SheetViews.SheetView[idx].TabSelected = false + ws.SheetViews.SheetView[idx].TabSelected = false } } } -- cgit v1.2.1 From 2285d4dc718fb8b96c3b2291c63b39c57468b0b9 Mon Sep 17 00:00:00 2001 From: xuri Date: Fri, 24 Apr 2020 08:26:16 +0800 Subject: handle the cell without r attribute in a row element --- sheet.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 50081e8..8c7f754 100644 --- a/sheet.go +++ b/sheet.go @@ -237,7 +237,7 @@ func (f *File) SetActiveSheet(index int) { for idx, name := range f.GetSheetList() { xlsx, err := f.workSheetReader(name) if err != nil { - // Chartsheet + // Chartsheet or dialogsheet return } if xlsx.SheetViews == nil { @@ -365,8 +365,8 @@ func (f *File) GetSheetIndex(name string) int { return idx } -// GetSheetMap provides a function to get worksheet and chartsheet name and -// ID map of XLSX. For example: +// GetSheetMap provides a function to get worksheet, chartsheet and +// dialogsheet ID and name map of XLSX. For example: // // f, err := excelize.OpenFile("Book1.xlsx") // if err != nil { @@ -387,8 +387,8 @@ func (f *File) GetSheetMap() map[int]string { return sheetMap } -// GetSheetList provides a function to get worksheet and chartsheet name list -// of workbook. +// GetSheetList provides a function to get worksheet, chartsheet and +// dialogsheet name list of workbook. func (f *File) GetSheetList() (list []string) { wb := f.workbookReader() if wb != nil { -- cgit v1.2.1 From 48fc4c08a2a80f7826d20bf3fd5a018f8e6f3185 Mon Sep 17 00:00:00 2001 From: xuri Date: Sun, 3 May 2020 18:44:43 +0800 Subject: init formula calculation engine, ref #65 and #599 --- sheet.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index 8c7f754..fa858af 100644 --- a/sheet.go +++ b/sheet.go @@ -211,10 +211,10 @@ func replaceRelationshipsBytes(content []byte) []byte { return bytesReplace(content, oldXmlns, newXmlns, -1) } -// SetActiveSheet provides function to set default active worksheet of XLSX by -// given index. Note that active index is different from the index returned by -// function GetSheetMap(). It should be greater or equal to 0 and less than -// total worksheet numbers. +// SetActiveSheet provides a function to set the default active sheet of the +// workbook by a given index. Note that the active index is different from the +// ID returned by function GetSheetMap(). It should be greater or equal to 0 +// and less than the total worksheet numbers. func (f *File) SetActiveSheet(index int) { if index < 0 { index = 0 @@ -327,9 +327,9 @@ func (f *File) getSheetNameByID(ID int) string { return "" } -// GetSheetName provides a function to get worksheet name of XLSX by given -// worksheet index. If given sheet index is invalid, will return an empty -// string. +// GetSheetName provides a function to get the sheet name of the workbook by +// the given sheet index. If the given sheet index is invalid, it will return +// an empty string. func (f *File) GetSheetName(index int) (name string) { for idx, sheet := range f.GetSheetList() { if idx == index { @@ -352,9 +352,9 @@ func (f *File) getSheetID(name string) int { return ID } -// GetSheetIndex provides a function to get worksheet index of XLSX by given -// sheet name. If given worksheet name is invalid, will return an integer type -// value -1. +// GetSheetIndex provides a function to get a sheet index of the workbook by +// the given sheet name. If the given sheet name is invalid, it will return an +// integer type value -1. func (f *File) GetSheetIndex(name string) int { var idx = -1 for index, sheet := range f.GetSheetList() { @@ -365,8 +365,8 @@ func (f *File) GetSheetIndex(name string) int { return idx } -// GetSheetMap provides a function to get worksheet, chartsheet and -// dialogsheet ID and name map of XLSX. For example: +// GetSheetMap provides a function to get worksheets, chart sheets, dialog +// sheets ID and name map of the workbook. For example: // // f, err := excelize.OpenFile("Book1.xlsx") // if err != nil { @@ -387,8 +387,8 @@ func (f *File) GetSheetMap() map[int]string { return sheetMap } -// GetSheetList provides a function to get worksheet, chartsheet and -// dialogsheet name list of workbook. +// GetSheetList provides a function to get worksheets, chart sheets, and +// dialog sheets name list of the workbook. func (f *File) GetSheetList() (list []string) { wb := f.workbookReader() if wb != nil { -- cgit v1.2.1 From dfea8f96edc326717822ec9c4b92f462d0fe1255 Mon Sep 17 00:00:00 2001 From: xuri Date: Tue, 12 May 2020 23:26:26 +0800 Subject: - New API: SetSheetFormatPr and GetSheetFormatPr - typo fix, resolve #635 --- sheet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sheet.go') diff --git a/sheet.go b/sheet.go index fa858af..6a935b1 100644 --- a/sheet.go +++ b/sheet.go @@ -354,7 +354,7 @@ func (f *File) getSheetID(name string) int { // GetSheetIndex provides a function to get a sheet index of the workbook by // the given sheet name. If the given sheet name is invalid, it will return an -// integer type value -1. +// integer type value 0. func (f *File) GetSheetIndex(name string) int { var idx = -1 for index, sheet := range f.GetSheetList() { -- cgit v1.2.1