summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--col.go3
-rw-r--r--date.go13
-rw-r--r--excelize.go40
-rw-r--r--excelize_test.go53
-rw-r--r--go.mod1
-rw-r--r--logo.pngbin5207 -> 7085 bytes
-rw-r--r--sheet.go95
-rw-r--r--sheetpr.go20
-rw-r--r--sheetpr_test.go8
-rw-r--r--sheetview.go10
-rw-r--r--sheetview_test.go18
-rw-r--r--test/Book1.xlsxbin23099 -> 23153 bytes
-rw-r--r--test/MergeCell.xlsxbin0 -> 8583 bytes
-rw-r--r--xmlWorkbook.go2
-rw-r--r--xmlWorksheet.go7
15 files changed, 212 insertions, 58 deletions
diff --git a/col.go b/col.go
index 32cda12..05405bd 100644
--- a/col.go
+++ b/col.go
@@ -337,7 +337,8 @@ func (f *File) RemoveCol(sheet, column string) {
f.adjustHelper(sheet, col, -1, -1)
}
-// Completion column element tags of XML in a sheet.
+// completeCol provieds function to completion column element tags of XML in a
+// sheet.
func completeCol(xlsx *xlsxWorksheet, row, cell int) {
buffer := bytes.Buffer{}
for r := range xlsx.SheetData.Row {
diff --git a/date.go b/date.go
index 45f3040..a0b8ceb 100644
--- a/date.go
+++ b/date.go
@@ -90,12 +90,13 @@ func julianDateToGregorianTime(part1, part2 float64) time.Time {
return time.Date(year, time.Month(month), day, hours, minutes, seconds, nanoseconds, time.UTC)
}
-// By this point generations of programmers have repeated the algorithm sent
-// to the editor of "Communications of the ACM" in 1968 (published in CACM,
-// volume 11, number 10, October 1968, p.657). None of those programmers seems
-// to have found it necessary to explain the constants or variable names set
-// out by Henry F. Fliegel and Thomas C. Van Flandern. Maybe one day I'll buy
-// that jounal and expand an explanation here - that day is not today.
+// doTheFliegelAndVanFlandernAlgorithm; By this point generations of
+// programmers have repeated the algorithm sent to the editor of
+// "Communications of the ACM" in 1968 (published in CACM, volume 11, number
+// 10, October 1968, p.657). None of those programmers seems to have found it
+// necessary to explain the constants or variable names set out by Henry F.
+// Fliegel and Thomas C. Van Flandern. Maybe one day I'll buy that jounal and
+// expand an explanation here - that day is not today.
func doTheFliegelAndVanFlandernAlgorithm(jd int) (day, month, year int) {
l := jd + 68569
n := (4 * l) / 146097
diff --git a/excelize.go b/excelize.go
index 309a8d8..4b4aa32 100644
--- a/excelize.go
+++ b/excelize.go
@@ -411,3 +411,43 @@ func (f *File) adjustAutoFilterHelper(xlsx *xlsxWorksheet, column, rowIndex, off
}
}
}
+
+// GetMergeCells provides a function to get all merged cells from a worksheet currently.
+func (f *File) GetMergeCells(sheet string) []MergeCell {
+ mergeCells := []MergeCell{}
+
+ xlsx := f.workSheetReader(sheet)
+ if xlsx.MergeCells != nil {
+ for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
+ ref := xlsx.MergeCells.Cells[i].Ref
+ axis := strings.Split(ref, ":")[0]
+ mergeCells = append(mergeCells, []string{ref, f.GetCellValue(sheet, axis)})
+ }
+ }
+
+ return mergeCells
+}
+
+// MergeCell define a merged cell data.
+// It consists of the following structure.
+// example: []string{"D4:E10", "cell value"}
+type MergeCell []string
+
+// GetCellValue returns merged cell value.
+func (m *MergeCell) GetCellValue() string {
+ return (*m)[1]
+}
+
+// GetStartAxis returns the merge start axis.
+// example: "C2"
+func (m *MergeCell) GetStartAxis() string {
+ axis := strings.Split((*m)[0], ":")
+ return axis[0]
+}
+
+// GetEndAxis returns the merge end axis.
+// example: "D4"
+func (m *MergeCell) GetEndAxis() string {
+ axis := strings.Split((*m)[0], ":")
+ return axis[1]
+}
diff --git a/excelize_test.go b/excelize_test.go
index 89d2e98..6eb3692 100644
--- a/excelize_test.go
+++ b/excelize_test.go
@@ -374,6 +374,59 @@ func TestMergeCell(t *testing.T) {
}
}
+func TestGetMergeCells(t *testing.T) {
+ wants := []struct {
+ value string
+ start string
+ end string
+ }{
+ {
+ value: "A1",
+ start: "A1",
+ end: "B1",
+ },
+ {
+ value: "A2",
+ start: "A2",
+ end: "A3",
+ },
+ {
+ value: "A4",
+ start: "A4",
+ end: "B5",
+ },
+ {
+ value: "A7",
+ start: "A7",
+ end: "C10",
+ },
+ }
+
+ xlsx, err := OpenFile("./test/MergeCell.xlsx")
+ if err != nil {
+ t.Error(err)
+ }
+
+ mergeCells := xlsx.GetMergeCells("Sheet1")
+ if len(mergeCells) != len(wants) {
+ t.Fatalf("Expected count of merge cells %d, but got %d\n", len(wants), len(mergeCells))
+ }
+
+ for i, m := range mergeCells {
+ if wants[i].value != m.GetCellValue() {
+ t.Fatalf("Expected merged cell value %s, but got %s\n", wants[i].value, m.GetCellValue())
+ }
+
+ if wants[i].start != m.GetStartAxis() {
+ t.Fatalf("Expected merged cell value %s, but got %s\n", wants[i].start, m.GetStartAxis())
+ }
+
+ if wants[i].end != m.GetEndAxis() {
+ t.Fatalf("Expected merged cell value %s, but got %s\n", wants[i].end, m.GetEndAxis())
+ }
+ }
+}
+
func TestSetCellStyleAlignment(t *testing.T) {
xlsx, err := OpenFile("./test/Book2.xlsx")
if err != nil {
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..0536371
--- /dev/null
+++ b/go.mod
@@ -0,0 +1 @@
+module github.com/360EntSecGroup-Skylar/excelize
diff --git a/logo.png b/logo.png
index df9059d..32235b8 100644
--- a/logo.png
+++ b/logo.png
Binary files differ
diff --git a/sheet.go b/sheet.go
index 3021946..f546561 100644
--- a/sheet.go
+++ b/sheet.go
@@ -33,18 +33,27 @@ func (f *File) NewSheet(name string) int {
if f.GetSheetIndex(name) != 0 {
return f.SheetCount
}
+ f.DeleteSheet(name)
f.SheetCount++
+ wb := f.workbookReader()
+ sheetID := 0
+ for _, v := range wb.Sheets.Sheet {
+ if v.SheetID > sheetID {
+ sheetID = v.SheetID
+ }
+ }
+ sheetID++
// Update docProps/app.xml
f.setAppXML()
// Update [Content_Types].xml
- f.setContentTypes(f.SheetCount)
+ f.setContentTypes(sheetID)
// Create new sheet /xl/worksheets/sheet%d.xml
- f.setSheet(f.SheetCount, name)
+ f.setSheet(sheetID, name)
// Update xl/_rels/workbook.xml.rels
- rID := f.addXlsxWorkbookRels(f.SheetCount)
+ rID := f.addXlsxWorkbookRels(sheetID)
// Update xl/workbook.xml
- f.setWorkbook(name, rID)
- return f.SheetCount
+ f.setWorkbook(name, sheetID, rID)
+ return sheetID
}
// contentTypesReader provides a function to get the pointer to the
@@ -118,7 +127,8 @@ func trimCell(column []xlsxC) []xlsxC {
return col[0:i]
}
-// Read and update property of contents type of XLSX.
+// setContentTypes provides a function to read and update property of contents
+// type of XLSX.
func (f *File) setContentTypes(index int) {
content := f.contentTypesReader()
content.Overrides = append(content.Overrides, xlsxOverride{
@@ -127,7 +137,7 @@ func (f *File) setContentTypes(index int) {
})
}
-// Update sheet property by given index.
+// 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"
@@ -141,19 +151,11 @@ func (f *File) setSheet(index int, name string) {
// setWorkbook update workbook property of XLSX. Maximum 31 characters are
// allowed in sheet title.
-func (f *File) setWorkbook(name string, rid int) {
+func (f *File) setWorkbook(name string, sheetID, rid int) {
content := f.workbookReader()
- rID := 0
- for _, v := range content.Sheets.Sheet {
- t, _ := strconv.Atoi(v.SheetID)
- if t > rID {
- rID = t
- }
- }
- rID++
content.Sheets.Sheet = append(content.Sheets.Sheet, xlsxSheet{
Name: trimSheetName(name),
- SheetID: strconv.Itoa(rID),
+ SheetID: sheetID,
ID: "rId" + strconv.Itoa(rid),
})
}
@@ -209,13 +211,13 @@ func (f *File) setAppXML() {
f.saveFileList("docProps/app.xml", []byte(templateDocpropsApp))
}
-// 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.
+// 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(`<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
newXmlns := []byte(`<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x15" xmlns:x15="http://schemas.microsoft.com/office/spreadsheetml/2010/11/main">`)
@@ -230,18 +232,23 @@ func (f *File) SetActiveSheet(index int) {
if index < 1 {
index = 1
}
- index--
- content := f.workbookReader()
- if len(content.BookViews.WorkBookView) > 0 {
- content.BookViews.WorkBookView[0].ActiveTab = index
- } else {
- content.BookViews.WorkBookView = append(content.BookViews.WorkBookView, xlsxWorkBookView{
- ActiveTab: index,
- })
+ wb := f.workbookReader()
+ for activeTab, sheet := range wb.Sheets.Sheet {
+ if sheet.SheetID == index {
+ if len(wb.BookViews.WorkBookView) > 0 {
+ wb.BookViews.WorkBookView[0].ActiveTab = activeTab
+ } else {
+ wb.BookViews.WorkBookView = append(wb.BookViews.WorkBookView, xlsxWorkBookView{
+ ActiveTab: activeTab,
+ })
+ }
+ }
}
- index++
for idx, name := range f.GetSheetMap() {
xlsx := f.workSheetReader(name)
+ if len(xlsx.SheetViews.SheetView) > 0 {
+ xlsx.SheetViews.SheetView[0].TabSelected = false
+ }
if index == idx {
if len(xlsx.SheetViews.SheetView) > 0 {
xlsx.SheetViews.SheetView[0].TabSelected = true
@@ -250,10 +257,6 @@ func (f *File) SetActiveSheet(index int) {
TabSelected: true,
})
}
- } else {
- if len(xlsx.SheetViews.SheetView) > 0 {
- xlsx.SheetViews.SheetView[0].TabSelected = false
- }
}
}
}
@@ -261,21 +264,13 @@ 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 {
- buffer := bytes.Buffer{}
- content := f.workbookReader()
- for _, v := range content.Sheets.Sheet {
- xlsx := xlsxWorksheet{}
- buffer.WriteString("xl/worksheets/sheet")
- buffer.WriteString(strings.TrimPrefix(v.ID, "rId"))
- buffer.WriteString(".xml")
- _ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(buffer.String())), &xlsx)
+ for idx, name := range f.GetSheetMap() {
+ xlsx := f.workSheetReader(name)
for _, sheetView := range xlsx.SheetViews.SheetView {
if sheetView.TabSelected {
- ID, _ := strconv.Atoi(strings.TrimPrefix(v.ID, "rId"))
- return ID
+ return idx
}
}
- buffer.Reset()
}
return 0
}
@@ -404,8 +399,8 @@ func (f *File) DeleteSheet(name string) {
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" + strings.TrimPrefix(v.ID, "rId") + ".xml"
- rels := "xl/worksheets/_rels/sheet" + strings.TrimPrefix(v.ID, "rId") + ".xml.rels"
+ 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)
f.deleteSheetFromContentTypes(target)
delete(f.sheetMap, name)
diff --git a/sheetpr.go b/sheetpr.go
index e38b64e..57eebd4 100644
--- a/sheetpr.go
+++ b/sheetpr.go
@@ -31,8 +31,26 @@ type (
FitToPage bool
// AutoPageBreaks is a SheetPrOption
AutoPageBreaks bool
+ // OutlineSummaryBelow is an outlinePr, within SheetPr option
+ OutlineSummaryBelow bool
)
+func (o OutlineSummaryBelow) setSheetPrOption(pr *xlsxSheetPr) {
+ if pr.OutlinePr == nil {
+ pr.OutlinePr = new(xlsxOutlinePr)
+ }
+ pr.OutlinePr.SummaryBelow = bool(o)
+}
+
+func (o *OutlineSummaryBelow) getSheetPrOption(pr *xlsxSheetPr) {
+ // Excel default: true
+ if pr == nil || pr.OutlinePr == nil {
+ *o = true
+ return
+ }
+ *o = OutlineSummaryBelow(defaultTrue(&pr.OutlinePr.SummaryBelow))
+}
+
func (o CodeName) setSheetPrOption(pr *xlsxSheetPr) {
pr.CodeName = string(o)
}
@@ -115,6 +133,7 @@ func (o *AutoPageBreaks) getSheetPrOption(pr *xlsxSheetPr) {
// Published(bool)
// FitToPage(bool)
// AutoPageBreaks(bool)
+// OutlineSummaryBelow(bool)
func (f *File) SetSheetPrOptions(name string, opts ...SheetPrOption) error {
sheet := f.workSheetReader(name)
pr := sheet.SheetPr
@@ -137,6 +156,7 @@ func (f *File) SetSheetPrOptions(name string, opts ...SheetPrOption) error {
// Published(bool)
// FitToPage(bool)
// AutoPageBreaks(bool)
+// OutlineSummaryBelow(bool)
func (f *File) GetSheetPrOptions(name string, opts ...SheetPrOptionPtr) error {
sheet := f.workSheetReader(name)
pr := sheet.SheetPr
diff --git a/sheetpr_test.go b/sheetpr_test.go
index e7e7482..d9f5059 100644
--- a/sheetpr_test.go
+++ b/sheetpr_test.go
@@ -15,6 +15,7 @@ var _ = []excelize.SheetPrOption{
excelize.Published(false),
excelize.FitToPage(true),
excelize.AutoPageBreaks(true),
+ excelize.OutlineSummaryBelow(true),
}
var _ = []excelize.SheetPrOptionPtr{
@@ -23,6 +24,7 @@ var _ = []excelize.SheetPrOptionPtr{
(*excelize.Published)(nil),
(*excelize.FitToPage)(nil),
(*excelize.AutoPageBreaks)(nil),
+ (*excelize.OutlineSummaryBelow)(nil),
}
func ExampleFile_SetSheetPrOptions() {
@@ -35,6 +37,7 @@ func ExampleFile_SetSheetPrOptions() {
excelize.Published(false),
excelize.FitToPage(true),
excelize.AutoPageBreaks(true),
+ excelize.OutlineSummaryBelow(false),
); err != nil {
panic(err)
}
@@ -51,6 +54,7 @@ func ExampleFile_GetSheetPrOptions() {
published excelize.Published
fitToPage excelize.FitToPage
autoPageBreaks excelize.AutoPageBreaks
+ outlineSummaryBelow excelize.OutlineSummaryBelow
)
if err := xl.GetSheetPrOptions(sheet,
@@ -59,6 +63,7 @@ func ExampleFile_GetSheetPrOptions() {
&published,
&fitToPage,
&autoPageBreaks,
+ &outlineSummaryBelow,
); err != nil {
panic(err)
}
@@ -68,6 +73,7 @@ func ExampleFile_GetSheetPrOptions() {
fmt.Println("- published:", published)
fmt.Println("- fitToPage:", fitToPage)
fmt.Println("- autoPageBreaks:", autoPageBreaks)
+ fmt.Println("- outlineSummaryBelow:", outlineSummaryBelow)
// Output:
// Defaults:
// - codeName: ""
@@ -75,6 +81,7 @@ func ExampleFile_GetSheetPrOptions() {
// - published: true
// - fitToPage: false
// - autoPageBreaks: false
+ // - outlineSummaryBelow: true
}
func TestSheetPrOptions(t *testing.T) {
@@ -88,6 +95,7 @@ func TestSheetPrOptions(t *testing.T) {
{new(excelize.Published), excelize.Published(false)},
{new(excelize.FitToPage), excelize.FitToPage(true)},
{new(excelize.AutoPageBreaks), excelize.AutoPageBreaks(true)},
+ {new(excelize.OutlineSummaryBelow), excelize.OutlineSummaryBelow(false)},
} {
opt := test.nonDefault
t.Logf("option %T", opt)
diff --git a/sheetview.go b/sheetview.go
index e76325c..37a0c39 100644
--- a/sheetview.go
+++ b/sheetview.go
@@ -35,6 +35,8 @@ type (
ShowRowColHeaders bool
// ZoomScale is a SheetViewOption.
ZoomScale float64
+ // TopLeftCell is a SheetViewOption.
+ TopLeftCell string
/* TODO
// ShowWhiteSpace is a SheetViewOption.
ShowWhiteSpace bool
@@ -47,6 +49,14 @@ type (
// Defaults for each option are described in XML schema for CT_SheetView
+func (o TopLeftCell) setSheetViewOption(view *xlsxSheetView) {
+ view.TopLeftCell = string(o)
+}
+
+func (o *TopLeftCell) getSheetViewOption(view *xlsxSheetView) {
+ *o = TopLeftCell(string(view.TopLeftCell))
+}
+
func (o DefaultGridColor) setSheetViewOption(view *xlsxSheetView) {
view.DefaultGridColor = boolPtr(bool(o))
}
diff --git a/sheetview_test.go b/sheetview_test.go
index c580906..ee81d5b 100644
--- a/sheetview_test.go
+++ b/sheetview_test.go
@@ -13,12 +13,14 @@ var _ = []excelize.SheetViewOption{
excelize.ShowFormulas(false),
excelize.ShowGridLines(true),
excelize.ShowRowColHeaders(true),
+ excelize.TopLeftCell("B2"),
// SheetViewOptionPtr are also SheetViewOption
new(excelize.DefaultGridColor),
new(excelize.RightToLeft),
new(excelize.ShowFormulas),
new(excelize.ShowGridLines),
new(excelize.ShowRowColHeaders),
+ new(excelize.TopLeftCell),
}
var _ = []excelize.SheetViewOptionPtr{
@@ -27,6 +29,7 @@ var _ = []excelize.SheetViewOptionPtr{
(*excelize.ShowFormulas)(nil),
(*excelize.ShowGridLines)(nil),
(*excelize.ShowRowColHeaders)(nil),
+ (*excelize.TopLeftCell)(nil),
}
func ExampleFile_SetSheetViewOptions() {
@@ -40,6 +43,7 @@ func ExampleFile_SetSheetViewOptions() {
excelize.ShowGridLines(true),
excelize.ShowRowColHeaders(true),
excelize.ZoomScale(80),
+ excelize.TopLeftCell("C3"),
); err != nil {
panic(err)
}
@@ -91,6 +95,7 @@ func ExampleFile_GetSheetViewOptions() {
showGridLines excelize.ShowGridLines
showRowColHeaders excelize.ShowRowColHeaders
zoomScale excelize.ZoomScale
+ topLeftCell excelize.TopLeftCell
)
if err := xl.GetSheetViewOptions(sheet, 0,
@@ -100,6 +105,7 @@ func ExampleFile_GetSheetViewOptions() {
&showGridLines,
&showRowColHeaders,
&zoomScale,
+ &topLeftCell,
); err != nil {
panic(err)
}
@@ -111,6 +117,15 @@ func ExampleFile_GetSheetViewOptions() {
fmt.Println("- showGridLines:", showGridLines)
fmt.Println("- showRowColHeaders:", showRowColHeaders)
fmt.Println("- zoomScale:", zoomScale)
+ fmt.Println("- topLeftCell:", `"`+topLeftCell+`"`)
+
+ if err := xl.SetSheetViewOptions(sheet, 0, excelize.TopLeftCell("B2")); err != nil {
+ panic(err)
+ }
+
+ if err := xl.GetSheetViewOptions(sheet, 0, &topLeftCell); err != nil {
+ panic(err)
+ }
if err := xl.SetSheetViewOptions(sheet, 0, excelize.ShowGridLines(false)); err != nil {
panic(err)
@@ -122,6 +137,7 @@ func ExampleFile_GetSheetViewOptions() {
fmt.Println("After change:")
fmt.Println("- showGridLines:", showGridLines)
+ fmt.Println("- topLeftCell:", topLeftCell)
// Output:
// Default:
@@ -131,8 +147,10 @@ func ExampleFile_GetSheetViewOptions() {
// - showGridLines: true
// - showRowColHeaders: true
// - zoomScale: 0
+ // - topLeftCell: ""
// After change:
// - showGridLines: false
+ // - topLeftCell: B2
}
func TestSheetViewOptionsErrors(t *testing.T) {
diff --git a/test/Book1.xlsx b/test/Book1.xlsx
index 84c43d1..2ef1121 100644
--- a/test/Book1.xlsx
+++ b/test/Book1.xlsx
Binary files differ
diff --git a/test/MergeCell.xlsx b/test/MergeCell.xlsx
new file mode 100644
index 0000000..d4dad18
--- /dev/null
+++ b/test/MergeCell.xlsx
Binary files differ
diff --git a/xmlWorkbook.go b/xmlWorkbook.go
index f00a0b8..6572033 100644
--- a/xmlWorkbook.go
+++ b/xmlWorkbook.go
@@ -151,7 +151,7 @@ type xlsxSheets struct {
// not checked it for completeness - it does as much as I need.
type xlsxSheet struct {
Name string `xml:"name,attr,omitempty"`
- SheetID string `xml:"sheetId,attr,omitempty"`
+ SheetID int `xml:"sheetId,attr,omitempty"`
ID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
State string `xml:"state,attr,omitempty"`
}
diff --git a/xmlWorksheet.go b/xmlWorksheet.go
index 42f8ddb..d35b40e 100644
--- a/xmlWorksheet.go
+++ b/xmlWorksheet.go
@@ -211,6 +211,13 @@ type xlsxSheetPr struct {
TransitionEntry bool `xml:"transitionEntry,attr,omitempty"`
TabColor *xlsxTabColor `xml:"tabColor,omitempty"`
PageSetUpPr *xlsxPageSetUpPr `xml:"pageSetUpPr,omitempty"`
+ OutlinePr *xlsxOutlinePr `xml:"outlinePr,omitempty"`
+}
+
+// xlsxOutlinePr maps to the outlinePr element
+// SummaryBelow allows you to adjust the direction of grouper controls
+type xlsxOutlinePr struct {
+ SummaryBelow bool `xml:"summaryBelow,attr"`
}
// xlsxPageSetUpPr directly maps the pageSetupPr element in the namespace