This closes #1358, made a refactor with breaking changes, see details:

This made a refactor with breaking changes:

Motivation and Context

When I decided to add set horizontal centered support for this library to resolve #1358, the reason I made this huge breaking change was:

- There are too many exported types for set sheet view, properties, and format properties, although a function using the functional options pattern can be optimized by returning an anonymous function, these types or property set or get function has no binding categorization, so I change these functions like `SetAppProps` to accept a pointer of options structure.
- Users can not easily find out which properties should be in the `SetSheetPrOptions` or `SetSheetFormatPr` categories
- Nested properties cannot proceed modify easily

Introduce 5 new export data types:
`HeaderFooterOptions`, `PageLayoutMarginsOptions`, `PageLayoutOptions`, `SheetPropsOptions`, and `ViewOptions`

Rename 4 exported data types:
- Rename `PivotTableOption` to `PivotTableOptions`
- Rename `FormatHeaderFooter` to `HeaderFooterOptions`
- Rename `FormatSheetProtection` to `SheetProtectionOptions`
- Rename `SparklineOption` to `SparklineOptions`

Remove 54 exported types:
`AutoPageBreaks`, `BaseColWidth`, `BlackAndWhite`, `CodeName`, `CustomHeight`, `Date1904`, `DefaultColWidth`, `DefaultGridColor`, `DefaultRowHeight`, `EnableFormatConditionsCalculation`, `FilterPrivacy`, `FirstPageNumber`, `FitToHeight`, `FitToPage`, `FitToWidth`, `OutlineSummaryBelow`, `PageLayoutOption`, `PageLayoutOptionPtr`, `PageLayoutOrientation`, `PageLayoutPaperSize`, `PageLayoutScale`, `PageMarginBottom`, `PageMarginFooter`, `PageMarginHeader`, `PageMarginLeft`, `PageMarginRight`, `PageMarginsOptions`, `PageMarginsOptionsPtr`, `PageMarginTop`, `Published`, `RightToLeft`, `SheetFormatPrOptions`, `SheetFormatPrOptionsPtr`, `SheetPrOption`, `SheetPrOptionPtr`, `SheetViewOption`, `SheetViewOptionPtr`, `ShowFormulas`, `ShowGridLines`, `ShowRowColHeaders`, `ShowRuler`, `ShowZeros`, `TabColorIndexed`, `TabColorRGB`, `TabColorTheme`, `TabColorTint`, `ThickBottom`, `ThickTop`, `TopLeftCell`, `View`, `WorkbookPrOption`, `WorkbookPrOptionPtr`, `ZeroHeight` and `ZoomScale`

Remove 2 exported constants:
`OrientationPortrait` and `OrientationLandscape`

Change 8 functions:
- Change the `func (f *File) SetPageLayout(sheet string, opts ...PageLayoutOption) error` to `func (f *File) SetPageLayout(sheet string, opts *PageLayoutOptions) error`
- Change the `func (f *File) GetPageLayout(sheet string, opts ...PageLayoutOptionPtr) error` to `func (f *File) GetPageLayout(sheet string) (PageLayoutOptions, error)`
- Change the `func (f *File) SetPageMargins(sheet string, opts ...PageMarginsOptions) error` to `func (f *File) SetPageMargins(sheet string, opts *PageLayoutMarginsOptions) error`
- Change the `func (f *File) GetPageMargins(sheet string, opts ...PageMarginsOptionsPtr) error` to `func (f *File) GetPageMargins(sheet string) (PageLayoutMarginsOptions, error)`
- Change the `func (f *File) SetSheetViewOptions(sheet string, viewIndex int, opts ...SheetViewOption) error` to `func (f *File) SetSheetView(sheet string, viewIndex int, opts *ViewOptions) error`
- Change the `func (f *File) GetSheetViewOptions(sheet string, viewIndex int, opts ...SheetViewOptionPtr) error` to `func (f *File) GetSheetView(sheet string, viewIndex int) (ViewOptions, error)`
- Change the `func (f *File) SetWorkbookPrOptions(opts ...WorkbookPrOption) error` to `func (f *File) SetWorkbookProps(opts *WorkbookPropsOptions) error`
- Change the `func (f *File) GetWorkbookPrOptions(opts ...WorkbookPrOptionPtr) error` to `func (f *File) GetWorkbookProps() (WorkbookPropsOptions, error)`

Introduce new function to instead of existing functions:
- New function `func (f *File) SetSheetProps(sheet string, opts *SheetPropsOptions) error` instead of `func (f *File) SetSheetPrOptions(sheet string, opts ...SheetPrOption) error` and `func (f *File) SetSheetFormatPr(sheet string, opts ...SheetFormatPrOption
This commit is contained in:
xuri 2022-09-29 22:00:21 +08:00
parent efcf599dfe
commit 53a495563a
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
33 changed files with 1364 additions and 2643 deletions

View File

@ -469,30 +469,30 @@ var (
} }
) )
// parseFormatChartSet provides a function to parse the format settings of the // parseChartOptions provides a function to parse the format settings of the
// chart with default value. // chart with default value.
func parseFormatChartSet(formatSet string) (*formatChart, error) { func parseChartOptions(opts string) (*chartOptions, error) {
format := formatChart{ options := chartOptions{
Dimension: formatChartDimension{ Dimension: chartDimensionOptions{
Width: 480, Width: 480,
Height: 290, Height: 290,
}, },
Format: formatPicture{ Format: pictureOptions{
FPrintsWithSheet: true, FPrintsWithSheet: true,
XScale: 1, XScale: 1,
YScale: 1, YScale: 1,
}, },
Legend: formatChartLegend{ Legend: chartLegendOptions{
Position: "bottom", Position: "bottom",
}, },
Title: formatChartTitle{ Title: chartTitleOptions{
Name: " ", Name: " ",
}, },
VaryColors: true, VaryColors: true,
ShowBlanksAs: "gap", ShowBlanksAs: "gap",
} }
err := json.Unmarshal([]byte(formatSet), &format) err := json.Unmarshal([]byte(opts), &options)
return &format, err return &options, err
} }
// AddChart provides the method to add chart in a sheet by given chart format // AddChart provides the method to add chart in a sheet by given chart format
@ -881,13 +881,13 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) {
// fmt.Println(err) // fmt.Println(err)
// } // }
// } // }
func (f *File) AddChart(sheet, cell, format string, combo ...string) error { func (f *File) AddChart(sheet, cell, opts string, combo ...string) error {
// Read sheet data. // Read sheet data.
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return err return err
} }
formatSet, comboCharts, err := f.getFormatChart(format, combo) options, comboCharts, err := f.getChartOptions(opts, combo)
if err != nil { if err != nil {
return err return err
} }
@ -898,11 +898,11 @@ func (f *File) AddChart(sheet, cell, format string, combo ...string) error {
drawingID, drawingXML = f.prepareDrawing(ws, drawingID, sheet, drawingXML) drawingID, drawingXML = f.prepareDrawing(ws, drawingID, sheet, drawingXML)
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels" drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "") drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "")
err = f.addDrawingChart(sheet, drawingXML, cell, formatSet.Dimension.Width, formatSet.Dimension.Height, drawingRID, &formatSet.Format) err = f.addDrawingChart(sheet, drawingXML, cell, options.Dimension.Width, options.Dimension.Height, drawingRID, &options.Format)
if err != nil { if err != nil {
return err return err
} }
f.addChart(formatSet, comboCharts) f.addChart(options, comboCharts)
f.addContentTypePart(chartID, "chart") f.addContentTypePart(chartID, "chart")
f.addContentTypePart(drawingID, "drawings") f.addContentTypePart(drawingID, "drawings")
f.addSheetNameSpace(sheet, SourceRelationship) f.addSheetNameSpace(sheet, SourceRelationship)
@ -913,12 +913,12 @@ func (f *File) AddChart(sheet, cell, format string, combo ...string) error {
// format set (such as offset, scale, aspect ratio setting and print settings) // format set (such as offset, scale, aspect ratio setting and print settings)
// and properties set. In Excel a chartsheet is a worksheet that only contains // and properties set. In Excel a chartsheet is a worksheet that only contains
// a chart. // a chart.
func (f *File) AddChartSheet(sheet, format string, combo ...string) error { func (f *File) AddChartSheet(sheet, opts string, combo ...string) error {
// Check if the worksheet already exists // Check if the worksheet already exists
if f.GetSheetIndex(sheet) != -1 { if f.GetSheetIndex(sheet) != -1 {
return ErrExistsWorksheet return ErrExistsWorksheet
} }
formatSet, comboCharts, err := f.getFormatChart(format, combo) options, comboCharts, err := f.getChartOptions(opts, combo)
if err != nil { if err != nil {
return err return err
} }
@ -945,8 +945,8 @@ func (f *File) AddChartSheet(sheet, format string, combo ...string) error {
f.prepareChartSheetDrawing(&cs, drawingID, sheet) f.prepareChartSheetDrawing(&cs, drawingID, sheet)
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels" drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "") drawingRID := f.addRels(drawingRels, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "")
f.addSheetDrawingChart(drawingXML, drawingRID, &formatSet.Format) f.addSheetDrawingChart(drawingXML, drawingRID, &options.Format)
f.addChart(formatSet, comboCharts) f.addChart(options, comboCharts)
f.addContentTypePart(chartID, "chart") f.addContentTypePart(chartID, "chart")
f.addContentTypePart(sheetID, "chartsheet") f.addContentTypePart(sheetID, "chartsheet")
f.addContentTypePart(drawingID, "drawings") f.addContentTypePart(drawingID, "drawings")
@ -960,45 +960,45 @@ func (f *File) AddChartSheet(sheet, format string, combo ...string) error {
return err return err
} }
// getFormatChart provides a function to check format set of the chart and // getChartOptions provides a function to check format set of the chart and
// create chart format. // create chart format.
func (f *File) getFormatChart(format string, combo []string) (*formatChart, []*formatChart, error) { func (f *File) getChartOptions(opts string, combo []string) (*chartOptions, []*chartOptions, error) {
var comboCharts []*formatChart var comboCharts []*chartOptions
formatSet, err := parseFormatChartSet(format) options, err := parseChartOptions(opts)
if err != nil { if err != nil {
return formatSet, comboCharts, err return options, comboCharts, err
} }
for _, comboFormat := range combo { for _, comboFormat := range combo {
comboChart, err := parseFormatChartSet(comboFormat) comboChart, err := parseChartOptions(comboFormat)
if err != nil { if err != nil {
return formatSet, comboCharts, err return options, comboCharts, err
} }
if _, ok := chartValAxNumFmtFormatCode[comboChart.Type]; !ok { if _, ok := chartValAxNumFmtFormatCode[comboChart.Type]; !ok {
return formatSet, comboCharts, newUnsupportedChartType(comboChart.Type) return options, comboCharts, newUnsupportedChartType(comboChart.Type)
} }
comboCharts = append(comboCharts, comboChart) comboCharts = append(comboCharts, comboChart)
} }
if _, ok := chartValAxNumFmtFormatCode[formatSet.Type]; !ok { if _, ok := chartValAxNumFmtFormatCode[options.Type]; !ok {
return formatSet, comboCharts, newUnsupportedChartType(formatSet.Type) return options, comboCharts, newUnsupportedChartType(options.Type)
} }
return formatSet, comboCharts, err return options, comboCharts, err
} }
// DeleteChart provides a function to delete chart in spreadsheet by given // DeleteChart provides a function to delete chart in spreadsheet by given
// worksheet name and cell reference. // worksheet name and cell reference.
func (f *File) DeleteChart(sheet, cell string) (err error) { func (f *File) DeleteChart(sheet, cell string) error {
col, row, err := CellNameToCoordinates(cell) col, row, err := CellNameToCoordinates(cell)
if err != nil { if err != nil {
return return err
} }
col-- col--
row-- row--
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return return err
} }
if ws.Drawing == nil { if ws.Drawing == nil {
return return err
} }
drawingXML := strings.ReplaceAll(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl") drawingXML := strings.ReplaceAll(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl")
return f.deleteDrawing(col, row, drawingXML, "Chart") return f.deleteDrawing(col, row, drawingXML, "Chart")

View File

@ -23,15 +23,15 @@ import (
"strings" "strings"
) )
// parseFormatCommentsSet provides a function to parse the format settings of // parseCommentOptions provides a function to parse the format settings of
// the comment with default value. // the comment with default value.
func parseFormatCommentsSet(formatSet string) (*formatComment, error) { func parseCommentOptions(opts string) (*commentOptions, error) {
format := formatComment{ options := commentOptions{
Author: "Author:", Author: "Author:",
Text: " ", Text: " ",
} }
err := json.Unmarshal([]byte(formatSet), &format) err := json.Unmarshal([]byte(opts), &options)
return &format, err return &options, err
} }
// GetComments retrieves all comments and returns a map of worksheet name to // GetComments retrieves all comments and returns a map of worksheet name to
@ -93,8 +93,8 @@ func (f *File) getSheetComments(sheetFile string) string {
// comment in Sheet1!$A$30: // comment in Sheet1!$A$30:
// //
// err := f.AddComment("Sheet1", "A30", `{"author":"Excelize: ","text":"This is a comment."}`) // err := f.AddComment("Sheet1", "A30", `{"author":"Excelize: ","text":"This is a comment."}`)
func (f *File) AddComment(sheet, cell, format string) error { func (f *File) AddComment(sheet, cell, opts string) error {
formatSet, err := parseFormatCommentsSet(format) options, err := parseCommentOptions(opts)
if err != nil { if err != nil {
return err return err
} }
@ -123,19 +123,19 @@ func (f *File) AddComment(sheet, cell, format string) error {
} }
commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml" commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml"
var colCount int var colCount int
for i, l := range strings.Split(formatSet.Text, "\n") { for i, l := range strings.Split(options.Text, "\n") {
if ll := len(l); ll > colCount { if ll := len(l); ll > colCount {
if i == 0 { if i == 0 {
ll += len(formatSet.Author) ll += len(options.Author)
} }
colCount = ll colCount = ll
} }
} }
err = f.addDrawingVML(commentID, drawingVML, cell, strings.Count(formatSet.Text, "\n")+1, colCount) err = f.addDrawingVML(commentID, drawingVML, cell, strings.Count(options.Text, "\n")+1, colCount)
if err != nil { if err != nil {
return err return err
} }
f.addComment(commentsXML, cell, formatSet) f.addComment(commentsXML, cell, options)
f.addContentTypePart(commentID, "comments") f.addContentTypePart(commentID, "comments")
return err return err
} }
@ -144,11 +144,12 @@ func (f *File) AddComment(sheet, cell, format string) error {
// worksheet name. For example, delete the comment in Sheet1!$A$30: // worksheet name. For example, delete the comment in Sheet1!$A$30:
// //
// err := f.DeleteComment("Sheet1", "A30") // err := f.DeleteComment("Sheet1", "A30")
func (f *File) DeleteComment(sheet, cell string) (err error) { func (f *File) DeleteComment(sheet, cell string) error {
var err error
sheetXMLPath, ok := f.getSheetXMLPath(sheet) sheetXMLPath, ok := f.getSheetXMLPath(sheet)
if !ok { if !ok {
err = newNoExistSheetError(sheet) err = newNoExistSheetError(sheet)
return return err
} }
commentsXML := f.getSheetComments(filepath.Base(sheetXMLPath)) commentsXML := f.getSheetComments(filepath.Base(sheetXMLPath))
if !strings.HasPrefix(commentsXML, "/") { if !strings.HasPrefix(commentsXML, "/") {
@ -173,7 +174,7 @@ func (f *File) DeleteComment(sheet, cell string) (err error) {
} }
f.Comments[commentsXML] = comments f.Comments[commentsXML] = comments
} }
return return err
} }
// addDrawingVML provides a function to create comment as // addDrawingVML provides a function to create comment as
@ -279,9 +280,9 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount,
// addComment provides a function to create chart as xl/comments%d.xml by // addComment provides a function to create chart as xl/comments%d.xml by
// given cell and format sets. // given cell and format sets.
func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) { func (f *File) addComment(commentsXML, cell string, opts *commentOptions) {
a := formatSet.Author a := opts.Author
t := formatSet.Text t := opts.Text
if len(a) > MaxFieldLength { if len(a) > MaxFieldLength {
a = a[:MaxFieldLength] a = a[:MaxFieldLength]
} }
@ -291,10 +292,10 @@ func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
comments := f.commentsReader(commentsXML) comments := f.commentsReader(commentsXML)
authorID := 0 authorID := 0
if comments == nil { if comments == nil {
comments = &xlsxComments{Authors: xlsxAuthor{Author: []string{formatSet.Author}}} comments = &xlsxComments{Authors: xlsxAuthor{Author: []string{opts.Author}}}
} }
if inStrSlice(comments.Authors.Author, formatSet.Author, true) == -1 { if inStrSlice(comments.Authors.Author, opts.Author, true) == -1 {
comments.Authors.Author = append(comments.Authors.Author, formatSet.Author) comments.Authors.Author = append(comments.Authors.Author, opts.Author)
authorID = len(comments.Authors.Author) - 1 authorID = len(comments.Authors.Author) - 1
} }
defaultFont := f.GetDefaultFont() defaultFont := f.GetDefaultFont()

View File

@ -64,19 +64,20 @@ import (
// HyperlinksChanged: true, // HyperlinksChanged: true,
// AppVersion: "16.0000", // AppVersion: "16.0000",
// }) // })
func (f *File) SetAppProps(appProperties *AppProperties) (err error) { func (f *File) SetAppProps(appProperties *AppProperties) error {
var ( var (
app *xlsxProperties app *xlsxProperties
fields []string err error
output []byte
immutable, mutable reflect.Value
field string field string
fields []string
immutable, mutable reflect.Value
output []byte
) )
app = new(xlsxProperties) app = new(xlsxProperties)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsApp)))). if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsApp)))).
Decode(app); err != nil && err != io.EOF { Decode(app); err != nil && err != io.EOF {
err = newDecodeXMLError(err) err = newDecodeXMLError(err)
return return err
} }
fields = []string{"Application", "ScaleCrop", "DocSecurity", "Company", "LinksUpToDate", "HyperlinksChanged", "AppVersion"} fields = []string{"Application", "ScaleCrop", "DocSecurity", "Company", "LinksUpToDate", "HyperlinksChanged", "AppVersion"}
immutable, mutable = reflect.ValueOf(*appProperties), reflect.ValueOf(app).Elem() immutable, mutable = reflect.ValueOf(*appProperties), reflect.ValueOf(app).Elem()
@ -94,7 +95,7 @@ func (f *File) SetAppProps(appProperties *AppProperties) (err error) {
app.Vt = NameSpaceDocumentPropertiesVariantTypes.Value app.Vt = NameSpaceDocumentPropertiesVariantTypes.Value
output, err = xml.Marshal(app) output, err = xml.Marshal(app)
f.saveFileList(defaultXMLPathDocPropsApp, output) f.saveFileList(defaultXMLPathDocPropsApp, output)
return return err
} }
// GetAppProps provides a function to get document application properties. // GetAppProps provides a function to get document application properties.
@ -167,23 +168,24 @@ func (f *File) GetAppProps() (ret *AppProperties, err error) {
// Language: "en-US", // Language: "en-US",
// Version: "1.0.0", // Version: "1.0.0",
// }) // })
func (f *File) SetDocProps(docProperties *DocProperties) (err error) { func (f *File) SetDocProps(docProperties *DocProperties) error {
var ( var (
core *decodeCoreProperties core *decodeCoreProperties
newProps *xlsxCoreProperties err error
fields []string
output []byte
immutable, mutable reflect.Value
field, val string field, val string
fields []string
immutable, mutable reflect.Value
newProps *xlsxCoreProperties
output []byte
) )
core = new(decodeCoreProperties) core = new(decodeCoreProperties)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsCore)))). if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsCore)))).
Decode(core); err != nil && err != io.EOF { Decode(core); err != nil && err != io.EOF {
err = newDecodeXMLError(err) err = newDecodeXMLError(err)
return return err
} }
newProps, err = &xlsxCoreProperties{ newProps = &xlsxCoreProperties{
Dc: NameSpaceDublinCore, Dc: NameSpaceDublinCore,
Dcterms: NameSpaceDublinCoreTerms, Dcterms: NameSpaceDublinCoreTerms,
Dcmitype: NameSpaceDublinCoreMetadataInitiative, Dcmitype: NameSpaceDublinCoreMetadataInitiative,
@ -200,7 +202,7 @@ func (f *File) SetDocProps(docProperties *DocProperties) (err error) {
ContentStatus: core.ContentStatus, ContentStatus: core.ContentStatus,
Category: core.Category, Category: core.Category,
Version: core.Version, Version: core.Version,
}, nil }
if core.Created != nil { if core.Created != nil {
newProps.Created = &xlsxDcTerms{Type: core.Created.Type, Text: core.Created.Text} newProps.Created = &xlsxDcTerms{Type: core.Created.Type, Text: core.Created.Text}
} }
@ -226,7 +228,7 @@ func (f *File) SetDocProps(docProperties *DocProperties) (err error) {
output, err = xml.Marshal(newProps) output, err = xml.Marshal(newProps)
f.saveFileList(defaultXMLPathDocPropsCore, output) f.saveFileList(defaultXMLPathDocPropsCore, output)
return return err
} }
// GetDocProps provides a function to get document core properties. // GetDocProps provides a function to get document core properties.

View File

@ -56,7 +56,7 @@ func (f *File) prepareChartSheetDrawing(cs *xlsxChartsheet, drawingID int, sheet
// addChart provides a function to create chart as xl/charts/chart%d.xml by // addChart provides a function to create chart as xl/charts/chart%d.xml by
// given format sets. // given format sets.
func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) { func (f *File) addChart(opts *chartOptions, comboCharts []*chartOptions) {
count := f.countCharts() count := f.countCharts()
xlsxChartSpace := xlsxChartSpace{ xlsxChartSpace := xlsxChartSpace{
XMLNSa: NameSpaceDrawingML.Value, XMLNSa: NameSpaceDrawingML.Value,
@ -101,7 +101,7 @@ func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) {
Lang: "en-US", Lang: "en-US",
AltLang: "en-US", AltLang: "en-US",
}, },
T: formatSet.Title.Name, T: opts.Title.Name,
}, },
}, },
}, },
@ -124,10 +124,10 @@ func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) {
Overlay: &attrValBool{Val: boolPtr(false)}, Overlay: &attrValBool{Val: boolPtr(false)},
}, },
View3D: &cView3D{ View3D: &cView3D{
RotX: &attrValInt{Val: intPtr(chartView3DRotX[formatSet.Type])}, RotX: &attrValInt{Val: intPtr(chartView3DRotX[opts.Type])},
RotY: &attrValInt{Val: intPtr(chartView3DRotY[formatSet.Type])}, RotY: &attrValInt{Val: intPtr(chartView3DRotY[opts.Type])},
Perspective: &attrValInt{Val: intPtr(chartView3DPerspective[formatSet.Type])}, Perspective: &attrValInt{Val: intPtr(chartView3DPerspective[opts.Type])},
RAngAx: &attrValInt{Val: intPtr(chartView3DRAngAx[formatSet.Type])}, RAngAx: &attrValInt{Val: intPtr(chartView3DRAngAx[opts.Type])},
}, },
Floor: &cThicknessSpPr{ Floor: &cThicknessSpPr{
Thickness: &attrValInt{Val: intPtr(0)}, Thickness: &attrValInt{Val: intPtr(0)},
@ -140,12 +140,12 @@ func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) {
}, },
PlotArea: &cPlotArea{}, PlotArea: &cPlotArea{},
Legend: &cLegend{ Legend: &cLegend{
LegendPos: &attrValString{Val: stringPtr(chartLegendPosition[formatSet.Legend.Position])}, LegendPos: &attrValString{Val: stringPtr(chartLegendPosition[opts.Legend.Position])},
Overlay: &attrValBool{Val: boolPtr(false)}, Overlay: &attrValBool{Val: boolPtr(false)},
}, },
PlotVisOnly: &attrValBool{Val: boolPtr(false)}, PlotVisOnly: &attrValBool{Val: boolPtr(false)},
DispBlanksAs: &attrValString{Val: stringPtr(formatSet.ShowBlanksAs)}, DispBlanksAs: &attrValString{Val: stringPtr(opts.ShowBlanksAs)},
ShowDLblsOverMax: &attrValBool{Val: boolPtr(false)}, ShowDLblsOverMax: &attrValBool{Val: boolPtr(false)},
}, },
SpPr: &cSpPr{ SpPr: &cSpPr{
@ -181,7 +181,7 @@ func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) {
}, },
}, },
} }
plotAreaFunc := map[string]func(*formatChart) *cPlotArea{ plotAreaFunc := map[string]func(*chartOptions) *cPlotArea{
Area: f.drawBaseChart, Area: f.drawBaseChart,
AreaStacked: f.drawBaseChart, AreaStacked: f.drawBaseChart,
AreaPercentStacked: f.drawBaseChart, AreaPercentStacked: f.drawBaseChart,
@ -237,7 +237,7 @@ func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) {
Bubble: f.drawBaseChart, Bubble: f.drawBaseChart,
Bubble3D: f.drawBaseChart, Bubble3D: f.drawBaseChart,
} }
if formatSet.Legend.None { if opts.Legend.None {
xlsxChartSpace.Chart.Legend = nil xlsxChartSpace.Chart.Legend = nil
} }
addChart := func(c, p *cPlotArea) { addChart := func(c, p *cPlotArea) {
@ -250,8 +250,8 @@ func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) {
immutable.FieldByName(mutable.Type().Field(i).Name).Set(field) immutable.FieldByName(mutable.Type().Field(i).Name).Set(field)
} }
} }
addChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[formatSet.Type](formatSet)) addChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[opts.Type](opts))
order := len(formatSet.Series) order := len(opts.Series)
for idx := range comboCharts { for idx := range comboCharts {
comboCharts[idx].order = order comboCharts[idx].order = order
addChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[comboCharts[idx].Type](comboCharts[idx])) addChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[comboCharts[idx].Type](comboCharts[idx]))
@ -264,7 +264,7 @@ func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) {
// drawBaseChart provides a function to draw the c:plotArea element for bar, // drawBaseChart provides a function to draw the c:plotArea element for bar,
// and column series charts by given format sets. // and column series charts by given format sets.
func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea { func (f *File) drawBaseChart(opts *chartOptions) *cPlotArea {
c := cCharts{ c := cCharts{
BarDir: &attrValString{ BarDir: &attrValString{
Val: stringPtr("col"), Val: stringPtr("col"),
@ -273,11 +273,11 @@ func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea {
Val: stringPtr("clustered"), Val: stringPtr("clustered"),
}, },
VaryColors: &attrValBool{ VaryColors: &attrValBool{
Val: boolPtr(formatSet.VaryColors), Val: boolPtr(opts.VaryColors),
}, },
Ser: f.drawChartSeries(formatSet), Ser: f.drawChartSeries(opts),
Shape: f.drawChartShape(formatSet), Shape: f.drawChartShape(opts),
DLbls: f.drawChartDLbls(formatSet), DLbls: f.drawChartDLbls(opts),
AxID: []*attrValInt{ AxID: []*attrValInt{
{Val: intPtr(754001152)}, {Val: intPtr(754001152)},
{Val: intPtr(753999904)}, {Val: intPtr(753999904)},
@ -285,17 +285,17 @@ func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea {
Overlap: &attrValInt{Val: intPtr(100)}, Overlap: &attrValInt{Val: intPtr(100)},
} }
var ok bool var ok bool
if *c.BarDir.Val, ok = plotAreaChartBarDir[formatSet.Type]; !ok { if *c.BarDir.Val, ok = plotAreaChartBarDir[opts.Type]; !ok {
c.BarDir = nil c.BarDir = nil
} }
if *c.Grouping.Val, ok = plotAreaChartGrouping[formatSet.Type]; !ok { if *c.Grouping.Val, ok = plotAreaChartGrouping[opts.Type]; !ok {
c.Grouping = nil c.Grouping = nil
} }
if *c.Overlap.Val, ok = plotAreaChartOverlap[formatSet.Type]; !ok { if *c.Overlap.Val, ok = plotAreaChartOverlap[opts.Type]; !ok {
c.Overlap = nil c.Overlap = nil
} }
catAx := f.drawPlotAreaCatAx(formatSet) catAx := f.drawPlotAreaCatAx(opts)
valAx := f.drawPlotAreaValAx(formatSet) valAx := f.drawPlotAreaValAx(opts)
charts := map[string]*cPlotArea{ charts := map[string]*cPlotArea{
"area": { "area": {
AreaChart: &c, AreaChart: &c,
@ -508,23 +508,23 @@ func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea {
ValAx: valAx, ValAx: valAx,
}, },
} }
return charts[formatSet.Type] return charts[opts.Type]
} }
// drawDoughnutChart provides a function to draw the c:plotArea element for // drawDoughnutChart provides a function to draw the c:plotArea element for
// doughnut chart by given format sets. // doughnut chart by given format sets.
func (f *File) drawDoughnutChart(formatSet *formatChart) *cPlotArea { func (f *File) drawDoughnutChart(opts *chartOptions) *cPlotArea {
holeSize := 75 holeSize := 75
if formatSet.HoleSize > 0 && formatSet.HoleSize <= 90 { if opts.HoleSize > 0 && opts.HoleSize <= 90 {
holeSize = formatSet.HoleSize holeSize = opts.HoleSize
} }
return &cPlotArea{ return &cPlotArea{
DoughnutChart: &cCharts{ DoughnutChart: &cCharts{
VaryColors: &attrValBool{ VaryColors: &attrValBool{
Val: boolPtr(formatSet.VaryColors), Val: boolPtr(opts.VaryColors),
}, },
Ser: f.drawChartSeries(formatSet), Ser: f.drawChartSeries(opts),
HoleSize: &attrValInt{Val: intPtr(holeSize)}, HoleSize: &attrValInt{Val: intPtr(holeSize)},
}, },
} }
@ -532,65 +532,65 @@ func (f *File) drawDoughnutChart(formatSet *formatChart) *cPlotArea {
// drawLineChart provides a function to draw the c:plotArea element for line // drawLineChart provides a function to draw the c:plotArea element for line
// chart by given format sets. // chart by given format sets.
func (f *File) drawLineChart(formatSet *formatChart) *cPlotArea { func (f *File) drawLineChart(opts *chartOptions) *cPlotArea {
return &cPlotArea{ return &cPlotArea{
LineChart: &cCharts{ LineChart: &cCharts{
Grouping: &attrValString{ Grouping: &attrValString{
Val: stringPtr(plotAreaChartGrouping[formatSet.Type]), Val: stringPtr(plotAreaChartGrouping[opts.Type]),
}, },
VaryColors: &attrValBool{ VaryColors: &attrValBool{
Val: boolPtr(false), Val: boolPtr(false),
}, },
Ser: f.drawChartSeries(formatSet), Ser: f.drawChartSeries(opts),
DLbls: f.drawChartDLbls(formatSet), DLbls: f.drawChartDLbls(opts),
AxID: []*attrValInt{ AxID: []*attrValInt{
{Val: intPtr(754001152)}, {Val: intPtr(754001152)},
{Val: intPtr(753999904)}, {Val: intPtr(753999904)},
}, },
}, },
CatAx: f.drawPlotAreaCatAx(formatSet), CatAx: f.drawPlotAreaCatAx(opts),
ValAx: f.drawPlotAreaValAx(formatSet), ValAx: f.drawPlotAreaValAx(opts),
} }
} }
// drawPieChart provides a function to draw the c:plotArea element for pie // drawPieChart provides a function to draw the c:plotArea element for pie
// chart by given format sets. // chart by given format sets.
func (f *File) drawPieChart(formatSet *formatChart) *cPlotArea { func (f *File) drawPieChart(opts *chartOptions) *cPlotArea {
return &cPlotArea{ return &cPlotArea{
PieChart: &cCharts{ PieChart: &cCharts{
VaryColors: &attrValBool{ VaryColors: &attrValBool{
Val: boolPtr(formatSet.VaryColors), Val: boolPtr(opts.VaryColors),
}, },
Ser: f.drawChartSeries(formatSet), Ser: f.drawChartSeries(opts),
}, },
} }
} }
// drawPie3DChart provides a function to draw the c:plotArea element for 3D // drawPie3DChart provides a function to draw the c:plotArea element for 3D
// pie chart by given format sets. // pie chart by given format sets.
func (f *File) drawPie3DChart(formatSet *formatChart) *cPlotArea { func (f *File) drawPie3DChart(opts *chartOptions) *cPlotArea {
return &cPlotArea{ return &cPlotArea{
Pie3DChart: &cCharts{ Pie3DChart: &cCharts{
VaryColors: &attrValBool{ VaryColors: &attrValBool{
Val: boolPtr(formatSet.VaryColors), Val: boolPtr(opts.VaryColors),
}, },
Ser: f.drawChartSeries(formatSet), Ser: f.drawChartSeries(opts),
}, },
} }
} }
// drawPieOfPieChart provides a function to draw the c:plotArea element for // drawPieOfPieChart provides a function to draw the c:plotArea element for
// pie chart by given format sets. // pie chart by given format sets.
func (f *File) drawPieOfPieChart(formatSet *formatChart) *cPlotArea { func (f *File) drawPieOfPieChart(opts *chartOptions) *cPlotArea {
return &cPlotArea{ return &cPlotArea{
OfPieChart: &cCharts{ OfPieChart: &cCharts{
OfPieType: &attrValString{ OfPieType: &attrValString{
Val: stringPtr("pie"), Val: stringPtr("pie"),
}, },
VaryColors: &attrValBool{ VaryColors: &attrValBool{
Val: boolPtr(formatSet.VaryColors), Val: boolPtr(opts.VaryColors),
}, },
Ser: f.drawChartSeries(formatSet), Ser: f.drawChartSeries(opts),
SerLines: &attrValString{}, SerLines: &attrValString{},
}, },
} }
@ -598,16 +598,16 @@ func (f *File) drawPieOfPieChart(formatSet *formatChart) *cPlotArea {
// drawBarOfPieChart provides a function to draw the c:plotArea element for // drawBarOfPieChart provides a function to draw the c:plotArea element for
// pie chart by given format sets. // pie chart by given format sets.
func (f *File) drawBarOfPieChart(formatSet *formatChart) *cPlotArea { func (f *File) drawBarOfPieChart(opts *chartOptions) *cPlotArea {
return &cPlotArea{ return &cPlotArea{
OfPieChart: &cCharts{ OfPieChart: &cCharts{
OfPieType: &attrValString{ OfPieType: &attrValString{
Val: stringPtr("bar"), Val: stringPtr("bar"),
}, },
VaryColors: &attrValBool{ VaryColors: &attrValBool{
Val: boolPtr(formatSet.VaryColors), Val: boolPtr(opts.VaryColors),
}, },
Ser: f.drawChartSeries(formatSet), Ser: f.drawChartSeries(opts),
SerLines: &attrValString{}, SerLines: &attrValString{},
}, },
} }
@ -615,7 +615,7 @@ func (f *File) drawBarOfPieChart(formatSet *formatChart) *cPlotArea {
// drawRadarChart provides a function to draw the c:plotArea element for radar // drawRadarChart provides a function to draw the c:plotArea element for radar
// chart by given format sets. // chart by given format sets.
func (f *File) drawRadarChart(formatSet *formatChart) *cPlotArea { func (f *File) drawRadarChart(opts *chartOptions) *cPlotArea {
return &cPlotArea{ return &cPlotArea{
RadarChart: &cCharts{ RadarChart: &cCharts{
RadarStyle: &attrValString{ RadarStyle: &attrValString{
@ -624,21 +624,21 @@ func (f *File) drawRadarChart(formatSet *formatChart) *cPlotArea {
VaryColors: &attrValBool{ VaryColors: &attrValBool{
Val: boolPtr(false), Val: boolPtr(false),
}, },
Ser: f.drawChartSeries(formatSet), Ser: f.drawChartSeries(opts),
DLbls: f.drawChartDLbls(formatSet), DLbls: f.drawChartDLbls(opts),
AxID: []*attrValInt{ AxID: []*attrValInt{
{Val: intPtr(754001152)}, {Val: intPtr(754001152)},
{Val: intPtr(753999904)}, {Val: intPtr(753999904)},
}, },
}, },
CatAx: f.drawPlotAreaCatAx(formatSet), CatAx: f.drawPlotAreaCatAx(opts),
ValAx: f.drawPlotAreaValAx(formatSet), ValAx: f.drawPlotAreaValAx(opts),
} }
} }
// drawScatterChart provides a function to draw the c:plotArea element for // drawScatterChart provides a function to draw the c:plotArea element for
// scatter chart by given format sets. // scatter chart by given format sets.
func (f *File) drawScatterChart(formatSet *formatChart) *cPlotArea { func (f *File) drawScatterChart(opts *chartOptions) *cPlotArea {
return &cPlotArea{ return &cPlotArea{
ScatterChart: &cCharts{ ScatterChart: &cCharts{
ScatterStyle: &attrValString{ ScatterStyle: &attrValString{
@ -647,35 +647,35 @@ func (f *File) drawScatterChart(formatSet *formatChart) *cPlotArea {
VaryColors: &attrValBool{ VaryColors: &attrValBool{
Val: boolPtr(false), Val: boolPtr(false),
}, },
Ser: f.drawChartSeries(formatSet), Ser: f.drawChartSeries(opts),
DLbls: f.drawChartDLbls(formatSet), DLbls: f.drawChartDLbls(opts),
AxID: []*attrValInt{ AxID: []*attrValInt{
{Val: intPtr(754001152)}, {Val: intPtr(754001152)},
{Val: intPtr(753999904)}, {Val: intPtr(753999904)},
}, },
}, },
CatAx: f.drawPlotAreaCatAx(formatSet), CatAx: f.drawPlotAreaCatAx(opts),
ValAx: f.drawPlotAreaValAx(formatSet), ValAx: f.drawPlotAreaValAx(opts),
} }
} }
// drawSurface3DChart provides a function to draw the c:surface3DChart element by // drawSurface3DChart provides a function to draw the c:surface3DChart element by
// given format sets. // given format sets.
func (f *File) drawSurface3DChart(formatSet *formatChart) *cPlotArea { func (f *File) drawSurface3DChart(opts *chartOptions) *cPlotArea {
plotArea := &cPlotArea{ plotArea := &cPlotArea{
Surface3DChart: &cCharts{ Surface3DChart: &cCharts{
Ser: f.drawChartSeries(formatSet), Ser: f.drawChartSeries(opts),
AxID: []*attrValInt{ AxID: []*attrValInt{
{Val: intPtr(754001152)}, {Val: intPtr(754001152)},
{Val: intPtr(753999904)}, {Val: intPtr(753999904)},
{Val: intPtr(832256642)}, {Val: intPtr(832256642)},
}, },
}, },
CatAx: f.drawPlotAreaCatAx(formatSet), CatAx: f.drawPlotAreaCatAx(opts),
ValAx: f.drawPlotAreaValAx(formatSet), ValAx: f.drawPlotAreaValAx(opts),
SerAx: f.drawPlotAreaSerAx(formatSet), SerAx: f.drawPlotAreaSerAx(opts),
} }
if formatSet.Type == WireframeSurface3D { if opts.Type == WireframeSurface3D {
plotArea.Surface3DChart.Wireframe = &attrValBool{Val: boolPtr(true)} plotArea.Surface3DChart.Wireframe = &attrValBool{Val: boolPtr(true)}
} }
return plotArea return plotArea
@ -683,21 +683,21 @@ func (f *File) drawSurface3DChart(formatSet *formatChart) *cPlotArea {
// drawSurfaceChart provides a function to draw the c:surfaceChart element by // drawSurfaceChart provides a function to draw the c:surfaceChart element by
// given format sets. // given format sets.
func (f *File) drawSurfaceChart(formatSet *formatChart) *cPlotArea { func (f *File) drawSurfaceChart(opts *chartOptions) *cPlotArea {
plotArea := &cPlotArea{ plotArea := &cPlotArea{
SurfaceChart: &cCharts{ SurfaceChart: &cCharts{
Ser: f.drawChartSeries(formatSet), Ser: f.drawChartSeries(opts),
AxID: []*attrValInt{ AxID: []*attrValInt{
{Val: intPtr(754001152)}, {Val: intPtr(754001152)},
{Val: intPtr(753999904)}, {Val: intPtr(753999904)},
{Val: intPtr(832256642)}, {Val: intPtr(832256642)},
}, },
}, },
CatAx: f.drawPlotAreaCatAx(formatSet), CatAx: f.drawPlotAreaCatAx(opts),
ValAx: f.drawPlotAreaValAx(formatSet), ValAx: f.drawPlotAreaValAx(opts),
SerAx: f.drawPlotAreaSerAx(formatSet), SerAx: f.drawPlotAreaSerAx(opts),
} }
if formatSet.Type == WireframeContour { if opts.Type == WireframeContour {
plotArea.SurfaceChart.Wireframe = &attrValBool{Val: boolPtr(true)} plotArea.SurfaceChart.Wireframe = &attrValBool{Val: boolPtr(true)}
} }
return plotArea return plotArea
@ -705,7 +705,7 @@ func (f *File) drawSurfaceChart(formatSet *formatChart) *cPlotArea {
// drawChartShape provides a function to draw the c:shape element by given // drawChartShape provides a function to draw the c:shape element by given
// format sets. // format sets.
func (f *File) drawChartShape(formatSet *formatChart) *attrValString { func (f *File) drawChartShape(opts *chartOptions) *attrValString {
shapes := map[string]string{ shapes := map[string]string{
Bar3DConeClustered: "cone", Bar3DConeClustered: "cone",
Bar3DConeStacked: "cone", Bar3DConeStacked: "cone",
@ -729,7 +729,7 @@ func (f *File) drawChartShape(formatSet *formatChart) *attrValString {
Col3DCylinderStacked: "cylinder", Col3DCylinderStacked: "cylinder",
Col3DCylinderPercentStacked: "cylinder", Col3DCylinderPercentStacked: "cylinder",
} }
if shape, ok := shapes[formatSet.Type]; ok { if shape, ok := shapes[opts.Type]; ok {
return &attrValString{Val: stringPtr(shape)} return &attrValString{Val: stringPtr(shape)}
} }
return nil return nil
@ -737,29 +737,29 @@ func (f *File) drawChartShape(formatSet *formatChart) *attrValString {
// drawChartSeries provides a function to draw the c:ser element by given // drawChartSeries provides a function to draw the c:ser element by given
// format sets. // format sets.
func (f *File) drawChartSeries(formatSet *formatChart) *[]cSer { func (f *File) drawChartSeries(opts *chartOptions) *[]cSer {
var ser []cSer var ser []cSer
for k := range formatSet.Series { for k := range opts.Series {
ser = append(ser, cSer{ ser = append(ser, cSer{
IDx: &attrValInt{Val: intPtr(k + formatSet.order)}, IDx: &attrValInt{Val: intPtr(k + opts.order)},
Order: &attrValInt{Val: intPtr(k + formatSet.order)}, Order: &attrValInt{Val: intPtr(k + opts.order)},
Tx: &cTx{ Tx: &cTx{
StrRef: &cStrRef{ StrRef: &cStrRef{
F: formatSet.Series[k].Name, F: opts.Series[k].Name,
}, },
}, },
SpPr: f.drawChartSeriesSpPr(k, formatSet), SpPr: f.drawChartSeriesSpPr(k, opts),
Marker: f.drawChartSeriesMarker(k, formatSet), Marker: f.drawChartSeriesMarker(k, opts),
DPt: f.drawChartSeriesDPt(k, formatSet), DPt: f.drawChartSeriesDPt(k, opts),
DLbls: f.drawChartSeriesDLbls(formatSet), DLbls: f.drawChartSeriesDLbls(opts),
InvertIfNegative: &attrValBool{Val: boolPtr(false)}, InvertIfNegative: &attrValBool{Val: boolPtr(false)},
Cat: f.drawChartSeriesCat(formatSet.Series[k], formatSet), Cat: f.drawChartSeriesCat(opts.Series[k], opts),
Smooth: &attrValBool{Val: boolPtr(formatSet.Series[k].Line.Smooth)}, Smooth: &attrValBool{Val: boolPtr(opts.Series[k].Line.Smooth)},
Val: f.drawChartSeriesVal(formatSet.Series[k], formatSet), Val: f.drawChartSeriesVal(opts.Series[k], opts),
XVal: f.drawChartSeriesXVal(formatSet.Series[k], formatSet), XVal: f.drawChartSeriesXVal(opts.Series[k], opts),
YVal: f.drawChartSeriesYVal(formatSet.Series[k], formatSet), YVal: f.drawChartSeriesYVal(opts.Series[k], opts),
BubbleSize: f.drawCharSeriesBubbleSize(formatSet.Series[k], formatSet), BubbleSize: f.drawCharSeriesBubbleSize(opts.Series[k], opts),
Bubble3D: f.drawCharSeriesBubble3D(formatSet), Bubble3D: f.drawCharSeriesBubble3D(opts),
}) })
} }
return &ser return &ser
@ -767,15 +767,15 @@ func (f *File) drawChartSeries(formatSet *formatChart) *[]cSer {
// drawChartSeriesSpPr provides a function to draw the c:spPr element by given // drawChartSeriesSpPr provides a function to draw the c:spPr element by given
// format sets. // format sets.
func (f *File) drawChartSeriesSpPr(i int, formatSet *formatChart) *cSpPr { func (f *File) drawChartSeriesSpPr(i int, opts *chartOptions) *cSpPr {
var srgbClr *attrValString var srgbClr *attrValString
var schemeClr *aSchemeClr var schemeClr *aSchemeClr
if color := stringPtr(formatSet.Series[i].Line.Color); *color != "" { if color := stringPtr(opts.Series[i].Line.Color); *color != "" {
*color = strings.TrimPrefix(*color, "#") *color = strings.TrimPrefix(*color, "#")
srgbClr = &attrValString{Val: color} srgbClr = &attrValString{Val: color}
} else { } else {
schemeClr = &aSchemeClr{Val: "accent" + strconv.Itoa((formatSet.order+i)%6+1)} schemeClr = &aSchemeClr{Val: "accent" + strconv.Itoa((opts.order+i)%6+1)}
} }
spPrScatter := &cSpPr{ spPrScatter := &cSpPr{
@ -786,7 +786,7 @@ func (f *File) drawChartSeriesSpPr(i int, formatSet *formatChart) *cSpPr {
} }
spPrLine := &cSpPr{ spPrLine := &cSpPr{
Ln: &aLn{ Ln: &aLn{
W: f.ptToEMUs(formatSet.Series[i].Line.Width), W: f.ptToEMUs(opts.Series[i].Line.Width),
Cap: "rnd", // rnd, sq, flat Cap: "rnd", // rnd, sq, flat
SolidFill: &aSolidFill{ SolidFill: &aSolidFill{
SchemeClr: schemeClr, SchemeClr: schemeClr,
@ -795,12 +795,12 @@ func (f *File) drawChartSeriesSpPr(i int, formatSet *formatChart) *cSpPr {
}, },
} }
chartSeriesSpPr := map[string]*cSpPr{Line: spPrLine, Scatter: spPrScatter} chartSeriesSpPr := map[string]*cSpPr{Line: spPrLine, Scatter: spPrScatter}
return chartSeriesSpPr[formatSet.Type] return chartSeriesSpPr[opts.Type]
} }
// drawChartSeriesDPt provides a function to draw the c:dPt element by given // drawChartSeriesDPt provides a function to draw the c:dPt element by given
// data index and format sets. // data index and format sets.
func (f *File) drawChartSeriesDPt(i int, formatSet *formatChart) []*cDPt { func (f *File) drawChartSeriesDPt(i int, opts *chartOptions) []*cDPt {
dpt := []*cDPt{{ dpt := []*cDPt{{
IDx: &attrValInt{Val: intPtr(i)}, IDx: &attrValInt{Val: intPtr(i)},
Bubble3D: &attrValBool{Val: boolPtr(false)}, Bubble3D: &attrValBool{Val: boolPtr(false)},
@ -824,19 +824,19 @@ func (f *File) drawChartSeriesDPt(i int, formatSet *formatChart) []*cDPt {
}, },
}} }}
chartSeriesDPt := map[string][]*cDPt{Pie: dpt, Pie3D: dpt} chartSeriesDPt := map[string][]*cDPt{Pie: dpt, Pie3D: dpt}
return chartSeriesDPt[formatSet.Type] return chartSeriesDPt[opts.Type]
} }
// drawChartSeriesCat provides a function to draw the c:cat element by given // drawChartSeriesCat provides a function to draw the c:cat element by given
// chart series and format sets. // chart series and format sets.
func (f *File) drawChartSeriesCat(v formatChartSeries, formatSet *formatChart) *cCat { func (f *File) drawChartSeriesCat(v chartSeriesOptions, opts *chartOptions) *cCat {
cat := &cCat{ cat := &cCat{
StrRef: &cStrRef{ StrRef: &cStrRef{
F: v.Categories, F: v.Categories,
}, },
} }
chartSeriesCat := map[string]*cCat{Scatter: nil, Bubble: nil, Bubble3D: nil} chartSeriesCat := map[string]*cCat{Scatter: nil, Bubble: nil, Bubble3D: nil}
if _, ok := chartSeriesCat[formatSet.Type]; ok || v.Categories == "" { if _, ok := chartSeriesCat[opts.Type]; ok || v.Categories == "" {
return nil return nil
} }
return cat return cat
@ -844,14 +844,14 @@ func (f *File) drawChartSeriesCat(v formatChartSeries, formatSet *formatChart) *
// drawChartSeriesVal provides a function to draw the c:val element by given // drawChartSeriesVal provides a function to draw the c:val element by given
// chart series and format sets. // chart series and format sets.
func (f *File) drawChartSeriesVal(v formatChartSeries, formatSet *formatChart) *cVal { func (f *File) drawChartSeriesVal(v chartSeriesOptions, opts *chartOptions) *cVal {
val := &cVal{ val := &cVal{
NumRef: &cNumRef{ NumRef: &cNumRef{
F: v.Values, F: v.Values,
}, },
} }
chartSeriesVal := map[string]*cVal{Scatter: nil, Bubble: nil, Bubble3D: nil} chartSeriesVal := map[string]*cVal{Scatter: nil, Bubble: nil, Bubble3D: nil}
if _, ok := chartSeriesVal[formatSet.Type]; ok { if _, ok := chartSeriesVal[opts.Type]; ok {
return nil return nil
} }
return val return val
@ -859,16 +859,16 @@ func (f *File) drawChartSeriesVal(v formatChartSeries, formatSet *formatChart) *
// drawChartSeriesMarker provides a function to draw the c:marker element by // drawChartSeriesMarker provides a function to draw the c:marker element by
// given data index and format sets. // given data index and format sets.
func (f *File) drawChartSeriesMarker(i int, formatSet *formatChart) *cMarker { func (f *File) drawChartSeriesMarker(i int, opts *chartOptions) *cMarker {
defaultSymbol := map[string]*attrValString{Scatter: {Val: stringPtr("circle")}} defaultSymbol := map[string]*attrValString{Scatter: {Val: stringPtr("circle")}}
marker := &cMarker{ marker := &cMarker{
Symbol: defaultSymbol[formatSet.Type], Symbol: defaultSymbol[opts.Type],
Size: &attrValInt{Val: intPtr(5)}, Size: &attrValInt{Val: intPtr(5)},
} }
if symbol := stringPtr(formatSet.Series[i].Marker.Symbol); *symbol != "" { if symbol := stringPtr(opts.Series[i].Marker.Symbol); *symbol != "" {
marker.Symbol = &attrValString{Val: symbol} marker.Symbol = &attrValString{Val: symbol}
} }
if size := intPtr(formatSet.Series[i].Marker.Size); *size != 0 { if size := intPtr(opts.Series[i].Marker.Size); *size != 0 {
marker.Size = &attrValInt{Val: size} marker.Size = &attrValInt{Val: size}
} }
if i < 6 { if i < 6 {
@ -889,37 +889,37 @@ func (f *File) drawChartSeriesMarker(i int, formatSet *formatChart) *cMarker {
} }
} }
chartSeriesMarker := map[string]*cMarker{Scatter: marker, Line: marker} chartSeriesMarker := map[string]*cMarker{Scatter: marker, Line: marker}
return chartSeriesMarker[formatSet.Type] return chartSeriesMarker[opts.Type]
} }
// drawChartSeriesXVal provides a function to draw the c:xVal element by given // drawChartSeriesXVal provides a function to draw the c:xVal element by given
// chart series and format sets. // chart series and format sets.
func (f *File) drawChartSeriesXVal(v formatChartSeries, formatSet *formatChart) *cCat { func (f *File) drawChartSeriesXVal(v chartSeriesOptions, opts *chartOptions) *cCat {
cat := &cCat{ cat := &cCat{
StrRef: &cStrRef{ StrRef: &cStrRef{
F: v.Categories, F: v.Categories,
}, },
} }
chartSeriesXVal := map[string]*cCat{Scatter: cat} chartSeriesXVal := map[string]*cCat{Scatter: cat}
return chartSeriesXVal[formatSet.Type] return chartSeriesXVal[opts.Type]
} }
// drawChartSeriesYVal provides a function to draw the c:yVal element by given // drawChartSeriesYVal provides a function to draw the c:yVal element by given
// chart series and format sets. // chart series and format sets.
func (f *File) drawChartSeriesYVal(v formatChartSeries, formatSet *formatChart) *cVal { func (f *File) drawChartSeriesYVal(v chartSeriesOptions, opts *chartOptions) *cVal {
val := &cVal{ val := &cVal{
NumRef: &cNumRef{ NumRef: &cNumRef{
F: v.Values, F: v.Values,
}, },
} }
chartSeriesYVal := map[string]*cVal{Scatter: val, Bubble: val, Bubble3D: val} chartSeriesYVal := map[string]*cVal{Scatter: val, Bubble: val, Bubble3D: val}
return chartSeriesYVal[formatSet.Type] return chartSeriesYVal[opts.Type]
} }
// drawCharSeriesBubbleSize provides a function to draw the c:bubbleSize // drawCharSeriesBubbleSize provides a function to draw the c:bubbleSize
// element by given chart series and format sets. // element by given chart series and format sets.
func (f *File) drawCharSeriesBubbleSize(v formatChartSeries, formatSet *formatChart) *cVal { func (f *File) drawCharSeriesBubbleSize(v chartSeriesOptions, opts *chartOptions) *cVal {
if _, ok := map[string]bool{Bubble: true, Bubble3D: true}[formatSet.Type]; !ok { if _, ok := map[string]bool{Bubble: true, Bubble3D: true}[opts.Type]; !ok {
return nil return nil
} }
return &cVal{ return &cVal{
@ -931,8 +931,8 @@ func (f *File) drawCharSeriesBubbleSize(v formatChartSeries, formatSet *formatCh
// drawCharSeriesBubble3D provides a function to draw the c:bubble3D element // drawCharSeriesBubble3D provides a function to draw the c:bubble3D element
// by given format sets. // by given format sets.
func (f *File) drawCharSeriesBubble3D(formatSet *formatChart) *attrValBool { func (f *File) drawCharSeriesBubble3D(opts *chartOptions) *attrValBool {
if _, ok := map[string]bool{Bubble3D: true}[formatSet.Type]; !ok { if _, ok := map[string]bool{Bubble3D: true}[opts.Type]; !ok {
return nil return nil
} }
return &attrValBool{Val: boolPtr(true)} return &attrValBool{Val: boolPtr(true)}
@ -940,51 +940,51 @@ func (f *File) drawCharSeriesBubble3D(formatSet *formatChart) *attrValBool {
// drawChartDLbls provides a function to draw the c:dLbls element by given // drawChartDLbls provides a function to draw the c:dLbls element by given
// format sets. // format sets.
func (f *File) drawChartDLbls(formatSet *formatChart) *cDLbls { func (f *File) drawChartDLbls(opts *chartOptions) *cDLbls {
return &cDLbls{ return &cDLbls{
ShowLegendKey: &attrValBool{Val: boolPtr(formatSet.Legend.ShowLegendKey)}, ShowLegendKey: &attrValBool{Val: boolPtr(opts.Legend.ShowLegendKey)},
ShowVal: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowVal)}, ShowVal: &attrValBool{Val: boolPtr(opts.Plotarea.ShowVal)},
ShowCatName: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowCatName)}, ShowCatName: &attrValBool{Val: boolPtr(opts.Plotarea.ShowCatName)},
ShowSerName: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowSerName)}, ShowSerName: &attrValBool{Val: boolPtr(opts.Plotarea.ShowSerName)},
ShowBubbleSize: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowBubbleSize)}, ShowBubbleSize: &attrValBool{Val: boolPtr(opts.Plotarea.ShowBubbleSize)},
ShowPercent: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowPercent)}, ShowPercent: &attrValBool{Val: boolPtr(opts.Plotarea.ShowPercent)},
ShowLeaderLines: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowLeaderLines)}, ShowLeaderLines: &attrValBool{Val: boolPtr(opts.Plotarea.ShowLeaderLines)},
} }
} }
// drawChartSeriesDLbls provides a function to draw the c:dLbls element by // drawChartSeriesDLbls provides a function to draw the c:dLbls element by
// given format sets. // given format sets.
func (f *File) drawChartSeriesDLbls(formatSet *formatChart) *cDLbls { func (f *File) drawChartSeriesDLbls(opts *chartOptions) *cDLbls {
dLbls := f.drawChartDLbls(formatSet) dLbls := f.drawChartDLbls(opts)
chartSeriesDLbls := map[string]*cDLbls{ chartSeriesDLbls := map[string]*cDLbls{
Scatter: nil, Surface3D: nil, WireframeSurface3D: nil, Contour: nil, WireframeContour: nil, Bubble: nil, Bubble3D: nil, Scatter: nil, Surface3D: nil, WireframeSurface3D: nil, Contour: nil, WireframeContour: nil, Bubble: nil, Bubble3D: nil,
} }
if _, ok := chartSeriesDLbls[formatSet.Type]; ok { if _, ok := chartSeriesDLbls[opts.Type]; ok {
return nil return nil
} }
return dLbls return dLbls
} }
// drawPlotAreaCatAx provides a function to draw the c:catAx element. // drawPlotAreaCatAx provides a function to draw the c:catAx element.
func (f *File) drawPlotAreaCatAx(formatSet *formatChart) []*cAxs { func (f *File) drawPlotAreaCatAx(opts *chartOptions) []*cAxs {
max := &attrValFloat{Val: formatSet.XAxis.Maximum} max := &attrValFloat{Val: opts.XAxis.Maximum}
min := &attrValFloat{Val: formatSet.XAxis.Minimum} min := &attrValFloat{Val: opts.XAxis.Minimum}
if formatSet.XAxis.Maximum == nil { if opts.XAxis.Maximum == nil {
max = nil max = nil
} }
if formatSet.XAxis.Minimum == nil { if opts.XAxis.Minimum == nil {
min = nil min = nil
} }
axs := []*cAxs{ axs := []*cAxs{
{ {
AxID: &attrValInt{Val: intPtr(754001152)}, AxID: &attrValInt{Val: intPtr(754001152)},
Scaling: &cScaling{ Scaling: &cScaling{
Orientation: &attrValString{Val: stringPtr(orientation[formatSet.XAxis.ReverseOrder])}, Orientation: &attrValString{Val: stringPtr(orientation[opts.XAxis.ReverseOrder])},
Max: max, Max: max,
Min: min, Min: min,
}, },
Delete: &attrValBool{Val: boolPtr(formatSet.XAxis.None)}, Delete: &attrValBool{Val: boolPtr(opts.XAxis.None)},
AxPos: &attrValString{Val: stringPtr(catAxPos[formatSet.XAxis.ReverseOrder])}, AxPos: &attrValString{Val: stringPtr(catAxPos[opts.XAxis.ReverseOrder])},
NumFmt: &cNumFmt{ NumFmt: &cNumFmt{
FormatCode: "General", FormatCode: "General",
SourceLinked: true, SourceLinked: true,
@ -1002,45 +1002,45 @@ func (f *File) drawPlotAreaCatAx(formatSet *formatChart) []*cAxs {
NoMultiLvlLbl: &attrValBool{Val: boolPtr(false)}, NoMultiLvlLbl: &attrValBool{Val: boolPtr(false)},
}, },
} }
if formatSet.XAxis.MajorGridlines { if opts.XAxis.MajorGridlines {
axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()} axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
} }
if formatSet.XAxis.MinorGridlines { if opts.XAxis.MinorGridlines {
axs[0].MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()} axs[0].MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
} }
if formatSet.XAxis.TickLabelSkip != 0 { if opts.XAxis.TickLabelSkip != 0 {
axs[0].TickLblSkip = &attrValInt{Val: intPtr(formatSet.XAxis.TickLabelSkip)} axs[0].TickLblSkip = &attrValInt{Val: intPtr(opts.XAxis.TickLabelSkip)}
} }
return axs return axs
} }
// drawPlotAreaValAx provides a function to draw the c:valAx element. // drawPlotAreaValAx provides a function to draw the c:valAx element.
func (f *File) drawPlotAreaValAx(formatSet *formatChart) []*cAxs { func (f *File) drawPlotAreaValAx(opts *chartOptions) []*cAxs {
max := &attrValFloat{Val: formatSet.YAxis.Maximum} max := &attrValFloat{Val: opts.YAxis.Maximum}
min := &attrValFloat{Val: formatSet.YAxis.Minimum} min := &attrValFloat{Val: opts.YAxis.Minimum}
if formatSet.YAxis.Maximum == nil { if opts.YAxis.Maximum == nil {
max = nil max = nil
} }
if formatSet.YAxis.Minimum == nil { if opts.YAxis.Minimum == nil {
min = nil min = nil
} }
var logBase *attrValFloat var logBase *attrValFloat
if formatSet.YAxis.LogBase >= 2 && formatSet.YAxis.LogBase <= 1000 { if opts.YAxis.LogBase >= 2 && opts.YAxis.LogBase <= 1000 {
logBase = &attrValFloat{Val: float64Ptr(formatSet.YAxis.LogBase)} logBase = &attrValFloat{Val: float64Ptr(opts.YAxis.LogBase)}
} }
axs := []*cAxs{ axs := []*cAxs{
{ {
AxID: &attrValInt{Val: intPtr(753999904)}, AxID: &attrValInt{Val: intPtr(753999904)},
Scaling: &cScaling{ Scaling: &cScaling{
LogBase: logBase, LogBase: logBase,
Orientation: &attrValString{Val: stringPtr(orientation[formatSet.YAxis.ReverseOrder])}, Orientation: &attrValString{Val: stringPtr(orientation[opts.YAxis.ReverseOrder])},
Max: max, Max: max,
Min: min, Min: min,
}, },
Delete: &attrValBool{Val: boolPtr(formatSet.YAxis.None)}, Delete: &attrValBool{Val: boolPtr(opts.YAxis.None)},
AxPos: &attrValString{Val: stringPtr(valAxPos[formatSet.YAxis.ReverseOrder])}, AxPos: &attrValString{Val: stringPtr(valAxPos[opts.YAxis.ReverseOrder])},
NumFmt: &cNumFmt{ NumFmt: &cNumFmt{
FormatCode: chartValAxNumFmtFormatCode[formatSet.Type], FormatCode: chartValAxNumFmtFormatCode[opts.Type],
SourceLinked: true, SourceLinked: true,
}, },
MajorTickMark: &attrValString{Val: stringPtr("none")}, MajorTickMark: &attrValString{Val: stringPtr("none")},
@ -1050,44 +1050,44 @@ func (f *File) drawPlotAreaValAx(formatSet *formatChart) []*cAxs {
TxPr: f.drawPlotAreaTxPr(), TxPr: f.drawPlotAreaTxPr(),
CrossAx: &attrValInt{Val: intPtr(754001152)}, CrossAx: &attrValInt{Val: intPtr(754001152)},
Crosses: &attrValString{Val: stringPtr("autoZero")}, Crosses: &attrValString{Val: stringPtr("autoZero")},
CrossBetween: &attrValString{Val: stringPtr(chartValAxCrossBetween[formatSet.Type])}, CrossBetween: &attrValString{Val: stringPtr(chartValAxCrossBetween[opts.Type])},
}, },
} }
if formatSet.YAxis.MajorGridlines { if opts.YAxis.MajorGridlines {
axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()} axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
} }
if formatSet.YAxis.MinorGridlines { if opts.YAxis.MinorGridlines {
axs[0].MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()} axs[0].MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
} }
if pos, ok := valTickLblPos[formatSet.Type]; ok { if pos, ok := valTickLblPos[opts.Type]; ok {
axs[0].TickLblPos.Val = stringPtr(pos) axs[0].TickLblPos.Val = stringPtr(pos)
} }
if formatSet.YAxis.MajorUnit != 0 { if opts.YAxis.MajorUnit != 0 {
axs[0].MajorUnit = &attrValFloat{Val: float64Ptr(formatSet.YAxis.MajorUnit)} axs[0].MajorUnit = &attrValFloat{Val: float64Ptr(opts.YAxis.MajorUnit)}
} }
return axs return axs
} }
// drawPlotAreaSerAx provides a function to draw the c:serAx element. // drawPlotAreaSerAx provides a function to draw the c:serAx element.
func (f *File) drawPlotAreaSerAx(formatSet *formatChart) []*cAxs { func (f *File) drawPlotAreaSerAx(opts *chartOptions) []*cAxs {
max := &attrValFloat{Val: formatSet.YAxis.Maximum} max := &attrValFloat{Val: opts.YAxis.Maximum}
min := &attrValFloat{Val: formatSet.YAxis.Minimum} min := &attrValFloat{Val: opts.YAxis.Minimum}
if formatSet.YAxis.Maximum == nil { if opts.YAxis.Maximum == nil {
max = nil max = nil
} }
if formatSet.YAxis.Minimum == nil { if opts.YAxis.Minimum == nil {
min = nil min = nil
} }
return []*cAxs{ return []*cAxs{
{ {
AxID: &attrValInt{Val: intPtr(832256642)}, AxID: &attrValInt{Val: intPtr(832256642)},
Scaling: &cScaling{ Scaling: &cScaling{
Orientation: &attrValString{Val: stringPtr(orientation[formatSet.YAxis.ReverseOrder])}, Orientation: &attrValString{Val: stringPtr(orientation[opts.YAxis.ReverseOrder])},
Max: max, Max: max,
Min: min, Min: min,
}, },
Delete: &attrValBool{Val: boolPtr(formatSet.YAxis.None)}, Delete: &attrValBool{Val: boolPtr(opts.YAxis.None)},
AxPos: &attrValString{Val: stringPtr(catAxPos[formatSet.XAxis.ReverseOrder])}, AxPos: &attrValString{Val: stringPtr(catAxPos[opts.XAxis.ReverseOrder])},
TickLblPos: &attrValString{Val: stringPtr("nextTo")}, TickLblPos: &attrValString{Val: stringPtr("nextTo")},
SpPr: f.drawPlotAreaSpPr(), SpPr: f.drawPlotAreaSpPr(),
TxPr: f.drawPlotAreaTxPr(), TxPr: f.drawPlotAreaTxPr(),
@ -1207,7 +1207,7 @@ func (f *File) drawingParser(path string) (*xlsxWsDr, int) {
// addDrawingChart provides a function to add chart graphic frame by given // addDrawingChart provides a function to add chart graphic frame by given
// sheet, drawingXML, cell, width, height, relationship index and format sets. // sheet, drawingXML, cell, width, height, relationship index and format sets.
func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rID int, formatSet *formatPicture) error { func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rID int, opts *pictureOptions) error {
col, row, err := CellNameToCoordinates(cell) col, row, err := CellNameToCoordinates(cell)
if err != nil { if err != nil {
return err return err
@ -1215,17 +1215,17 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
colIdx := col - 1 colIdx := col - 1
rowIdx := row - 1 rowIdx := row - 1
width = int(float64(width) * formatSet.XScale) width = int(float64(width) * opts.XScale)
height = int(float64(height) * formatSet.YScale) height = int(float64(height) * opts.YScale)
colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, colIdx, rowIdx, formatSet.OffsetX, formatSet.OffsetY, width, height) colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, colIdx, rowIdx, opts.OffsetX, opts.OffsetY, width, height)
content, cNvPrID := f.drawingParser(drawingXML) content, cNvPrID := f.drawingParser(drawingXML)
twoCellAnchor := xdrCellAnchor{} twoCellAnchor := xdrCellAnchor{}
twoCellAnchor.EditAs = formatSet.Positioning twoCellAnchor.EditAs = opts.Positioning
from := xlsxFrom{} from := xlsxFrom{}
from.Col = colStart from.Col = colStart
from.ColOff = formatSet.OffsetX * EMU from.ColOff = opts.OffsetX * EMU
from.Row = rowStart from.Row = rowStart
from.RowOff = formatSet.OffsetY * EMU from.RowOff = opts.OffsetY * EMU
to := xlsxTo{} to := xlsxTo{}
to.Col = colEnd to.Col = colEnd
to.ColOff = x2 * EMU to.ColOff = x2 * EMU
@ -1255,8 +1255,8 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
graphic, _ := xml.Marshal(graphicFrame) graphic, _ := xml.Marshal(graphicFrame)
twoCellAnchor.GraphicFrame = string(graphic) twoCellAnchor.GraphicFrame = string(graphic)
twoCellAnchor.ClientData = &xdrClientData{ twoCellAnchor.ClientData = &xdrClientData{
FLocksWithSheet: formatSet.FLocksWithSheet, FLocksWithSheet: opts.FLocksWithSheet,
FPrintsWithSheet: formatSet.FPrintsWithSheet, FPrintsWithSheet: opts.FPrintsWithSheet,
} }
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor) content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
f.Drawings.Store(drawingXML, content) f.Drawings.Store(drawingXML, content)
@ -1266,10 +1266,10 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
// addSheetDrawingChart provides a function to add chart graphic frame for // addSheetDrawingChart provides a function to add chart graphic frame for
// chartsheet by given sheet, drawingXML, width, height, relationship index // chartsheet by given sheet, drawingXML, width, height, relationship index
// and format sets. // and format sets.
func (f *File) addSheetDrawingChart(drawingXML string, rID int, formatSet *formatPicture) { func (f *File) addSheetDrawingChart(drawingXML string, rID int, opts *pictureOptions) {
content, cNvPrID := f.drawingParser(drawingXML) content, cNvPrID := f.drawingParser(drawingXML)
absoluteAnchor := xdrCellAnchor{ absoluteAnchor := xdrCellAnchor{
EditAs: formatSet.Positioning, EditAs: opts.Positioning,
Pos: &xlsxPoint2D{}, Pos: &xlsxPoint2D{},
Ext: &xlsxExt{}, Ext: &xlsxExt{},
} }
@ -1295,8 +1295,8 @@ func (f *File) addSheetDrawingChart(drawingXML string, rID int, formatSet *forma
graphic, _ := xml.Marshal(graphicFrame) graphic, _ := xml.Marshal(graphicFrame)
absoluteAnchor.GraphicFrame = string(graphic) absoluteAnchor.GraphicFrame = string(graphic)
absoluteAnchor.ClientData = &xdrClientData{ absoluteAnchor.ClientData = &xdrClientData{
FLocksWithSheet: formatSet.FLocksWithSheet, FLocksWithSheet: opts.FLocksWithSheet,
FPrintsWithSheet: formatSet.FPrintsWithSheet, FPrintsWithSheet: opts.FPrintsWithSheet,
} }
content.AbsoluteAnchor = append(content.AbsoluteAnchor, &absoluteAnchor) content.AbsoluteAnchor = append(content.AbsoluteAnchor, &absoluteAnchor)
f.Drawings.Store(drawingXML, content) f.Drawings.Store(drawingXML, content)
@ -1304,8 +1304,9 @@ func (f *File) addSheetDrawingChart(drawingXML string, rID int, formatSet *forma
// deleteDrawing provides a function to delete chart graphic frame by given by // deleteDrawing provides a function to delete chart graphic frame by given by
// given coordinates and graphic type. // given coordinates and graphic type.
func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (err error) { func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) error {
var ( var (
err error
wsDr *xlsxWsDr wsDr *xlsxWsDr
deTwoCellAnchor *decodeTwoCellAnchor deTwoCellAnchor *decodeTwoCellAnchor
) )
@ -1331,7 +1332,7 @@ func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (err
if err = f.xmlNewDecoder(strings.NewReader("<decodeTwoCellAnchor>" + wsDr.TwoCellAnchor[idx].GraphicFrame + "</decodeTwoCellAnchor>")). if err = f.xmlNewDecoder(strings.NewReader("<decodeTwoCellAnchor>" + wsDr.TwoCellAnchor[idx].GraphicFrame + "</decodeTwoCellAnchor>")).
Decode(deTwoCellAnchor); err != nil && err != io.EOF { Decode(deTwoCellAnchor); err != nil && err != io.EOF {
err = newDecodeXMLError(err) err = newDecodeXMLError(err)
return return err
} }
if err = nil; deTwoCellAnchor.From != nil && decodeTwoCellAnchorFuncs[drawingType](deTwoCellAnchor) { if err = nil; deTwoCellAnchor.From != nil && decodeTwoCellAnchorFuncs[drawingType](deTwoCellAnchor) {
if deTwoCellAnchor.From.Col == col && deTwoCellAnchor.From.Row == row { if deTwoCellAnchor.From.Col == col && deTwoCellAnchor.From.Row == row {

View File

@ -319,13 +319,13 @@ func TestNewFile(t *testing.T) {
t.FailNow() t.FailNow()
} }
// Test add picture to worksheet without formatset. // Test add picture to worksheet without options.
err = f.AddPicture("Sheet1", "C2", filepath.Join("test", "images", "excel.png"), "") err = f.AddPicture("Sheet1", "C2", filepath.Join("test", "images", "excel.png"), "")
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
// Test add picture to worksheet with invalid formatset. // Test add picture to worksheet with invalid options.
err = f.AddPicture("Sheet1", "C2", filepath.Join("test", "images", "excel.png"), `{`) err = f.AddPicture("Sheet1", "C2", filepath.Join("test", "images", "excel.png"), `{`)
if !assert.Error(t, err) { if !assert.Error(t, err) {
t.FailNow() t.FailNow()
@ -1021,12 +1021,6 @@ func TestRelsWriter(t *testing.T) {
f.relsWriter() f.relsWriter()
} }
func TestGetSheetView(t *testing.T) {
f := NewFile()
_, err := f.getSheetView("SheetN", 0)
assert.EqualError(t, err, "sheet SheetN does not exist")
}
func TestConditionalFormat(t *testing.T) { func TestConditionalFormat(t *testing.T) {
f := NewFile() f := NewFile()
sheet1 := f.GetSheetName(0) sheet1 := f.GetSheetName(0)
@ -1228,7 +1222,7 @@ func TestProtectSheet(t *testing.T) {
sheetName := f.GetSheetName(0) sheetName := f.GetSheetName(0)
assert.NoError(t, f.ProtectSheet(sheetName, nil)) assert.NoError(t, f.ProtectSheet(sheetName, nil))
// Test protect worksheet with XOR hash algorithm // Test protect worksheet with XOR hash algorithm
assert.NoError(t, f.ProtectSheet(sheetName, &FormatSheetProtection{ assert.NoError(t, f.ProtectSheet(sheetName, &SheetProtectionOptions{
Password: "password", Password: "password",
EditScenarios: false, EditScenarios: false,
})) }))
@ -1237,7 +1231,7 @@ func TestProtectSheet(t *testing.T) {
assert.Equal(t, "83AF", ws.SheetProtection.Password) assert.Equal(t, "83AF", ws.SheetProtection.Password)
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestProtectSheet.xlsx"))) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestProtectSheet.xlsx")))
// Test protect worksheet with SHA-512 hash algorithm // Test protect worksheet with SHA-512 hash algorithm
assert.NoError(t, f.ProtectSheet(sheetName, &FormatSheetProtection{ assert.NoError(t, f.ProtectSheet(sheetName, &SheetProtectionOptions{
AlgorithmName: "SHA-512", AlgorithmName: "SHA-512",
Password: "password", Password: "password",
})) }))
@ -1251,15 +1245,15 @@ func TestProtectSheet(t *testing.T) {
// Test remove sheet protection with password verification // Test remove sheet protection with password verification
assert.NoError(t, f.UnprotectSheet(sheetName, "password")) assert.NoError(t, f.UnprotectSheet(sheetName, "password"))
// Test protect worksheet with empty password // Test protect worksheet with empty password
assert.NoError(t, f.ProtectSheet(sheetName, &FormatSheetProtection{})) assert.NoError(t, f.ProtectSheet(sheetName, &SheetProtectionOptions{}))
assert.Equal(t, "", ws.SheetProtection.Password) assert.Equal(t, "", ws.SheetProtection.Password)
// Test protect worksheet with password exceeds the limit length // Test protect worksheet with password exceeds the limit length
assert.EqualError(t, f.ProtectSheet(sheetName, &FormatSheetProtection{ assert.EqualError(t, f.ProtectSheet(sheetName, &SheetProtectionOptions{
AlgorithmName: "MD4", AlgorithmName: "MD4",
Password: strings.Repeat("s", MaxFieldLength+1), Password: strings.Repeat("s", MaxFieldLength+1),
}), ErrPasswordLengthInvalid.Error()) }), ErrPasswordLengthInvalid.Error())
// Test protect worksheet with unsupported hash algorithm // Test protect worksheet with unsupported hash algorithm
assert.EqualError(t, f.ProtectSheet(sheetName, &FormatSheetProtection{ assert.EqualError(t, f.ProtectSheet(sheetName, &SheetProtectionOptions{
AlgorithmName: "RIPEMD-160", AlgorithmName: "RIPEMD-160",
Password: "password", Password: "password",
}), ErrUnsupportedHashAlgorithm.Error()) }), ErrUnsupportedHashAlgorithm.Error())
@ -1282,13 +1276,13 @@ func TestUnprotectSheet(t *testing.T) {
f = NewFile() f = NewFile()
sheetName := f.GetSheetName(0) sheetName := f.GetSheetName(0)
assert.NoError(t, f.ProtectSheet(sheetName, &FormatSheetProtection{Password: "password"})) assert.NoError(t, f.ProtectSheet(sheetName, &SheetProtectionOptions{Password: "password"}))
// Test remove sheet protection with an incorrect password // Test remove sheet protection with an incorrect password
assert.EqualError(t, f.UnprotectSheet(sheetName, "wrongPassword"), ErrUnprotectSheetPassword.Error()) assert.EqualError(t, f.UnprotectSheet(sheetName, "wrongPassword"), ErrUnprotectSheetPassword.Error())
// Test remove sheet protection with password verification // Test remove sheet protection with password verification
assert.NoError(t, f.UnprotectSheet(sheetName, "password")) assert.NoError(t, f.UnprotectSheet(sheetName, "password"))
// Test with invalid salt value // Test with invalid salt value
assert.NoError(t, f.ProtectSheet(sheetName, &FormatSheetProtection{ assert.NoError(t, f.ProtectSheet(sheetName, &SheetProtectionOptions{
AlgorithmName: "SHA-512", AlgorithmName: "SHA-512",
Password: "password", Password: "password",
})) }))
@ -1309,7 +1303,7 @@ func TestSetDefaultTimeStyle(t *testing.T) {
func TestAddVBAProject(t *testing.T) { func TestAddVBAProject(t *testing.T) {
f := NewFile() f := NewFile()
assert.NoError(t, f.SetSheetPrOptions("Sheet1", CodeName("Sheet1"))) assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{CodeName: stringPtr("Sheet1")}))
assert.EqualError(t, f.AddVBAProject("macros.bin"), "stat macros.bin: no such file or directory") assert.EqualError(t, f.AddVBAProject("macros.bin"), "stat macros.bin: no such file or directory")
assert.EqualError(t, f.AddVBAProject(filepath.Join("test", "Book1.xlsx")), ErrAddVBAProject.Error()) assert.EqualError(t, f.AddVBAProject(filepath.Join("test", "Book1.xlsx")), ErrAddVBAProject.Error())
assert.NoError(t, f.AddVBAProject(filepath.Join("test", "vbaProject.bin"))) assert.NoError(t, f.AddVBAProject(filepath.Join("test", "vbaProject.bin")))

19
lib.go
View File

@ -422,20 +422,15 @@ func boolPtr(b bool) *bool { return &b }
// intPtr returns a pointer to an int with the given value. // intPtr returns a pointer to an int with the given value.
func intPtr(i int) *int { return &i } func intPtr(i int) *int { return &i }
// uintPtr returns a pointer to an int with the given value.
func uintPtr(i uint) *uint { return &i }
// float64Ptr returns a pointer to a float64 with the given value. // float64Ptr returns a pointer to a float64 with the given value.
func float64Ptr(f float64) *float64 { return &f } func float64Ptr(f float64) *float64 { return &f }
// stringPtr returns a pointer to a string with the given value. // stringPtr returns a pointer to a string with the given value.
func stringPtr(s string) *string { return &s } func stringPtr(s string) *string { return &s }
// defaultTrue returns true if b is nil, or the pointed value.
func defaultTrue(b *bool) bool {
if b == nil {
return true
}
return *b
}
// MarshalXML convert the boolean data type to literal values 0 or 1 on // MarshalXML convert the boolean data type to literal values 0 or 1 on
// serialization. // serialization.
func (avb attrValBool) MarshalXML(e *xml.Encoder, start xml.StartElement) error { func (avb attrValBool) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
@ -499,11 +494,11 @@ func (avb *attrValBool) UnmarshalXML(d *xml.Decoder, start xml.StartElement) err
return nil return nil
} }
// parseFormatSet provides a method to convert format string to []byte and // fallbackOptions provides a method to convert format string to []byte and
// handle empty string. // handle empty string.
func parseFormatSet(formatSet string) []byte { func fallbackOptions(opts string) []byte {
if formatSet != "" { if opts != "" {
return []byte(formatSet) return []byte(opts)
} }
return []byte("{}") return []byte("{}")
} }

View File

@ -25,15 +25,15 @@ import (
"strings" "strings"
) )
// parseFormatPictureSet provides a function to parse the format settings of // parsePictureOptions provides a function to parse the format settings of
// the picture with default value. // the picture with default value.
func parseFormatPictureSet(formatSet string) (*formatPicture, error) { func parsePictureOptions(opts string) (*pictureOptions, error) {
format := formatPicture{ format := pictureOptions{
FPrintsWithSheet: true, FPrintsWithSheet: true,
XScale: 1, XScale: 1,
YScale: 1, YScale: 1,
} }
err := json.Unmarshal(parseFormatSet(formatSet), &format) err := json.Unmarshal(fallbackOptions(opts), &format)
return &format, err return &format, err
} }
@ -148,14 +148,14 @@ func (f *File) AddPicture(sheet, cell, picture, format string) error {
// fmt.Println(err) // fmt.Println(err)
// } // }
// } // }
func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string, file []byte) error { func (f *File) AddPictureFromBytes(sheet, cell, opts, name, extension string, file []byte) error {
var drawingHyperlinkRID int var drawingHyperlinkRID int
var hyperlinkType string var hyperlinkType string
ext, ok := supportedImageTypes[extension] ext, ok := supportedImageTypes[extension]
if !ok { if !ok {
return ErrImgExt return ErrImgExt
} }
formatSet, err := parseFormatPictureSet(format) options, err := parsePictureOptions(opts)
if err != nil { if err != nil {
return err return err
} }
@ -177,14 +177,14 @@ func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string,
mediaStr := ".." + strings.TrimPrefix(f.addMedia(file, ext), "xl") mediaStr := ".." + strings.TrimPrefix(f.addMedia(file, ext), "xl")
drawingRID := f.addRels(drawingRels, SourceRelationshipImage, mediaStr, hyperlinkType) drawingRID := f.addRels(drawingRels, SourceRelationshipImage, mediaStr, hyperlinkType)
// Add picture with hyperlink. // Add picture with hyperlink.
if formatSet.Hyperlink != "" && formatSet.HyperlinkType != "" { if options.Hyperlink != "" && options.HyperlinkType != "" {
if formatSet.HyperlinkType == "External" { if options.HyperlinkType == "External" {
hyperlinkType = formatSet.HyperlinkType hyperlinkType = options.HyperlinkType
} }
drawingHyperlinkRID = f.addRels(drawingRels, SourceRelationshipHyperLink, formatSet.Hyperlink, hyperlinkType) drawingHyperlinkRID = f.addRels(drawingRels, SourceRelationshipHyperLink, options.Hyperlink, hyperlinkType)
} }
ws.Unlock() ws.Unlock()
err = f.addDrawingPicture(sheet, drawingXML, cell, name, img.Width, img.Height, drawingRID, drawingHyperlinkRID, formatSet) err = f.addDrawingPicture(sheet, drawingXML, cell, name, img.Width, img.Height, drawingRID, drawingHyperlinkRID, options)
if err != nil { if err != nil {
return err return err
} }
@ -264,31 +264,31 @@ func (f *File) countDrawings() (count int) {
// addDrawingPicture provides a function to add picture by given sheet, // addDrawingPicture provides a function to add picture by given sheet,
// drawingXML, cell, file name, width, height relationship index and format // drawingXML, cell, file name, width, height relationship index and format
// sets. // sets.
func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, height, rID, hyperlinkRID int, formatSet *formatPicture) error { func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, height, rID, hyperlinkRID int, opts *pictureOptions) error {
col, row, err := CellNameToCoordinates(cell) col, row, err := CellNameToCoordinates(cell)
if err != nil { if err != nil {
return err return err
} }
if formatSet.Autofit { if opts.Autofit {
width, height, col, row, err = f.drawingResize(sheet, cell, float64(width), float64(height), formatSet) width, height, col, row, err = f.drawingResize(sheet, cell, float64(width), float64(height), opts)
if err != nil { if err != nil {
return err return err
} }
} else { } else {
width = int(float64(width) * formatSet.XScale) width = int(float64(width) * opts.XScale)
height = int(float64(height) * formatSet.YScale) height = int(float64(height) * opts.YScale)
} }
col-- col--
row-- row--
colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, col, row, formatSet.OffsetX, formatSet.OffsetY, width, height) colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, col, row, opts.OffsetX, opts.OffsetY, width, height)
content, cNvPrID := f.drawingParser(drawingXML) content, cNvPrID := f.drawingParser(drawingXML)
twoCellAnchor := xdrCellAnchor{} twoCellAnchor := xdrCellAnchor{}
twoCellAnchor.EditAs = formatSet.Positioning twoCellAnchor.EditAs = opts.Positioning
from := xlsxFrom{} from := xlsxFrom{}
from.Col = colStart from.Col = colStart
from.ColOff = formatSet.OffsetX * EMU from.ColOff = opts.OffsetX * EMU
from.Row = rowStart from.Row = rowStart
from.RowOff = formatSet.OffsetY * EMU from.RowOff = opts.OffsetY * EMU
to := xlsxTo{} to := xlsxTo{}
to.Col = colEnd to.Col = colEnd
to.ColOff = x2 * EMU to.ColOff = x2 * EMU
@ -297,7 +297,7 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, he
twoCellAnchor.From = &from twoCellAnchor.From = &from
twoCellAnchor.To = &to twoCellAnchor.To = &to
pic := xlsxPic{} pic := xlsxPic{}
pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect = formatSet.NoChangeAspect pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect = opts.NoChangeAspect
pic.NvPicPr.CNvPr.ID = cNvPrID pic.NvPicPr.CNvPr.ID = cNvPrID
pic.NvPicPr.CNvPr.Descr = file pic.NvPicPr.CNvPr.Descr = file
pic.NvPicPr.CNvPr.Name = "Picture " + strconv.Itoa(cNvPrID) pic.NvPicPr.CNvPr.Name = "Picture " + strconv.Itoa(cNvPrID)
@ -313,8 +313,8 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, he
twoCellAnchor.Pic = &pic twoCellAnchor.Pic = &pic
twoCellAnchor.ClientData = &xdrClientData{ twoCellAnchor.ClientData = &xdrClientData{
FLocksWithSheet: formatSet.FLocksWithSheet, FLocksWithSheet: opts.FLocksWithSheet,
FPrintsWithSheet: formatSet.FPrintsWithSheet, FPrintsWithSheet: opts.FPrintsWithSheet,
} }
content.Lock() content.Lock()
defer content.Unlock() defer content.Unlock()
@ -514,19 +514,19 @@ func (f *File) GetPicture(sheet, cell string) (string, []byte, error) {
// DeletePicture provides a function to delete charts in spreadsheet by given // DeletePicture provides a function to delete charts in spreadsheet by given
// worksheet name and cell reference. Note that the image file won't be deleted // worksheet name and cell reference. Note that the image file won't be deleted
// from the document currently. // from the document currently.
func (f *File) DeletePicture(sheet, cell string) (err error) { func (f *File) DeletePicture(sheet, cell string) error {
col, row, err := CellNameToCoordinates(cell) col, row, err := CellNameToCoordinates(cell)
if err != nil { if err != nil {
return return err
} }
col-- col--
row-- row--
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return return err
} }
if ws.Drawing == nil { if ws.Drawing == nil {
return return err
} }
drawingXML := strings.ReplaceAll(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl") drawingXML := strings.ReplaceAll(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl")
return f.deleteDrawing(col, row, drawingXML, "Pic") return f.deleteDrawing(col, row, drawingXML, "Pic")
@ -636,7 +636,7 @@ func (f *File) drawingsWriter() {
} }
// drawingResize calculate the height and width after resizing. // drawingResize calculate the height and width after resizing.
func (f *File) drawingResize(sheet, cell string, width, height float64, formatSet *formatPicture) (w, h, c, r int, err error) { func (f *File) drawingResize(sheet, cell string, width, height float64, opts *pictureOptions) (w, h, c, r int, err error) {
var mergeCells []MergeCell var mergeCells []MergeCell
mergeCells, err = f.GetMergeCells(sheet) mergeCells, err = f.GetMergeCells(sheet)
if err != nil { if err != nil {
@ -678,7 +678,7 @@ func (f *File) drawingResize(sheet, cell string, width, height float64, formatSe
asp := float64(cellHeight) / height asp := float64(cellHeight) / height
height, width = float64(cellHeight), width*asp height, width = float64(cellHeight), width*asp
} }
width, height = width-float64(formatSet.OffsetX), height-float64(formatSet.OffsetY) width, height = width-float64(opts.OffsetX), height-float64(opts.OffsetY)
w, h = int(width*formatSet.XScale), int(height*formatSet.YScale) w, h = int(width*opts.XScale), int(height*opts.YScale)
return return
} }

View File

@ -18,14 +18,14 @@ import (
"strings" "strings"
) )
// PivotTableOption directly maps the format settings of the pivot table. // PivotTableOptions directly maps the format settings of the pivot table.
// //
// PivotTableStyleName: The built-in pivot table style names // PivotTableStyleName: The built-in pivot table style names
// //
// PivotStyleLight1 - PivotStyleLight28 // PivotStyleLight1 - PivotStyleLight28
// PivotStyleMedium1 - PivotStyleMedium28 // PivotStyleMedium1 - PivotStyleMedium28
// PivotStyleDark1 - PivotStyleDark28 // PivotStyleDark1 - PivotStyleDark28
type PivotTableOption struct { type PivotTableOptions struct {
pivotTableSheetName string pivotTableSheetName string
DataRange string `json:"data_range"` DataRange string `json:"data_range"`
PivotTableRange string `json:"pivot_table_range"` PivotTableRange string `json:"pivot_table_range"`
@ -81,9 +81,9 @@ type PivotTableField struct {
// options. Note that the same fields can not in Columns, Rows and Filter // options. Note that the same fields can not in Columns, Rows and Filter
// fields at the same time. // fields at the same time.
// //
// For example, create a pivot table on the Sheet1!$G$2:$M$34 range reference // For example, create a pivot table on the range reference Sheet1!$G$2:$M$34
// with the region Sheet1!$A$1:$E$31 as the data source, summarize by sum for // with the range reference Sheet1!$A$1:$E$31 as the data source, summarize by
// sales: // sum for sales:
// //
// package main // package main
// //
@ -129,7 +129,7 @@ type PivotTableField struct {
// fmt.Println(err) // fmt.Println(err)
// } // }
// } // }
func (f *File) AddPivotTable(opts *PivotTableOption) error { func (f *File) AddPivotTable(opts *PivotTableOptions) error {
// parameter validation // parameter validation
_, pivotTableSheetPath, err := f.parseFormatPivotTableSet(opts) _, pivotTableSheetPath, err := f.parseFormatPivotTableSet(opts)
if err != nil { if err != nil {
@ -168,7 +168,7 @@ func (f *File) AddPivotTable(opts *PivotTableOption) error {
// parseFormatPivotTableSet provides a function to validate pivot table // parseFormatPivotTableSet provides a function to validate pivot table
// properties. // properties.
func (f *File) parseFormatPivotTableSet(opts *PivotTableOption) (*xlsxWorksheet, string, error) { func (f *File) parseFormatPivotTableSet(opts *PivotTableOptions) (*xlsxWorksheet, string, error) {
if opts == nil { if opts == nil {
return nil, "", ErrParameterRequired return nil, "", ErrParameterRequired
} }
@ -228,7 +228,7 @@ func (f *File) adjustRange(rangeStr string) (string, []int, error) {
// getPivotFieldsOrder provides a function to get order list of pivot table // getPivotFieldsOrder provides a function to get order list of pivot table
// fields. // fields.
func (f *File) getPivotFieldsOrder(opts *PivotTableOption) ([]string, error) { func (f *File) getPivotFieldsOrder(opts *PivotTableOptions) ([]string, error) {
var order []string var order []string
dataRange := f.getDefinedNameRefTo(opts.DataRange, opts.pivotTableSheetName) dataRange := f.getDefinedNameRefTo(opts.DataRange, opts.pivotTableSheetName)
if dataRange == "" { if dataRange == "" {
@ -250,7 +250,7 @@ func (f *File) getPivotFieldsOrder(opts *PivotTableOption) ([]string, error) {
} }
// addPivotCache provides a function to create a pivot cache by given properties. // addPivotCache provides a function to create a pivot cache by given properties.
func (f *File) addPivotCache(pivotCacheXML string, opts *PivotTableOption) error { func (f *File) addPivotCache(pivotCacheXML string, opts *PivotTableOptions) error {
// validate data range // validate data range
definedNameRef := true definedNameRef := true
dataRange := f.getDefinedNameRefTo(opts.DataRange, opts.pivotTableSheetName) dataRange := f.getDefinedNameRefTo(opts.DataRange, opts.pivotTableSheetName)
@ -312,7 +312,7 @@ func (f *File) addPivotCache(pivotCacheXML string, opts *PivotTableOption) error
// addPivotTable provides a function to create a pivot table by given pivot // addPivotTable provides a function to create a pivot table by given pivot
// table ID and properties. // table ID and properties.
func (f *File) addPivotTable(cacheID, pivotTableID int, pivotTableXML string, opts *PivotTableOption) error { func (f *File) addPivotTable(cacheID, pivotTableID int, pivotTableXML string, opts *PivotTableOptions) error {
// validate pivot table range // validate pivot table range
_, coordinates, err := f.adjustRange(opts.PivotTableRange) _, coordinates, err := f.adjustRange(opts.PivotTableRange)
if err != nil { if err != nil {
@ -391,7 +391,7 @@ func (f *File) addPivotTable(cacheID, pivotTableID int, pivotTableXML string, op
// addPivotRowFields provides a method to add row fields for pivot table by // addPivotRowFields provides a method to add row fields for pivot table by
// given pivot table options. // given pivot table options.
func (f *File) addPivotRowFields(pt *xlsxPivotTableDefinition, opts *PivotTableOption) error { func (f *File) addPivotRowFields(pt *xlsxPivotTableDefinition, opts *PivotTableOptions) error {
// row fields // row fields
rowFieldsIndex, err := f.getPivotFieldsIndex(opts.Rows, opts) rowFieldsIndex, err := f.getPivotFieldsIndex(opts.Rows, opts)
if err != nil { if err != nil {
@ -415,7 +415,7 @@ func (f *File) addPivotRowFields(pt *xlsxPivotTableDefinition, opts *PivotTableO
// addPivotPageFields provides a method to add page fields for pivot table by // addPivotPageFields provides a method to add page fields for pivot table by
// given pivot table options. // given pivot table options.
func (f *File) addPivotPageFields(pt *xlsxPivotTableDefinition, opts *PivotTableOption) error { func (f *File) addPivotPageFields(pt *xlsxPivotTableDefinition, opts *PivotTableOptions) error {
// page fields // page fields
pageFieldsIndex, err := f.getPivotFieldsIndex(opts.Filter, opts) pageFieldsIndex, err := f.getPivotFieldsIndex(opts.Filter, opts)
if err != nil { if err != nil {
@ -441,7 +441,7 @@ func (f *File) addPivotPageFields(pt *xlsxPivotTableDefinition, opts *PivotTable
// addPivotDataFields provides a method to add data fields for pivot table by // addPivotDataFields provides a method to add data fields for pivot table by
// given pivot table options. // given pivot table options.
func (f *File) addPivotDataFields(pt *xlsxPivotTableDefinition, opts *PivotTableOption) error { func (f *File) addPivotDataFields(pt *xlsxPivotTableDefinition, opts *PivotTableOptions) error {
// data fields // data fields
dataFieldsIndex, err := f.getPivotFieldsIndex(opts.Data, opts) dataFieldsIndex, err := f.getPivotFieldsIndex(opts.Data, opts)
if err != nil { if err != nil {
@ -481,7 +481,7 @@ func inPivotTableField(a []PivotTableField, x string) int {
// addPivotColFields create pivot column fields by given pivot table // addPivotColFields create pivot column fields by given pivot table
// definition and option. // definition and option.
func (f *File) addPivotColFields(pt *xlsxPivotTableDefinition, opts *PivotTableOption) error { func (f *File) addPivotColFields(pt *xlsxPivotTableDefinition, opts *PivotTableOptions) error {
if len(opts.Columns) == 0 { if len(opts.Columns) == 0 {
if len(opts.Data) <= 1 { if len(opts.Data) <= 1 {
return nil return nil
@ -522,7 +522,7 @@ func (f *File) addPivotColFields(pt *xlsxPivotTableDefinition, opts *PivotTableO
// addPivotFields create pivot fields based on the column order of the first // addPivotFields create pivot fields based on the column order of the first
// row in the data region by given pivot table definition and option. // row in the data region by given pivot table definition and option.
func (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opts *PivotTableOption) error { func (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opts *PivotTableOptions) error {
order, err := f.getPivotFieldsOrder(opts) order, err := f.getPivotFieldsOrder(opts)
if err != nil { if err != nil {
return err return err
@ -627,7 +627,7 @@ func (f *File) countPivotCache() int {
// getPivotFieldsIndex convert the column of the first row in the data region // getPivotFieldsIndex convert the column of the first row in the data region
// to a sequential index by given fields and pivot option. // to a sequential index by given fields and pivot option.
func (f *File) getPivotFieldsIndex(fields []PivotTableField, opts *PivotTableOption) ([]int, error) { func (f *File) getPivotFieldsIndex(fields []PivotTableField, opts *PivotTableOptions) ([]int, error) {
var pivotFieldsIndex []int var pivotFieldsIndex []int
orders, err := f.getPivotFieldsOrder(opts) orders, err := f.getPivotFieldsOrder(opts)
if err != nil { if err != nil {

View File

@ -25,7 +25,7 @@ func TestAddPivotTable(t *testing.T) {
assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("D%d", row), rand.Intn(5000))) assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("D%d", row), rand.Intn(5000)))
assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("E%d", row), region[rand.Intn(4)])) assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("E%d", row), region[rand.Intn(4)]))
} }
assert.NoError(t, f.AddPivotTable(&PivotTableOption{ assert.NoError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!$A$1:$E$31", DataRange: "Sheet1!$A$1:$E$31",
PivotTableRange: "Sheet1!$G$2:$M$34", PivotTableRange: "Sheet1!$G$2:$M$34",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -41,7 +41,7 @@ func TestAddPivotTable(t *testing.T) {
ShowError: true, ShowError: true,
})) }))
// Use different order of coordinate tests // Use different order of coordinate tests
assert.NoError(t, f.AddPivotTable(&PivotTableOption{ assert.NoError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!$A$1:$E$31", DataRange: "Sheet1!$A$1:$E$31",
PivotTableRange: "Sheet1!$U$34:$O$2", PivotTableRange: "Sheet1!$U$34:$O$2",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -55,7 +55,7 @@ func TestAddPivotTable(t *testing.T) {
ShowLastColumn: true, ShowLastColumn: true,
})) }))
assert.NoError(t, f.AddPivotTable(&PivotTableOption{ assert.NoError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!$A$1:$E$31", DataRange: "Sheet1!$A$1:$E$31",
PivotTableRange: "Sheet1!$W$2:$AC$34", PivotTableRange: "Sheet1!$W$2:$AC$34",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -68,7 +68,7 @@ func TestAddPivotTable(t *testing.T) {
ShowColHeaders: true, ShowColHeaders: true,
ShowLastColumn: true, ShowLastColumn: true,
})) }))
assert.NoError(t, f.AddPivotTable(&PivotTableOption{ assert.NoError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!$A$1:$E$31", DataRange: "Sheet1!$A$1:$E$31",
PivotTableRange: "Sheet1!$G$37:$W$50", PivotTableRange: "Sheet1!$G$37:$W$50",
Rows: []PivotTableField{{Data: "Month"}}, Rows: []PivotTableField{{Data: "Month"}},
@ -81,7 +81,7 @@ func TestAddPivotTable(t *testing.T) {
ShowColHeaders: true, ShowColHeaders: true,
ShowLastColumn: true, ShowLastColumn: true,
})) }))
assert.NoError(t, f.AddPivotTable(&PivotTableOption{ assert.NoError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!$A$1:$E$31", DataRange: "Sheet1!$A$1:$E$31",
PivotTableRange: "Sheet1!$AE$2:$AG$33", PivotTableRange: "Sheet1!$AE$2:$AG$33",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -94,7 +94,7 @@ func TestAddPivotTable(t *testing.T) {
ShowLastColumn: true, ShowLastColumn: true,
})) }))
// Create pivot table with empty subtotal field name and specified style // Create pivot table with empty subtotal field name and specified style
assert.NoError(t, f.AddPivotTable(&PivotTableOption{ assert.NoError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!$A$1:$E$31", DataRange: "Sheet1!$A$1:$E$31",
PivotTableRange: "Sheet1!$AJ$2:$AP1$35", PivotTableRange: "Sheet1!$AJ$2:$AP1$35",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -110,7 +110,7 @@ func TestAddPivotTable(t *testing.T) {
PivotTableStyleName: "PivotStyleLight19", PivotTableStyleName: "PivotStyleLight19",
})) }))
f.NewSheet("Sheet2") f.NewSheet("Sheet2")
assert.NoError(t, f.AddPivotTable(&PivotTableOption{ assert.NoError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!$A$1:$E$31", DataRange: "Sheet1!$A$1:$E$31",
PivotTableRange: "Sheet2!$A$1:$AR$15", PivotTableRange: "Sheet2!$A$1:$AR$15",
Rows: []PivotTableField{{Data: "Month"}}, Rows: []PivotTableField{{Data: "Month"}},
@ -123,7 +123,7 @@ func TestAddPivotTable(t *testing.T) {
ShowColHeaders: true, ShowColHeaders: true,
ShowLastColumn: true, ShowLastColumn: true,
})) }))
assert.NoError(t, f.AddPivotTable(&PivotTableOption{ assert.NoError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!$A$1:$E$31", DataRange: "Sheet1!$A$1:$E$31",
PivotTableRange: "Sheet2!$A$18:$AR$54", PivotTableRange: "Sheet2!$A$18:$AR$54",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Type"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Type"}},
@ -143,7 +143,7 @@ func TestAddPivotTable(t *testing.T) {
Comment: "Pivot Table Data Range", Comment: "Pivot Table Data Range",
Scope: "Sheet2", Scope: "Sheet2",
})) }))
assert.NoError(t, f.AddPivotTable(&PivotTableOption{ assert.NoError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "dataRange", DataRange: "dataRange",
PivotTableRange: "Sheet2!$A$57:$AJ$91", PivotTableRange: "Sheet2!$A$57:$AJ$91",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -160,7 +160,7 @@ func TestAddPivotTable(t *testing.T) {
// Test empty pivot table options // Test empty pivot table options
assert.EqualError(t, f.AddPivotTable(nil), ErrParameterRequired.Error()) assert.EqualError(t, f.AddPivotTable(nil), ErrParameterRequired.Error())
// Test invalid data range // Test invalid data range
assert.EqualError(t, f.AddPivotTable(&PivotTableOption{ assert.EqualError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!$A$1:$A$1", DataRange: "Sheet1!$A$1:$A$1",
PivotTableRange: "Sheet1!$U$34:$O$2", PivotTableRange: "Sheet1!$U$34:$O$2",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -168,7 +168,7 @@ func TestAddPivotTable(t *testing.T) {
Data: []PivotTableField{{Data: "Sales"}}, Data: []PivotTableField{{Data: "Sales"}},
}), `parameter 'DataRange' parsing error: parameter is invalid`) }), `parameter 'DataRange' parsing error: parameter is invalid`)
// Test the data range of the worksheet that is not declared // Test the data range of the worksheet that is not declared
assert.EqualError(t, f.AddPivotTable(&PivotTableOption{ assert.EqualError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "$A$1:$E$31", DataRange: "$A$1:$E$31",
PivotTableRange: "Sheet1!$U$34:$O$2", PivotTableRange: "Sheet1!$U$34:$O$2",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -176,7 +176,7 @@ func TestAddPivotTable(t *testing.T) {
Data: []PivotTableField{{Data: "Sales"}}, Data: []PivotTableField{{Data: "Sales"}},
}), `parameter 'DataRange' parsing error: parameter is invalid`) }), `parameter 'DataRange' parsing error: parameter is invalid`)
// Test the worksheet declared in the data range does not exist // Test the worksheet declared in the data range does not exist
assert.EqualError(t, f.AddPivotTable(&PivotTableOption{ assert.EqualError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "SheetN!$A$1:$E$31", DataRange: "SheetN!$A$1:$E$31",
PivotTableRange: "Sheet1!$U$34:$O$2", PivotTableRange: "Sheet1!$U$34:$O$2",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -184,7 +184,7 @@ func TestAddPivotTable(t *testing.T) {
Data: []PivotTableField{{Data: "Sales"}}, Data: []PivotTableField{{Data: "Sales"}},
}), "sheet SheetN does not exist") }), "sheet SheetN does not exist")
// Test the pivot table range of the worksheet that is not declared // Test the pivot table range of the worksheet that is not declared
assert.EqualError(t, f.AddPivotTable(&PivotTableOption{ assert.EqualError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!$A$1:$E$31", DataRange: "Sheet1!$A$1:$E$31",
PivotTableRange: "$U$34:$O$2", PivotTableRange: "$U$34:$O$2",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -192,7 +192,7 @@ func TestAddPivotTable(t *testing.T) {
Data: []PivotTableField{{Data: "Sales"}}, Data: []PivotTableField{{Data: "Sales"}},
}), `parameter 'PivotTableRange' parsing error: parameter is invalid`) }), `parameter 'PivotTableRange' parsing error: parameter is invalid`)
// Test the worksheet declared in the pivot table range does not exist // Test the worksheet declared in the pivot table range does not exist
assert.EqualError(t, f.AddPivotTable(&PivotTableOption{ assert.EqualError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!$A$1:$E$31", DataRange: "Sheet1!$A$1:$E$31",
PivotTableRange: "SheetN!$U$34:$O$2", PivotTableRange: "SheetN!$U$34:$O$2",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -200,7 +200,7 @@ func TestAddPivotTable(t *testing.T) {
Data: []PivotTableField{{Data: "Sales"}}, Data: []PivotTableField{{Data: "Sales"}},
}), "sheet SheetN does not exist") }), "sheet SheetN does not exist")
// Test not exists worksheet in data range // Test not exists worksheet in data range
assert.EqualError(t, f.AddPivotTable(&PivotTableOption{ assert.EqualError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "SheetN!$A$1:$E$31", DataRange: "SheetN!$A$1:$E$31",
PivotTableRange: "Sheet1!$U$34:$O$2", PivotTableRange: "Sheet1!$U$34:$O$2",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -208,7 +208,7 @@ func TestAddPivotTable(t *testing.T) {
Data: []PivotTableField{{Data: "Sales"}}, Data: []PivotTableField{{Data: "Sales"}},
}), "sheet SheetN does not exist") }), "sheet SheetN does not exist")
// Test invalid row number in data range // Test invalid row number in data range
assert.EqualError(t, f.AddPivotTable(&PivotTableOption{ assert.EqualError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!$A$0:$E$31", DataRange: "Sheet1!$A$0:$E$31",
PivotTableRange: "Sheet1!$U$34:$O$2", PivotTableRange: "Sheet1!$U$34:$O$2",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -217,7 +217,7 @@ func TestAddPivotTable(t *testing.T) {
}), `parameter 'DataRange' parsing error: cannot convert cell "A0" to coordinates: invalid cell name "A0"`) }), `parameter 'DataRange' parsing error: cannot convert cell "A0" to coordinates: invalid cell name "A0"`)
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddPivotTable1.xlsx"))) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddPivotTable1.xlsx")))
// Test with field names that exceed the length limit and invalid subtotal // Test with field names that exceed the length limit and invalid subtotal
assert.NoError(t, f.AddPivotTable(&PivotTableOption{ assert.NoError(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!$A$1:$E$31", DataRange: "Sheet1!$A$1:$E$31",
PivotTableRange: "Sheet1!$G$2:$M$34", PivotTableRange: "Sheet1!$G$2:$M$34",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -232,12 +232,12 @@ func TestAddPivotTable(t *testing.T) {
_, _, err = f.adjustRange("sheet1!") _, _, err = f.adjustRange("sheet1!")
assert.EqualError(t, err, "parameter is invalid") assert.EqualError(t, err, "parameter is invalid")
// Test get pivot fields order with empty data range // Test get pivot fields order with empty data range
_, err = f.getPivotFieldsOrder(&PivotTableOption{}) _, err = f.getPivotFieldsOrder(&PivotTableOptions{})
assert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`) assert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`)
// Test add pivot cache with empty data range // Test add pivot cache with empty data range
assert.EqualError(t, f.addPivotCache("", &PivotTableOption{}), "parameter 'DataRange' parsing error: parameter is required") assert.EqualError(t, f.addPivotCache("", &PivotTableOptions{}), "parameter 'DataRange' parsing error: parameter is required")
// Test add pivot cache with invalid data range // Test add pivot cache with invalid data range
assert.EqualError(t, f.addPivotCache("", &PivotTableOption{ assert.EqualError(t, f.addPivotCache("", &PivotTableOptions{
DataRange: "$A$1:$E$31", DataRange: "$A$1:$E$31",
PivotTableRange: "Sheet1!$U$34:$O$2", PivotTableRange: "Sheet1!$U$34:$O$2",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -245,11 +245,11 @@ func TestAddPivotTable(t *testing.T) {
Data: []PivotTableField{{Data: "Sales"}}, Data: []PivotTableField{{Data: "Sales"}},
}), "parameter 'DataRange' parsing error: parameter is invalid") }), "parameter 'DataRange' parsing error: parameter is invalid")
// Test add pivot table with empty options // Test add pivot table with empty options
assert.EqualError(t, f.addPivotTable(0, 0, "", &PivotTableOption{}), "parameter 'PivotTableRange' parsing error: parameter is required") assert.EqualError(t, f.addPivotTable(0, 0, "", &PivotTableOptions{}), "parameter 'PivotTableRange' parsing error: parameter is required")
// Test add pivot table with invalid data range // Test add pivot table with invalid data range
assert.EqualError(t, f.addPivotTable(0, 0, "", &PivotTableOption{}), "parameter 'PivotTableRange' parsing error: parameter is required") assert.EqualError(t, f.addPivotTable(0, 0, "", &PivotTableOptions{}), "parameter 'PivotTableRange' parsing error: parameter is required")
// Test add pivot fields with empty data range // Test add pivot fields with empty data range
assert.EqualError(t, f.addPivotFields(nil, &PivotTableOption{ assert.EqualError(t, f.addPivotFields(nil, &PivotTableOptions{
DataRange: "$A$1:$E$31", DataRange: "$A$1:$E$31",
PivotTableRange: "Sheet1!$U$34:$O$2", PivotTableRange: "Sheet1!$U$34:$O$2",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
@ -257,14 +257,14 @@ func TestAddPivotTable(t *testing.T) {
Data: []PivotTableField{{Data: "Sales"}}, Data: []PivotTableField{{Data: "Sales"}},
}), `parameter 'DataRange' parsing error: parameter is invalid`) }), `parameter 'DataRange' parsing error: parameter is invalid`)
// Test get pivot fields index with empty data range // Test get pivot fields index with empty data range
_, err = f.getPivotFieldsIndex([]PivotTableField{}, &PivotTableOption{}) _, err = f.getPivotFieldsIndex([]PivotTableField{}, &PivotTableOptions{})
assert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`) assert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`)
} }
func TestAddPivotRowFields(t *testing.T) { func TestAddPivotRowFields(t *testing.T) {
f := NewFile() f := NewFile()
// Test invalid data range // Test invalid data range
assert.EqualError(t, f.addPivotRowFields(&xlsxPivotTableDefinition{}, &PivotTableOption{ assert.EqualError(t, f.addPivotRowFields(&xlsxPivotTableDefinition{}, &PivotTableOptions{
DataRange: "Sheet1!$A$1:$A$1", DataRange: "Sheet1!$A$1:$A$1",
}), `parameter 'DataRange' parsing error: parameter is invalid`) }), `parameter 'DataRange' parsing error: parameter is invalid`)
} }
@ -272,7 +272,7 @@ func TestAddPivotRowFields(t *testing.T) {
func TestAddPivotPageFields(t *testing.T) { func TestAddPivotPageFields(t *testing.T) {
f := NewFile() f := NewFile()
// Test invalid data range // Test invalid data range
assert.EqualError(t, f.addPivotPageFields(&xlsxPivotTableDefinition{}, &PivotTableOption{ assert.EqualError(t, f.addPivotPageFields(&xlsxPivotTableDefinition{}, &PivotTableOptions{
DataRange: "Sheet1!$A$1:$A$1", DataRange: "Sheet1!$A$1:$A$1",
}), `parameter 'DataRange' parsing error: parameter is invalid`) }), `parameter 'DataRange' parsing error: parameter is invalid`)
} }
@ -280,7 +280,7 @@ func TestAddPivotPageFields(t *testing.T) {
func TestAddPivotDataFields(t *testing.T) { func TestAddPivotDataFields(t *testing.T) {
f := NewFile() f := NewFile()
// Test invalid data range // Test invalid data range
assert.EqualError(t, f.addPivotDataFields(&xlsxPivotTableDefinition{}, &PivotTableOption{ assert.EqualError(t, f.addPivotDataFields(&xlsxPivotTableDefinition{}, &PivotTableOptions{
DataRange: "Sheet1!$A$1:$A$1", DataRange: "Sheet1!$A$1:$A$1",
}), `parameter 'DataRange' parsing error: parameter is invalid`) }), `parameter 'DataRange' parsing error: parameter is invalid`)
} }
@ -288,7 +288,7 @@ func TestAddPivotDataFields(t *testing.T) {
func TestAddPivotColFields(t *testing.T) { func TestAddPivotColFields(t *testing.T) {
f := NewFile() f := NewFile()
// Test invalid data range // Test invalid data range
assert.EqualError(t, f.addPivotColFields(&xlsxPivotTableDefinition{}, &PivotTableOption{ assert.EqualError(t, f.addPivotColFields(&xlsxPivotTableDefinition{}, &PivotTableOptions{
DataRange: "Sheet1!$A$1:$A$1", DataRange: "Sheet1!$A$1:$A$1",
Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}},
}), `parameter 'DataRange' parsing error: parameter is invalid`) }), `parameter 'DataRange' parsing error: parameter is invalid`)
@ -297,7 +297,7 @@ func TestAddPivotColFields(t *testing.T) {
func TestGetPivotFieldsOrder(t *testing.T) { func TestGetPivotFieldsOrder(t *testing.T) {
f := NewFile() f := NewFile()
// Test get pivot fields order with not exist worksheet // Test get pivot fields order with not exist worksheet
_, err := f.getPivotFieldsOrder(&PivotTableOption{DataRange: "SheetN!$A$1:$E$31"}) _, err := f.getPivotFieldsOrder(&PivotTableOptions{DataRange: "SheetN!$A$1:$E$31"})
assert.EqualError(t, err, "sheet SheetN does not exist") assert.EqualError(t, err, "sheet SheetN does not exist")
} }

View File

@ -165,10 +165,10 @@ func TestRowHeight(t *testing.T) {
assert.EqualError(t, err, "sheet SheetN does not exist") assert.EqualError(t, err, "sheet SheetN does not exist")
// Test get row height with custom default row height. // Test get row height with custom default row height.
assert.NoError(t, f.SetSheetFormatPr(sheet1, assert.NoError(t, f.SetSheetProps(sheet1, &SheetPropsOptions{
DefaultRowHeight(30.0), DefaultRowHeight: float64Ptr(30.0),
CustomHeight(true), CustomHeight: boolPtr(true),
)) }))
height, err = f.GetRowHeight(sheet1, 100) height, err = f.GetRowHeight(sheet1, 100)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 30.0, height) assert.Equal(t, 30.0, height)

View File

@ -17,21 +17,21 @@ import (
"strings" "strings"
) )
// parseFormatShapeSet provides a function to parse the format settings of the // parseShapeOptions provides a function to parse the format settings of the
// shape with default value. // shape with default value.
func parseFormatShapeSet(formatSet string) (*formatShape, error) { func parseShapeOptions(opts string) (*shapeOptions, error) {
format := formatShape{ options := shapeOptions{
Width: 160, Width: 160,
Height: 160, Height: 160,
Format: formatPicture{ Format: pictureOptions{
FPrintsWithSheet: true, FPrintsWithSheet: true,
XScale: 1, XScale: 1,
YScale: 1, YScale: 1,
}, },
Line: formatLine{Width: 1}, Line: lineOptions{Width: 1},
} }
err := json.Unmarshal([]byte(formatSet), &format) err := json.Unmarshal([]byte(opts), &options)
return &format, err return &options, err
} }
// AddShape provides the method to add shape in a sheet by given worksheet // AddShape provides the method to add shape in a sheet by given worksheet
@ -277,8 +277,8 @@ func parseFormatShapeSet(formatSet string) (*formatShape, error) {
// wavy // wavy
// wavyHeavy // wavyHeavy
// wavyDbl // wavyDbl
func (f *File) AddShape(sheet, cell, format string) error { func (f *File) AddShape(sheet, cell, opts string) error {
formatSet, err := parseFormatShapeSet(format) options, err := parseShapeOptions(opts)
if err != nil { if err != nil {
return err return err
} }
@ -305,7 +305,7 @@ func (f *File) AddShape(sheet, cell, format string) error {
f.addSheetDrawing(sheet, rID) f.addSheetDrawing(sheet, rID)
f.addSheetNameSpace(sheet, SourceRelationship) f.addSheetNameSpace(sheet, SourceRelationship)
} }
err = f.addDrawingShape(sheet, drawingXML, cell, formatSet) err = f.addDrawingShape(sheet, drawingXML, cell, options)
if err != nil { if err != nil {
return err return err
} }
@ -315,7 +315,7 @@ func (f *File) AddShape(sheet, cell, format string) error {
// addDrawingShape provides a function to add preset geometry by given sheet, // addDrawingShape provides a function to add preset geometry by given sheet,
// drawingXMLand format sets. // drawingXMLand format sets.
func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *formatShape) error { func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *shapeOptions) error {
fromCol, fromRow, err := CellNameToCoordinates(cell) fromCol, fromRow, err := CellNameToCoordinates(cell)
if err != nil { if err != nil {
return err return err
@ -344,19 +344,19 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *format
"wavyDbl": true, "wavyDbl": true,
} }
width := int(float64(formatSet.Width) * formatSet.Format.XScale) width := int(float64(opts.Width) * opts.Format.XScale)
height := int(float64(formatSet.Height) * formatSet.Format.YScale) height := int(float64(opts.Height) * opts.Format.YScale)
colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, colIdx, rowIdx, formatSet.Format.OffsetX, formatSet.Format.OffsetY, colStart, rowStart, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, colIdx, rowIdx, opts.Format.OffsetX, opts.Format.OffsetY,
width, height) width, height)
content, cNvPrID := f.drawingParser(drawingXML) content, cNvPrID := f.drawingParser(drawingXML)
twoCellAnchor := xdrCellAnchor{} twoCellAnchor := xdrCellAnchor{}
twoCellAnchor.EditAs = formatSet.Format.Positioning twoCellAnchor.EditAs = opts.Format.Positioning
from := xlsxFrom{} from := xlsxFrom{}
from.Col = colStart from.Col = colStart
from.ColOff = formatSet.Format.OffsetX * EMU from.ColOff = opts.Format.OffsetX * EMU
from.Row = rowStart from.Row = rowStart
from.RowOff = formatSet.Format.OffsetY * EMU from.RowOff = opts.Format.OffsetY * EMU
to := xlsxTo{} to := xlsxTo{}
to.Col = colEnd to.Col = colEnd
to.ColOff = x2 * EMU to.ColOff = x2 * EMU
@ -365,7 +365,7 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *format
twoCellAnchor.From = &from twoCellAnchor.From = &from
twoCellAnchor.To = &to twoCellAnchor.To = &to
shape := xdrSp{ shape := xdrSp{
Macro: formatSet.Macro, Macro: opts.Macro,
NvSpPr: &xdrNvSpPr{ NvSpPr: &xdrNvSpPr{
CNvPr: &xlsxCNvPr{ CNvPr: &xlsxCNvPr{
ID: cNvPrID, ID: cNvPrID,
@ -377,13 +377,13 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *format
}, },
SpPr: &xlsxSpPr{ SpPr: &xlsxSpPr{
PrstGeom: xlsxPrstGeom{ PrstGeom: xlsxPrstGeom{
Prst: formatSet.Type, Prst: opts.Type,
}, },
}, },
Style: &xdrStyle{ Style: &xdrStyle{
LnRef: setShapeRef(formatSet.Color.Line, 2), LnRef: setShapeRef(opts.Color.Line, 2),
FillRef: setShapeRef(formatSet.Color.Fill, 1), FillRef: setShapeRef(opts.Color.Fill, 1),
EffectRef: setShapeRef(formatSet.Color.Effect, 0), EffectRef: setShapeRef(opts.Color.Effect, 0),
FontRef: &aFontRef{ FontRef: &aFontRef{
Idx: "minor", Idx: "minor",
SchemeClr: &attrValString{ SchemeClr: &attrValString{
@ -401,13 +401,13 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *format
}, },
}, },
} }
if formatSet.Line.Width != 1 { if opts.Line.Width != 1 {
shape.SpPr.Ln = xlsxLineProperties{ shape.SpPr.Ln = xlsxLineProperties{
W: f.ptToEMUs(formatSet.Line.Width), W: f.ptToEMUs(opts.Line.Width),
} }
} }
if len(formatSet.Paragraph) < 1 { if len(opts.Paragraph) < 1 {
formatSet.Paragraph = []formatShapeParagraph{ opts.Paragraph = []shapeParagraphOptions{
{ {
Font: Font{ Font: Font{
Bold: false, Bold: false,
@ -421,7 +421,7 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *format
}, },
} }
} }
for _, p := range formatSet.Paragraph { for _, p := range opts.Paragraph {
u := p.Font.Underline u := p.Font.Underline
_, ok := textUnderlineType[u] _, ok := textUnderlineType[u]
if !ok { if !ok {
@ -460,8 +460,8 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *format
} }
twoCellAnchor.Sp = &shape twoCellAnchor.Sp = &shape
twoCellAnchor.ClientData = &xdrClientData{ twoCellAnchor.ClientData = &xdrClientData{
FLocksWithSheet: formatSet.Format.FLocksWithSheet, FLocksWithSheet: opts.Format.FLocksWithSheet,
FPrintsWithSheet: formatSet.Format.FPrintsWithSheet, FPrintsWithSheet: opts.Format.FPrintsWithSheet,
} }
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor) content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
f.Drawings.Store(drawingXML, content) f.Drawings.Store(drawingXML, content)

328
sheet.go
View File

@ -38,10 +38,10 @@ import (
// Note that when creating a new workbook, the default worksheet named // Note that when creating a new workbook, the default worksheet named
// `Sheet1` will be created. // `Sheet1` will be created.
func (f *File) NewSheet(sheet string) int { func (f *File) NewSheet(sheet string) int {
// Check if the worksheet already exists
if trimSheetName(sheet) == "" { if trimSheetName(sheet) == "" {
return -1 return -1
} }
// Check if the worksheet already exists
index := f.GetSheetIndex(sheet) index := f.GetSheetIndex(sheet)
if index != -1 { if index != -1 {
return index return index
@ -675,10 +675,10 @@ func (f *File) SetSheetVisible(sheet string, visible bool) error {
return nil return nil
} }
// parseFormatPanesSet provides a function to parse the panes settings. // parsePanesOptions provides a function to parse the panes settings.
func parseFormatPanesSet(formatSet string) (*formatPanes, error) { func parsePanesOptions(opts string) (*panesOptions, error) {
format := formatPanes{} format := panesOptions{}
err := json.Unmarshal([]byte(formatSet), &format) err := json.Unmarshal([]byte(opts), &format)
return &format, err return &format, err
} }
@ -767,7 +767,7 @@ func parseFormatPanesSet(formatSet string) (*formatPanes, error) {
// //
// f.SetPanes("Sheet1", `{"freeze":false,"split":false}`) // f.SetPanes("Sheet1", `{"freeze":false,"split":false}`)
func (f *File) SetPanes(sheet, panes string) error { func (f *File) SetPanes(sheet, panes string) error {
fs, _ := parseFormatPanesSet(panes) fs, _ := parsePanesOptions(panes)
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return err return err
@ -1021,7 +1021,7 @@ func attrValToBool(name string, attrs []xml.Attr) (val bool, err error) {
// | // |
// &R | Right section // &R | Right section
// | // |
// &S | Strikethrough text format // &S | Strike through text format
// | // |
// &T | Current time // &T | Current time
// | // |
@ -1068,7 +1068,7 @@ func attrValToBool(name string, attrs []xml.Attr) (val bool, err error) {
// that same page // that same page
// //
// - No footer on the first page // - No footer on the first page
func (f *File) SetHeaderFooter(sheet string, settings *FormatHeaderFooter) error { func (f *File) SetHeaderFooter(sheet string, settings *HeaderFooterOptions) error {
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return err return err
@ -1113,13 +1113,13 @@ func (f *File) SetHeaderFooter(sheet string, settings *FormatHeaderFooter) error
// Password: "password", // Password: "password",
// EditScenarios: false, // EditScenarios: false,
// }) // })
func (f *File) ProtectSheet(sheet string, settings *FormatSheetProtection) error { func (f *File) ProtectSheet(sheet string, settings *SheetProtectionOptions) error {
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return err return err
} }
if settings == nil { if settings == nil {
settings = &FormatSheetProtection{ settings = &SheetProtectionOptions{
EditObjects: true, EditObjects: true,
EditScenarios: true, EditScenarios: true,
SelectLockedCells: true, SelectLockedCells: true,
@ -1213,173 +1213,8 @@ func trimSheetName(name string) string {
return name return name
} }
// PageLayoutOption is an option of a page layout of a worksheet. See
// SetPageLayout().
type PageLayoutOption interface {
setPageLayout(layout *xlsxPageSetUp)
}
// PageLayoutOptionPtr is a writable PageLayoutOption. See GetPageLayout().
type PageLayoutOptionPtr interface {
PageLayoutOption
getPageLayout(layout *xlsxPageSetUp)
}
type (
// BlackAndWhite specified print black and white.
BlackAndWhite bool
// FirstPageNumber specified the first printed page number. If no value is
// specified, then 'automatic' is assumed.
FirstPageNumber uint
// PageLayoutOrientation defines the orientation of page layout for a
// worksheet.
PageLayoutOrientation string
// PageLayoutPaperSize defines the paper size of the worksheet.
PageLayoutPaperSize int
// FitToHeight specified the number of vertical pages to fit on.
FitToHeight int
// FitToWidth specified the number of horizontal pages to fit on.
FitToWidth int
// PageLayoutScale defines the print scaling. This attribute is restricted
// to value ranging from 10 (10%) to 400 (400%). This setting is
// overridden when fitToWidth and/or fitToHeight are in use.
PageLayoutScale uint
)
const (
// OrientationPortrait indicates page layout orientation id portrait.
OrientationPortrait = "portrait"
// OrientationLandscape indicates page layout orientation id landscape.
OrientationLandscape = "landscape"
)
// setPageLayout provides a method to set the print black and white for the
// worksheet.
func (p BlackAndWhite) setPageLayout(ps *xlsxPageSetUp) {
ps.BlackAndWhite = bool(p)
}
// getPageLayout provides a method to get the print black and white for the
// worksheet.
func (p *BlackAndWhite) getPageLayout(ps *xlsxPageSetUp) {
if ps == nil {
*p = false
return
}
*p = BlackAndWhite(ps.BlackAndWhite)
}
// setPageLayout provides a method to set the first printed page number for
// the worksheet.
func (p FirstPageNumber) setPageLayout(ps *xlsxPageSetUp) {
if 0 < int(p) {
ps.FirstPageNumber = strconv.Itoa(int(p))
ps.UseFirstPageNumber = true
}
}
// getPageLayout provides a method to get the first printed page number for
// the worksheet.
func (p *FirstPageNumber) getPageLayout(ps *xlsxPageSetUp) {
if ps != nil && ps.UseFirstPageNumber {
if number, _ := strconv.Atoi(ps.FirstPageNumber); number != 0 {
*p = FirstPageNumber(number)
return
}
}
*p = 1
}
// setPageLayout provides a method to set the orientation for the worksheet.
func (o PageLayoutOrientation) setPageLayout(ps *xlsxPageSetUp) {
ps.Orientation = string(o)
}
// getPageLayout provides a method to get the orientation for the worksheet.
func (o *PageLayoutOrientation) getPageLayout(ps *xlsxPageSetUp) {
// Excel default: portrait
if ps == nil || ps.Orientation == "" {
*o = OrientationPortrait
return
}
*o = PageLayoutOrientation(ps.Orientation)
}
// setPageLayout provides a method to set the paper size for the worksheet.
func (p PageLayoutPaperSize) setPageLayout(ps *xlsxPageSetUp) {
ps.PaperSize = intPtr(int(p))
}
// getPageLayout provides a method to get the paper size for the worksheet.
func (p *PageLayoutPaperSize) getPageLayout(ps *xlsxPageSetUp) {
// Excel default: 1
if ps == nil || ps.PaperSize == nil {
*p = 1
return
}
*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 = intPtr(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 == nil {
*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 = intPtr(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 == nil {
*p = 1
return
}
*p = FitToWidth(*ps.FitToWidth)
}
// setPageLayout provides a method to set the scale for the worksheet.
func (p PageLayoutScale) setPageLayout(ps *xlsxPageSetUp) {
if 10 <= int(p) && int(p) <= 400 {
ps.Scale = int(p)
}
}
// getPageLayout provides a method to get the scale for the worksheet.
func (p *PageLayoutScale) getPageLayout(ps *xlsxPageSetUp) {
if ps == nil || ps.Scale < 10 || ps.Scale > 400 {
*p = 100
return
}
*p = PageLayoutScale(ps.Scale)
}
// SetPageLayout provides a function to sets worksheet page layout. // SetPageLayout provides a function to sets worksheet page layout.
// //
// Available options:
//
// BlackAndWhite(bool)
// FirstPageNumber(uint)
// PageLayoutOrientation(string)
// PageLayoutPaperSize(int)
// FitToHeight(int)
// FitToWidth(int)
// PageLayoutScale(uint)
//
// The following shows the paper size sorted by excelize index number: // The following shows the paper size sorted by excelize index number:
// //
// Index | Paper Size // Index | Paper Size
@ -1500,42 +1335,93 @@ func (p *PageLayoutScale) getPageLayout(ps *xlsxPageSetUp) {
// 116 | PRC Envelope #8 Rotated (309 mm x 120 mm) // 116 | PRC Envelope #8 Rotated (309 mm x 120 mm)
// 117 | PRC Envelope #9 Rotated (324 mm x 229 mm) // 117 | PRC Envelope #9 Rotated (324 mm x 229 mm)
// 118 | PRC Envelope #10 Rotated (458 mm x 324 mm) // 118 | PRC Envelope #10 Rotated (458 mm x 324 mm)
func (f *File) SetPageLayout(sheet string, opts ...PageLayoutOption) error { func (f *File) SetPageLayout(sheet string, opts *PageLayoutOptions) error {
s, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return err return err
} }
ps := s.PageSetUp if opts == nil {
if ps == nil { return err
ps = new(xlsxPageSetUp)
s.PageSetUp = ps
}
for _, opt := range opts {
opt.setPageLayout(ps)
} }
ws.setPageSetUp(opts)
return err return err
} }
// GetPageLayout provides a function to gets worksheet page layout. // newPageSetUp initialize page setup settings for the worksheet if which not
// // exist.
// Available options: func (ws *xlsxWorksheet) newPageSetUp() {
// if ws.PageSetUp == nil {
// PageLayoutOrientation(string) ws.PageSetUp = new(xlsxPageSetUp)
// PageLayoutPaperSize(int)
// FitToHeight(int)
// FitToWidth(int)
func (f *File) GetPageLayout(sheet string, opts ...PageLayoutOptionPtr) error {
s, err := f.workSheetReader(sheet)
if err != nil {
return err
} }
ps := s.PageSetUp }
for _, opt := range opts { // setPageSetUp set page setup settings for the worksheet by given options.
opt.getPageLayout(ps) func (ws *xlsxWorksheet) setPageSetUp(opts *PageLayoutOptions) {
if opts.Size != nil {
ws.newPageSetUp()
ws.PageSetUp.PaperSize = opts.Size
} }
return err if opts.Orientation != nil && (*opts.Orientation == "portrait" || *opts.Orientation == "landscape") {
ws.newPageSetUp()
ws.PageSetUp.Orientation = *opts.Orientation
}
if opts.FirstPageNumber != nil && *opts.FirstPageNumber > 0 {
ws.newPageSetUp()
ws.PageSetUp.FirstPageNumber = strconv.Itoa(int(*opts.FirstPageNumber))
ws.PageSetUp.UseFirstPageNumber = true
}
if opts.AdjustTo != nil && 10 <= *opts.AdjustTo && *opts.AdjustTo <= 400 {
ws.newPageSetUp()
ws.PageSetUp.Scale = int(*opts.AdjustTo)
}
if opts.FitToHeight != nil {
ws.newPageSetUp()
ws.PageSetUp.FitToHeight = opts.FitToHeight
}
if opts.FitToWidth != nil {
ws.newPageSetUp()
ws.PageSetUp.FitToWidth = opts.FitToWidth
}
if opts.BlackAndWhite != nil {
ws.newPageSetUp()
ws.PageSetUp.BlackAndWhite = *opts.BlackAndWhite
}
}
// GetPageLayout provides a function to gets worksheet page layout.
func (f *File) GetPageLayout(sheet string) (PageLayoutOptions, error) {
opts := PageLayoutOptions{
Size: intPtr(0),
Orientation: stringPtr("portrait"),
FirstPageNumber: uintPtr(1),
AdjustTo: uintPtr(100),
}
ws, err := f.workSheetReader(sheet)
if err != nil {
return opts, err
}
if ws.PageSetUp != nil {
if ws.PageSetUp.PaperSize != nil {
opts.Size = ws.PageSetUp.PaperSize
}
if ws.PageSetUp.Orientation != "" {
opts.Orientation = stringPtr(ws.PageSetUp.Orientation)
}
if num, _ := strconv.Atoi(ws.PageSetUp.FirstPageNumber); num != 0 {
opts.FirstPageNumber = uintPtr(uint(num))
}
if ws.PageSetUp.Scale >= 10 && ws.PageSetUp.Scale <= 400 {
opts.AdjustTo = uintPtr(uint(ws.PageSetUp.Scale))
}
if ws.PageSetUp.FitToHeight != nil {
opts.FitToHeight = ws.PageSetUp.FitToHeight
}
if ws.PageSetUp.FitToWidth != nil {
opts.FitToWidth = ws.PageSetUp.FitToWidth
}
opts.BlackAndWhite = boolPtr(ws.PageSetUp.BlackAndWhite)
}
return opts, err
} }
// SetDefinedName provides a function to set the defined names of the workbook // SetDefinedName provides a function to set the defined names of the workbook
@ -1690,20 +1576,23 @@ func (f *File) UngroupSheets() error {
// ends and where begins the next one by given worksheet name and cell reference, so the // ends and where begins the next one by given worksheet name and cell reference, so the
// content before the page break will be printed on one page and after the // content before the page break will be printed on one page and after the
// page break on another. // page break on another.
func (f *File) InsertPageBreak(sheet, cell string) (err error) { func (f *File) InsertPageBreak(sheet, cell string) error {
var ws *xlsxWorksheet var (
var row, col int ws *xlsxWorksheet
row, col int
err error
)
rowBrk, colBrk := -1, -1 rowBrk, colBrk := -1, -1
if ws, err = f.workSheetReader(sheet); err != nil { if ws, err = f.workSheetReader(sheet); err != nil {
return return err
} }
if col, row, err = CellNameToCoordinates(cell); err != nil { if col, row, err = CellNameToCoordinates(cell); err != nil {
return return err
} }
col-- col--
row-- row--
if col == row && col == 0 { if col == row && col == 0 {
return return err
} }
if ws.RowBreaks == nil { if ws.RowBreaks == nil {
ws.RowBreaks = &xlsxBreaks{} ws.RowBreaks = &xlsxBreaks{}
@ -1741,24 +1630,27 @@ func (f *File) InsertPageBreak(sheet, cell string) (err error) {
} }
ws.RowBreaks.Count = len(ws.RowBreaks.Brk) ws.RowBreaks.Count = len(ws.RowBreaks.Brk)
ws.ColBreaks.Count = len(ws.ColBreaks.Brk) ws.ColBreaks.Count = len(ws.ColBreaks.Brk)
return return err
} }
// RemovePageBreak remove a page break by given worksheet name and cell // RemovePageBreak remove a page break by given worksheet name and cell
// reference. // reference.
func (f *File) RemovePageBreak(sheet, cell string) (err error) { func (f *File) RemovePageBreak(sheet, cell string) error {
var ws *xlsxWorksheet var (
var row, col int ws *xlsxWorksheet
row, col int
err error
)
if ws, err = f.workSheetReader(sheet); err != nil { if ws, err = f.workSheetReader(sheet); err != nil {
return return err
} }
if col, row, err = CellNameToCoordinates(cell); err != nil { if col, row, err = CellNameToCoordinates(cell); err != nil {
return return err
} }
col-- col--
row-- row--
if col == row && col == 0 { if col == row && col == 0 {
return return err
} }
removeBrk := func(ID int, brks []*xlsxBrk) []*xlsxBrk { removeBrk := func(ID int, brks []*xlsxBrk) []*xlsxBrk {
for i, brk := range brks { for i, brk := range brks {
@ -1769,7 +1661,7 @@ func (f *File) RemovePageBreak(sheet, cell string) (err error) {
return brks return brks
} }
if ws.RowBreaks == nil || ws.ColBreaks == nil { if ws.RowBreaks == nil || ws.ColBreaks == nil {
return return err
} }
rowBrks := len(ws.RowBreaks.Brk) rowBrks := len(ws.RowBreaks.Brk)
colBrks := len(ws.ColBreaks.Brk) colBrks := len(ws.ColBreaks.Brk)
@ -1780,20 +1672,20 @@ func (f *File) RemovePageBreak(sheet, cell string) (err error) {
ws.ColBreaks.Count = len(ws.ColBreaks.Brk) ws.ColBreaks.Count = len(ws.ColBreaks.Brk)
ws.RowBreaks.ManualBreakCount-- ws.RowBreaks.ManualBreakCount--
ws.ColBreaks.ManualBreakCount-- ws.ColBreaks.ManualBreakCount--
return return err
} }
if rowBrks > 0 && rowBrks > colBrks { if rowBrks > 0 && rowBrks > colBrks {
ws.RowBreaks.Brk = removeBrk(row, ws.RowBreaks.Brk) ws.RowBreaks.Brk = removeBrk(row, ws.RowBreaks.Brk)
ws.RowBreaks.Count = len(ws.RowBreaks.Brk) ws.RowBreaks.Count = len(ws.RowBreaks.Brk)
ws.RowBreaks.ManualBreakCount-- ws.RowBreaks.ManualBreakCount--
return return err
} }
if colBrks > 0 && colBrks > rowBrks { if colBrks > 0 && colBrks > rowBrks {
ws.ColBreaks.Brk = removeBrk(col, ws.ColBreaks.Brk) ws.ColBreaks.Brk = removeBrk(col, ws.ColBreaks.Brk)
ws.ColBreaks.Count = len(ws.ColBreaks.Brk) ws.ColBreaks.Count = len(ws.ColBreaks.Brk)
ws.ColBreaks.ManualBreakCount-- ws.ColBreaks.ManualBreakCount--
} }
return return err
} }
// relsReader provides a function to get the pointer to the structure // relsReader provides a function to get the pointer to the structure

View File

@ -8,78 +8,9 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/mohae/deepcopy"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func ExampleFile_SetPageLayout() {
f := NewFile()
if err := f.SetPageLayout(
"Sheet1",
BlackAndWhite(true),
FirstPageNumber(2),
PageLayoutOrientation(OrientationLandscape),
PageLayoutPaperSize(10),
FitToHeight(2),
FitToWidth(2),
PageLayoutScale(50),
); err != nil {
fmt.Println(err)
}
// Output:
}
func ExampleFile_GetPageLayout() {
f := NewFile()
var (
blackAndWhite BlackAndWhite
firstPageNumber FirstPageNumber
orientation PageLayoutOrientation
paperSize PageLayoutPaperSize
fitToHeight FitToHeight
fitToWidth FitToWidth
scale PageLayoutScale
)
if err := f.GetPageLayout("Sheet1", &blackAndWhite); err != nil {
fmt.Println(err)
}
if err := f.GetPageLayout("Sheet1", &firstPageNumber); err != nil {
fmt.Println(err)
}
if err := f.GetPageLayout("Sheet1", &orientation); err != nil {
fmt.Println(err)
}
if err := f.GetPageLayout("Sheet1", &paperSize); err != nil {
fmt.Println(err)
}
if err := f.GetPageLayout("Sheet1", &fitToHeight); err != nil {
fmt.Println(err)
}
if err := f.GetPageLayout("Sheet1", &fitToWidth); err != nil {
fmt.Println(err)
}
if err := f.GetPageLayout("Sheet1", &scale); err != nil {
fmt.Println(err)
}
fmt.Println("Defaults:")
fmt.Printf("- print black and white: %t\n", blackAndWhite)
fmt.Printf("- page number for first printed page: %d\n", firstPageNumber)
fmt.Printf("- orientation: %q\n", orientation)
fmt.Printf("- paper size: %d\n", paperSize)
fmt.Printf("- fit to height: %d\n", fitToHeight)
fmt.Printf("- fit to width: %d\n", fitToWidth)
fmt.Printf("- scale: %d\n", scale)
// Output:
// Defaults:
// - print black and white: false
// - page number for first printed page: 1
// - orientation: "portrait"
// - paper size: 1
// - fit to height: 1
// - fit to width: 1
// - scale: 100
}
func TestNewSheet(t *testing.T) { func TestNewSheet(t *testing.T) {
f := NewFile() f := NewFile()
f.NewSheet("Sheet2") f.NewSheet("Sheet2")
@ -114,68 +45,6 @@ func TestSetPane(t *testing.T) {
assert.NoError(t, f.SetPanes("Sheet1", `{"freeze":true,"split":false,"x_split":1,"y_split":0,"top_left_cell":"B1","active_pane":"topRight","panes":[{"sqref":"K16","active_cell":"K16","pane":"topRight"}]}`)) assert.NoError(t, f.SetPanes("Sheet1", `{"freeze":true,"split":false,"x_split":1,"y_split":0,"top_left_cell":"B1","active_pane":"topRight","panes":[{"sqref":"K16","active_cell":"K16","pane":"topRight"}]}`))
} }
func TestPageLayoutOption(t *testing.T) {
const sheet = "Sheet1"
testData := []struct {
container PageLayoutOptionPtr
nonDefault PageLayoutOption
}{
{new(BlackAndWhite), BlackAndWhite(true)},
{new(FirstPageNumber), FirstPageNumber(2)},
{new(PageLayoutOrientation), PageLayoutOrientation(OrientationLandscape)},
{new(PageLayoutPaperSize), PageLayoutPaperSize(10)},
{new(FitToHeight), FitToHeight(2)},
{new(FitToWidth), FitToWidth(2)},
{new(PageLayoutScale), PageLayoutScale(50)},
}
for i, test := range testData {
t.Run(fmt.Sprintf("TestData%d", i), func(t *testing.T) {
opts := test.nonDefault
t.Logf("option %T", opts)
def := deepcopy.Copy(test.container).(PageLayoutOptionPtr)
val1 := deepcopy.Copy(def).(PageLayoutOptionPtr)
val2 := deepcopy.Copy(def).(PageLayoutOptionPtr)
f := NewFile()
// Get the default value
assert.NoError(t, f.GetPageLayout(sheet, def), opts)
// Get again and check
assert.NoError(t, f.GetPageLayout(sheet, val1), opts)
if !assert.Equal(t, val1, def, opts) {
t.FailNow()
}
// Set the same value
assert.NoError(t, f.SetPageLayout(sheet, val1), opts)
// Get again and check
assert.NoError(t, f.GetPageLayout(sheet, val1), opts)
if !assert.Equal(t, val1, def, "%T: value should not have changed", opts) {
t.FailNow()
}
// Set a different value
assert.NoError(t, f.SetPageLayout(sheet, test.nonDefault), opts)
assert.NoError(t, f.GetPageLayout(sheet, val1), opts)
// Get again and compare
assert.NoError(t, f.GetPageLayout(sheet, val2), opts)
if !assert.Equal(t, val1, val2, "%T: value should not have changed", opts) {
t.FailNow()
}
// Value should not be the same as the default
if !assert.NotEqual(t, def, val1, "%T: value should have changed from default", opts) {
t.FailNow()
}
// Restore the default value
assert.NoError(t, f.SetPageLayout(sheet, def), opts)
assert.NoError(t, f.GetPageLayout(sheet, val1), opts)
if !assert.Equal(t, def, val1) {
t.FailNow()
}
})
}
}
func TestSearchSheet(t *testing.T) { func TestSearchSheet(t *testing.T) {
f, err := OpenFile(filepath.Join("test", "SharedStrings.xlsx")) f, err := OpenFile(filepath.Join("test", "SharedStrings.xlsx"))
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
@ -226,14 +95,32 @@ func TestSearchSheet(t *testing.T) {
func TestSetPageLayout(t *testing.T) { func TestSetPageLayout(t *testing.T) {
f := NewFile() f := NewFile()
assert.NoError(t, f.SetPageLayout("Sheet1", nil))
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
ws.(*xlsxWorksheet).PageSetUp = nil
expected := PageLayoutOptions{
Size: intPtr(1),
Orientation: stringPtr("landscape"),
FirstPageNumber: uintPtr(1),
AdjustTo: uintPtr(120),
FitToHeight: intPtr(2),
FitToWidth: intPtr(2),
BlackAndWhite: boolPtr(true),
}
assert.NoError(t, f.SetPageLayout("Sheet1", &expected))
opts, err := f.GetPageLayout("Sheet1")
assert.NoError(t, err)
assert.Equal(t, expected, opts)
// Test set page layout on not exists worksheet. // Test set page layout on not exists worksheet.
assert.EqualError(t, f.SetPageLayout("SheetN"), "sheet SheetN does not exist") assert.EqualError(t, f.SetPageLayout("SheetN", nil), "sheet SheetN does not exist")
} }
func TestGetPageLayout(t *testing.T) { func TestGetPageLayout(t *testing.T) {
f := NewFile() f := NewFile()
// Test get page layout on not exists worksheet. // Test get page layout on not exists worksheet.
assert.EqualError(t, f.GetPageLayout("SheetN"), "sheet SheetN does not exist") _, err := f.GetPageLayout("SheetN")
assert.EqualError(t, err, "sheet SheetN does not exist")
} }
func TestSetHeaderFooter(t *testing.T) { func TestSetHeaderFooter(t *testing.T) {
@ -242,20 +129,20 @@ func TestSetHeaderFooter(t *testing.T) {
// Test set header and footer on not exists worksheet. // Test set header and footer on not exists worksheet.
assert.EqualError(t, f.SetHeaderFooter("SheetN", nil), "sheet SheetN does not exist") assert.EqualError(t, f.SetHeaderFooter("SheetN", nil), "sheet SheetN does not exist")
// Test set header and footer with illegal setting. // Test set header and footer with illegal setting.
assert.EqualError(t, f.SetHeaderFooter("Sheet1", &FormatHeaderFooter{ assert.EqualError(t, f.SetHeaderFooter("Sheet1", &HeaderFooterOptions{
OddHeader: strings.Repeat("c", MaxFieldLength+1), OddHeader: strings.Repeat("c", MaxFieldLength+1),
}), newFieldLengthError("OddHeader").Error()) }), newFieldLengthError("OddHeader").Error())
assert.NoError(t, f.SetHeaderFooter("Sheet1", nil)) assert.NoError(t, f.SetHeaderFooter("Sheet1", nil))
text := strings.Repeat("一", MaxFieldLength) text := strings.Repeat("一", MaxFieldLength)
assert.NoError(t, f.SetHeaderFooter("Sheet1", &FormatHeaderFooter{ assert.NoError(t, f.SetHeaderFooter("Sheet1", &HeaderFooterOptions{
OddHeader: text, OddHeader: text,
OddFooter: text, OddFooter: text,
EvenHeader: text, EvenHeader: text,
EvenFooter: text, EvenFooter: text,
FirstHeader: text, FirstHeader: text,
})) }))
assert.NoError(t, f.SetHeaderFooter("Sheet1", &FormatHeaderFooter{ assert.NoError(t, f.SetHeaderFooter("Sheet1", &HeaderFooterOptions{
DifferentFirst: true, DifferentFirst: true,
DifferentOddEven: true, DifferentOddEven: true,
OddHeader: "&R&P", OddHeader: "&R&P",

View File

@ -11,647 +11,249 @@
package excelize package excelize
import "strings"
// SheetPrOption is an option of a view of a worksheet. See SetSheetPrOptions().
type SheetPrOption interface {
setSheetPrOption(view *xlsxSheetPr)
}
// SheetPrOptionPtr is a writable SheetPrOption. See GetSheetPrOptions().
type SheetPrOptionPtr interface {
SheetPrOption
getSheetPrOption(view *xlsxSheetPr)
}
type (
// CodeName is an option used for SheetPrOption and WorkbookPrOption
CodeName string
// EnableFormatConditionsCalculation is a SheetPrOption
EnableFormatConditionsCalculation bool
// Published is a SheetPrOption
Published bool
// FitToPage is a SheetPrOption
FitToPage bool
// TabColorIndexed is a TabColor option, within SheetPrOption
TabColorIndexed int
// TabColorRGB is a TabColor option, within SheetPrOption
TabColorRGB string
// TabColorTheme is a TabColor option, within SheetPrOption
TabColorTheme int
// TabColorTint is a TabColor option, within SheetPrOption
TabColorTint float64
// AutoPageBreaks is a SheetPrOption
AutoPageBreaks bool
// OutlineSummaryBelow is an outlinePr, within SheetPr option
OutlineSummaryBelow bool
)
// setSheetPrOption implements the SheetPrOption interface.
func (o OutlineSummaryBelow) setSheetPrOption(pr *xlsxSheetPr) {
if pr.OutlinePr == nil {
pr.OutlinePr = new(xlsxOutlinePr)
}
pr.OutlinePr.SummaryBelow = bool(o)
}
// getSheetPrOption implements the SheetPrOptionPtr interface.
func (o *OutlineSummaryBelow) getSheetPrOption(pr *xlsxSheetPr) {
// Excel default: true
if pr == nil || pr.OutlinePr == nil {
*o = true
return
}
*o = OutlineSummaryBelow(defaultTrue(&pr.OutlinePr.SummaryBelow))
}
// setSheetPrOption implements the SheetPrOption interface and specifies a
// stable name of the sheet.
func (o CodeName) setSheetPrOption(pr *xlsxSheetPr) {
pr.CodeName = string(o)
}
// getSheetPrOption implements the SheetPrOptionPtr interface and get the
// stable name of the sheet.
func (o *CodeName) getSheetPrOption(pr *xlsxSheetPr) {
if pr == nil {
*o = ""
return
}
*o = CodeName(pr.CodeName)
}
// setSheetPrOption implements the SheetPrOption interface and flag indicating
// whether the conditional formatting calculations shall be evaluated.
func (o EnableFormatConditionsCalculation) setSheetPrOption(pr *xlsxSheetPr) {
pr.EnableFormatConditionsCalculation = boolPtr(bool(o))
}
// getSheetPrOption implements the SheetPrOptionPtr interface and get the
// settings of whether the conditional formatting calculations shall be
// evaluated.
func (o *EnableFormatConditionsCalculation) getSheetPrOption(pr *xlsxSheetPr) {
if pr == nil {
*o = true
return
}
*o = EnableFormatConditionsCalculation(defaultTrue(pr.EnableFormatConditionsCalculation))
}
// setSheetPrOption implements the SheetPrOption interface and flag indicating
// whether the worksheet is published.
func (o Published) setSheetPrOption(pr *xlsxSheetPr) {
pr.Published = boolPtr(bool(o))
}
// getSheetPrOption implements the SheetPrOptionPtr interface and get the
// settings of whether the worksheet is published.
func (o *Published) getSheetPrOption(pr *xlsxSheetPr) {
if pr == nil {
*o = true
return
}
*o = Published(defaultTrue(pr.Published))
}
// setSheetPrOption implements the SheetPrOption interface.
func (o FitToPage) setSheetPrOption(pr *xlsxSheetPr) {
if pr.PageSetUpPr == nil {
if !o {
return
}
pr.PageSetUpPr = new(xlsxPageSetUpPr)
}
pr.PageSetUpPr.FitToPage = bool(o)
}
// getSheetPrOption implements the SheetPrOptionPtr interface.
func (o *FitToPage) getSheetPrOption(pr *xlsxSheetPr) {
// Excel default: false
if pr == nil || pr.PageSetUpPr == nil {
*o = false
return
}
*o = FitToPage(pr.PageSetUpPr.FitToPage)
}
// setSheetPrOption implements the SheetPrOption interface and sets the
// TabColor Indexed.
func (o TabColorIndexed) setSheetPrOption(pr *xlsxSheetPr) {
if pr.TabColor == nil {
pr.TabColor = new(xlsxTabColor)
}
pr.TabColor.Indexed = int(o)
}
// getSheetPrOption implements the SheetPrOptionPtr interface and gets the
// TabColor Indexed. Defaults to -1 if no indexed has been set.
func (o *TabColorIndexed) getSheetPrOption(pr *xlsxSheetPr) {
if pr == nil || pr.TabColor == nil {
*o = TabColorIndexed(ColorMappingTypeUnset)
return
}
*o = TabColorIndexed(pr.TabColor.Indexed)
}
// setSheetPrOption implements the SheetPrOption interface and specifies a
// stable name of the sheet.
func (o TabColorRGB) setSheetPrOption(pr *xlsxSheetPr) {
if pr.TabColor == nil {
if string(o) == "" {
return
}
pr.TabColor = new(xlsxTabColor)
}
pr.TabColor.RGB = getPaletteColor(string(o))
}
// getSheetPrOption implements the SheetPrOptionPtr interface and get the
// stable name of the sheet.
func (o *TabColorRGB) getSheetPrOption(pr *xlsxSheetPr) {
if pr == nil || pr.TabColor == nil {
*o = ""
return
}
*o = TabColorRGB(strings.TrimPrefix(pr.TabColor.RGB, "FF"))
}
// setSheetPrOption implements the SheetPrOption interface and sets the
// TabColor Theme. Warning: it does not create a clrScheme!
func (o TabColorTheme) setSheetPrOption(pr *xlsxSheetPr) {
if pr.TabColor == nil {
pr.TabColor = new(xlsxTabColor)
}
pr.TabColor.Theme = int(o)
}
// getSheetPrOption implements the SheetPrOptionPtr interface and gets the
// TabColor Theme. Defaults to -1 if no theme has been set.
func (o *TabColorTheme) getSheetPrOption(pr *xlsxSheetPr) {
if pr == nil || pr.TabColor == nil {
*o = TabColorTheme(ColorMappingTypeUnset)
return
}
*o = TabColorTheme(pr.TabColor.Theme)
}
// setSheetPrOption implements the SheetPrOption interface and sets the
// TabColor Tint.
func (o TabColorTint) setSheetPrOption(pr *xlsxSheetPr) {
if pr.TabColor == nil {
pr.TabColor = new(xlsxTabColor)
}
pr.TabColor.Tint = float64(o)
}
// getSheetPrOption implements the SheetPrOptionPtr interface and gets the
// TabColor Tint. Defaults to 0.0 if no tint has been set.
func (o *TabColorTint) getSheetPrOption(pr *xlsxSheetPr) {
if pr == nil || pr.TabColor == nil {
*o = 0.0
return
}
*o = TabColorTint(pr.TabColor.Tint)
}
// setSheetPrOption implements the SheetPrOption interface.
func (o AutoPageBreaks) setSheetPrOption(pr *xlsxSheetPr) {
if pr.PageSetUpPr == nil {
if !o {
return
}
pr.PageSetUpPr = new(xlsxPageSetUpPr)
}
pr.PageSetUpPr.AutoPageBreaks = bool(o)
}
// getSheetPrOption implements the SheetPrOptionPtr interface.
func (o *AutoPageBreaks) getSheetPrOption(pr *xlsxSheetPr) {
// Excel default: false
if pr == nil || pr.PageSetUpPr == nil {
*o = false
return
}
*o = AutoPageBreaks(pr.PageSetUpPr.AutoPageBreaks)
}
// SetSheetPrOptions provides a function to sets worksheet properties.
//
// Available options:
//
// CodeName(string)
// EnableFormatConditionsCalculation(bool)
// Published(bool)
// FitToPage(bool)
// TabColorIndexed(int)
// TabColorRGB(string)
// TabColorTheme(int)
// TabColorTint(float64)
// AutoPageBreaks(bool)
// OutlineSummaryBelow(bool)
func (f *File) SetSheetPrOptions(sheet string, opts ...SheetPrOption) error {
ws, err := f.workSheetReader(sheet)
if err != nil {
return err
}
pr := ws.SheetPr
if pr == nil {
pr = new(xlsxSheetPr)
ws.SheetPr = pr
}
for _, opt := range opts {
opt.setSheetPrOption(pr)
}
return err
}
// GetSheetPrOptions provides a function to gets worksheet properties.
//
// Available options:
//
// CodeName(string)
// EnableFormatConditionsCalculation(bool)
// Published(bool)
// FitToPage(bool)
// TabColorIndexed(int)
// TabColorRGB(string)
// TabColorTheme(int)
// TabColorTint(float64)
// AutoPageBreaks(bool)
// OutlineSummaryBelow(bool)
func (f *File) GetSheetPrOptions(sheet string, opts ...SheetPrOptionPtr) error {
ws, err := f.workSheetReader(sheet)
if err != nil {
return err
}
pr := ws.SheetPr
for _, opt := range opts {
opt.getSheetPrOption(pr)
}
return err
}
type (
// PageMarginBottom specifies the bottom margin for the page.
PageMarginBottom float64
// PageMarginFooter specifies the footer margin for the page.
PageMarginFooter float64
// PageMarginHeader specifies the header margin for the page.
PageMarginHeader float64
// PageMarginLeft specifies the left margin for the page.
PageMarginLeft float64
// PageMarginRight specifies the right margin for the page.
PageMarginRight float64
// PageMarginTop specifies the top margin for the page.
PageMarginTop float64
)
// setPageMargins provides a method to set the bottom margin for the worksheet.
func (p PageMarginBottom) setPageMargins(pm *xlsxPageMargins) {
pm.Bottom = float64(p)
}
// setPageMargins provides a method to get the bottom margin for the worksheet.
func (p *PageMarginBottom) getPageMargins(pm *xlsxPageMargins) {
// Excel default: 0.75
if pm == nil || pm.Bottom == 0 {
*p = 0.75
return
}
*p = PageMarginBottom(pm.Bottom)
}
// setPageMargins provides a method to set the footer margin for the worksheet.
func (p PageMarginFooter) setPageMargins(pm *xlsxPageMargins) {
pm.Footer = float64(p)
}
// setPageMargins provides a method to get the footer margin for the worksheet.
func (p *PageMarginFooter) getPageMargins(pm *xlsxPageMargins) {
// Excel default: 0.3
if pm == nil || pm.Footer == 0 {
*p = 0.3
return
}
*p = PageMarginFooter(pm.Footer)
}
// setPageMargins provides a method to set the header margin for the worksheet.
func (p PageMarginHeader) setPageMargins(pm *xlsxPageMargins) {
pm.Header = float64(p)
}
// setPageMargins provides a method to get the header margin for the worksheet.
func (p *PageMarginHeader) getPageMargins(pm *xlsxPageMargins) {
// Excel default: 0.3
if pm == nil || pm.Header == 0 {
*p = 0.3
return
}
*p = PageMarginHeader(pm.Header)
}
// setPageMargins provides a method to set the left margin for the worksheet.
func (p PageMarginLeft) setPageMargins(pm *xlsxPageMargins) {
pm.Left = float64(p)
}
// setPageMargins provides a method to get the left margin for the worksheet.
func (p *PageMarginLeft) getPageMargins(pm *xlsxPageMargins) {
// Excel default: 0.7
if pm == nil || pm.Left == 0 {
*p = 0.7
return
}
*p = PageMarginLeft(pm.Left)
}
// setPageMargins provides a method to set the right margin for the worksheet.
func (p PageMarginRight) setPageMargins(pm *xlsxPageMargins) {
pm.Right = float64(p)
}
// setPageMargins provides a method to get the right margin for the worksheet.
func (p *PageMarginRight) getPageMargins(pm *xlsxPageMargins) {
// Excel default: 0.7
if pm == nil || pm.Right == 0 {
*p = 0.7
return
}
*p = PageMarginRight(pm.Right)
}
// setPageMargins provides a method to set the top margin for the worksheet.
func (p PageMarginTop) setPageMargins(pm *xlsxPageMargins) {
pm.Top = float64(p)
}
// setPageMargins provides a method to get the top margin for the worksheet.
func (p *PageMarginTop) getPageMargins(pm *xlsxPageMargins) {
// Excel default: 0.75
if pm == nil || pm.Top == 0 {
*p = 0.75
return
}
*p = PageMarginTop(pm.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 margins. // SetPageMargins provides a function to set worksheet page margins.
// func (f *File) SetPageMargins(sheet string, opts *PageLayoutMarginsOptions) error {
// Available options: ws, err := f.workSheetReader(sheet)
//
// PageMarginBottom(float64)
// PageMarginFooter(float64)
// PageMarginHeader(float64)
// PageMarginLeft(float64)
// PageMarginRight(float64)
// PageMarginTop(float64)
func (f *File) SetPageMargins(sheet string, opts ...PageMarginsOptions) error {
s, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return err return err
} }
pm := s.PageMargins if opts == nil {
if pm == nil { return err
pm = new(xlsxPageMargins)
s.PageMargins = pm
} }
preparePageMargins := func(ws *xlsxWorksheet) {
for _, opt := range opts { if ws.PageMargins == nil {
opt.setPageMargins(pm) ws.PageMargins = new(xlsxPageMargins)
}
}
preparePrintOptions := func(ws *xlsxWorksheet) {
if ws.PrintOptions == nil {
ws.PrintOptions = new(xlsxPrintOptions)
}
}
if opts.Bottom != nil {
preparePageMargins(ws)
ws.PageMargins.Bottom = *opts.Bottom
}
if opts.Footer != nil {
preparePageMargins(ws)
ws.PageMargins.Footer = *opts.Footer
}
if opts.Header != nil {
preparePageMargins(ws)
ws.PageMargins.Header = *opts.Header
}
if opts.Left != nil {
preparePageMargins(ws)
ws.PageMargins.Left = *opts.Left
}
if opts.Right != nil {
preparePageMargins(ws)
ws.PageMargins.Right = *opts.Right
}
if opts.Top != nil {
preparePageMargins(ws)
ws.PageMargins.Top = *opts.Top
}
if opts.Horizontally != nil {
preparePrintOptions(ws)
ws.PrintOptions.HorizontalCentered = *opts.Horizontally
}
if opts.Vertically != nil {
preparePrintOptions(ws)
ws.PrintOptions.VerticalCentered = *opts.Vertically
} }
return err return err
} }
// GetPageMargins provides a function to get worksheet page margins. // GetPageMargins provides a function to get worksheet page margins.
// func (f *File) GetPageMargins(sheet string) (PageLayoutMarginsOptions, error) {
// Available options: opts := PageLayoutMarginsOptions{
// Bottom: float64Ptr(0.75),
// PageMarginBottom(float64) Footer: float64Ptr(0.3),
// PageMarginFooter(float64) Header: float64Ptr(0.3),
// PageMarginHeader(float64) Left: float64Ptr(0.7),
// PageMarginLeft(float64) Right: float64Ptr(0.7),
// PageMarginRight(float64) Top: float64Ptr(0.75),
// PageMarginTop(float64) }
func (f *File) GetPageMargins(sheet string, opts ...PageMarginsOptionsPtr) error { ws, err := f.workSheetReader(sheet)
s, err := f.workSheetReader(sheet) if err != nil {
return opts, err
}
if ws.PageMargins != nil {
if ws.PageMargins.Bottom != 0 {
opts.Bottom = float64Ptr(ws.PageMargins.Bottom)
}
if ws.PageMargins.Footer != 0 {
opts.Footer = float64Ptr(ws.PageMargins.Footer)
}
if ws.PageMargins.Header != 0 {
opts.Header = float64Ptr(ws.PageMargins.Header)
}
if ws.PageMargins.Left != 0 {
opts.Left = float64Ptr(ws.PageMargins.Left)
}
if ws.PageMargins.Right != 0 {
opts.Right = float64Ptr(ws.PageMargins.Right)
}
if ws.PageMargins.Top != 0 {
opts.Top = float64Ptr(ws.PageMargins.Top)
}
}
if ws.PrintOptions != nil {
opts.Horizontally = boolPtr(ws.PrintOptions.HorizontalCentered)
opts.Vertically = boolPtr(ws.PrintOptions.VerticalCentered)
}
return opts, err
}
// setSheetProps set worksheet format properties by given options.
func (ws *xlsxWorksheet) setSheetProps(opts *SheetPropsOptions) {
prepareSheetPr := func(ws *xlsxWorksheet) {
if ws.SheetPr == nil {
ws.SheetPr = new(xlsxSheetPr)
}
}
preparePageSetUpPr := func(ws *xlsxWorksheet) {
prepareSheetPr(ws)
if ws.SheetPr.PageSetUpPr == nil {
ws.SheetPr.PageSetUpPr = new(xlsxPageSetUpPr)
}
}
prepareOutlinePr := func(ws *xlsxWorksheet) {
prepareSheetPr(ws)
if ws.SheetPr.OutlinePr == nil {
ws.SheetPr.OutlinePr = new(xlsxOutlinePr)
}
}
prepareTabColor := func(ws *xlsxWorksheet) {
prepareSheetPr(ws)
if ws.SheetPr.TabColor == nil {
ws.SheetPr.TabColor = new(xlsxTabColor)
}
}
if opts.CodeName != nil {
prepareSheetPr(ws)
ws.SheetPr.CodeName = *opts.CodeName
}
if opts.EnableFormatConditionsCalculation != nil {
prepareSheetPr(ws)
ws.SheetPr.EnableFormatConditionsCalculation = opts.EnableFormatConditionsCalculation
}
if opts.Published != nil {
prepareSheetPr(ws)
ws.SheetPr.Published = opts.Published
}
if opts.AutoPageBreaks != nil {
preparePageSetUpPr(ws)
ws.SheetPr.PageSetUpPr.AutoPageBreaks = *opts.AutoPageBreaks
}
if opts.FitToPage != nil {
preparePageSetUpPr(ws)
ws.SheetPr.PageSetUpPr.FitToPage = *opts.FitToPage
}
if opts.OutlineSummaryBelow != nil {
prepareOutlinePr(ws)
ws.SheetPr.OutlinePr.SummaryBelow = *opts.OutlineSummaryBelow
}
if opts.TabColorIndexed != nil {
prepareTabColor(ws)
ws.SheetPr.TabColor.Indexed = *opts.TabColorIndexed
}
if opts.TabColorRGB != nil {
prepareTabColor(ws)
ws.SheetPr.TabColor.RGB = *opts.TabColorRGB
}
if opts.TabColorTheme != nil {
prepareTabColor(ws)
ws.SheetPr.TabColor.Theme = *opts.TabColorTheme
}
if opts.TabColorTint != nil {
prepareTabColor(ws)
ws.SheetPr.TabColor.Tint = *opts.TabColorTint
}
}
// SetSheetProps provides a function to set worksheet properties.
func (f *File) SetSheetProps(sheet string, opts *SheetPropsOptions) error {
ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return err return err
} }
pm := s.PageMargins if opts == nil {
return err
for _, opt := range opts { }
opt.getPageMargins(pm) ws.setSheetProps(opts)
if ws.SheetFormatPr == nil {
ws.SheetFormatPr = &xlsxSheetFormatPr{DefaultRowHeight: defaultRowHeight}
}
if opts.BaseColWidth != nil {
ws.SheetFormatPr.BaseColWidth = *opts.BaseColWidth
}
if opts.DefaultColWidth != nil {
ws.SheetFormatPr.DefaultColWidth = *opts.DefaultColWidth
}
if opts.DefaultRowHeight != nil {
ws.SheetFormatPr.DefaultRowHeight = *opts.DefaultRowHeight
}
if opts.CustomHeight != nil {
ws.SheetFormatPr.CustomHeight = *opts.CustomHeight
}
if opts.ZeroHeight != nil {
ws.SheetFormatPr.ZeroHeight = *opts.ZeroHeight
}
if opts.ThickTop != nil {
ws.SheetFormatPr.ThickTop = *opts.ThickTop
}
if opts.ThickBottom != nil {
ws.SheetFormatPr.ThickBottom = *opts.ThickBottom
} }
return err return err
} }
// SheetFormatPrOptions is an option of the formatting properties of a // GetSheetProps provides a function to get worksheet properties.
// worksheet. See SetSheetFormatPr(). func (f *File) GetSheetProps(sheet string) (SheetPropsOptions, error) {
type SheetFormatPrOptions interface { baseColWidth := uint8(8)
setSheetFormatPr(formatPr *xlsxSheetFormatPr) opts := SheetPropsOptions{
} EnableFormatConditionsCalculation: boolPtr(true),
Published: boolPtr(true),
// SheetFormatPrOptionsPtr is a writable SheetFormatPrOptions. See AutoPageBreaks: boolPtr(true),
// GetSheetFormatPr(). OutlineSummaryBelow: boolPtr(true),
type SheetFormatPrOptionsPtr interface { BaseColWidth: &baseColWidth,
SheetFormatPrOptions
getSheetFormatPr(formatPr *xlsxSheetFormatPr)
}
type (
// BaseColWidth specifies the number of characters of the maximum digit width
// of the normal style's font. This value does not include margin padding or
// extra padding for gridlines. It is only the number of characters.
BaseColWidth uint8
// DefaultColWidth specifies the default column width measured as the number
// of characters of the maximum digit width of the normal style's font.
DefaultColWidth float64
// DefaultRowHeight specifies the default row height measured in point size.
// Optimization so we don't have to write the height on all rows. This can be
// written out if most rows have custom height, to achieve the optimization.
DefaultRowHeight float64
// CustomHeight specifies the custom height.
CustomHeight bool
// ZeroHeight specifies if rows are hidden.
ZeroHeight bool
// ThickTop specifies if rows have a thick top border by default.
ThickTop bool
// ThickBottom specifies if rows have a thick bottom border by default.
ThickBottom bool
)
// setSheetFormatPr provides a method to set the number of characters of the
// maximum digit width of the normal style's font.
func (p BaseColWidth) setSheetFormatPr(fp *xlsxSheetFormatPr) {
fp.BaseColWidth = uint8(p)
}
// setSheetFormatPr provides a method to set the number of characters of the
// maximum digit width of the normal style's font.
func (p *BaseColWidth) getSheetFormatPr(fp *xlsxSheetFormatPr) {
if fp == nil {
*p = 0
return
} }
*p = BaseColWidth(fp.BaseColWidth) ws, err := f.workSheetReader(sheet)
}
// setSheetFormatPr provides a method to set the default column width measured
// as the number of characters of the maximum digit width of the normal
// style's font.
func (p DefaultColWidth) setSheetFormatPr(fp *xlsxSheetFormatPr) {
fp.DefaultColWidth = float64(p)
}
// getSheetFormatPr provides a method to get the default column width measured
// as the number of characters of the maximum digit width of the normal
// style's font.
func (p *DefaultColWidth) getSheetFormatPr(fp *xlsxSheetFormatPr) {
if fp == nil {
*p = 0
return
}
*p = DefaultColWidth(fp.DefaultColWidth)
}
// setSheetFormatPr provides a method to set the default row height measured
// in point size.
func (p DefaultRowHeight) setSheetFormatPr(fp *xlsxSheetFormatPr) {
fp.DefaultRowHeight = float64(p)
}
// getSheetFormatPr provides a method to get the default row height measured
// in point size.
func (p *DefaultRowHeight) getSheetFormatPr(fp *xlsxSheetFormatPr) {
if fp == nil {
*p = 15
return
}
*p = DefaultRowHeight(fp.DefaultRowHeight)
}
// setSheetFormatPr provides a method to set the custom height.
func (p CustomHeight) setSheetFormatPr(fp *xlsxSheetFormatPr) {
fp.CustomHeight = bool(p)
}
// getSheetFormatPr provides a method to get the custom height.
func (p *CustomHeight) getSheetFormatPr(fp *xlsxSheetFormatPr) {
if fp == nil {
*p = false
return
}
*p = CustomHeight(fp.CustomHeight)
}
// setSheetFormatPr provides a method to set if rows are hidden.
func (p ZeroHeight) setSheetFormatPr(fp *xlsxSheetFormatPr) {
fp.ZeroHeight = bool(p)
}
// getSheetFormatPr provides a method to get if rows are hidden.
func (p *ZeroHeight) getSheetFormatPr(fp *xlsxSheetFormatPr) {
if fp == nil {
*p = false
return
}
*p = ZeroHeight(fp.ZeroHeight)
}
// setSheetFormatPr provides a method to set if rows have a thick top border
// by default.
func (p ThickTop) setSheetFormatPr(fp *xlsxSheetFormatPr) {
fp.ThickTop = bool(p)
}
// getSheetFormatPr provides a method to get if rows have a thick top border
// by default.
func (p *ThickTop) getSheetFormatPr(fp *xlsxSheetFormatPr) {
if fp == nil {
*p = false
return
}
*p = ThickTop(fp.ThickTop)
}
// setSheetFormatPr provides a method to set if rows have a thick bottom
// border by default.
func (p ThickBottom) setSheetFormatPr(fp *xlsxSheetFormatPr) {
fp.ThickBottom = bool(p)
}
// setSheetFormatPr provides a method to set if rows have a thick bottom
// border by default.
func (p *ThickBottom) getSheetFormatPr(fp *xlsxSheetFormatPr) {
if fp == nil {
*p = false
return
}
*p = ThickBottom(fp.ThickBottom)
}
// SetSheetFormatPr provides a function to set worksheet formatting properties.
//
// Available options:
//
// BaseColWidth(uint8)
// DefaultColWidth(float64)
// DefaultRowHeight(float64)
// CustomHeight(bool)
// ZeroHeight(bool)
// ThickTop(bool)
// ThickBottom(bool)
func (f *File) SetSheetFormatPr(sheet string, opts ...SheetFormatPrOptions) error {
s, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return err return opts, err
} }
fp := s.SheetFormatPr if ws.SheetPr != nil {
if fp == nil { opts.CodeName = stringPtr(ws.SheetPr.CodeName)
fp = new(xlsxSheetFormatPr) if ws.SheetPr.EnableFormatConditionsCalculation != nil {
s.SheetFormatPr = fp opts.EnableFormatConditionsCalculation = ws.SheetPr.EnableFormatConditionsCalculation
}
if ws.SheetPr.Published != nil {
opts.Published = ws.SheetPr.Published
}
if ws.SheetPr.PageSetUpPr != nil {
opts.AutoPageBreaks = boolPtr(ws.SheetPr.PageSetUpPr.AutoPageBreaks)
opts.FitToPage = boolPtr(ws.SheetPr.PageSetUpPr.FitToPage)
}
if ws.SheetPr.OutlinePr != nil {
opts.OutlineSummaryBelow = boolPtr(ws.SheetPr.OutlinePr.SummaryBelow)
}
if ws.SheetPr.TabColor != nil {
opts.TabColorIndexed = intPtr(ws.SheetPr.TabColor.Indexed)
opts.TabColorRGB = stringPtr(ws.SheetPr.TabColor.RGB)
opts.TabColorTheme = intPtr(ws.SheetPr.TabColor.Theme)
opts.TabColorTint = float64Ptr(ws.SheetPr.TabColor.Tint)
}
} }
for _, opt := range opts { if ws.SheetFormatPr != nil {
opt.setSheetFormatPr(fp) opts.BaseColWidth = &ws.SheetFormatPr.BaseColWidth
opts.DefaultColWidth = float64Ptr(ws.SheetFormatPr.DefaultColWidth)
opts.DefaultRowHeight = float64Ptr(ws.SheetFormatPr.DefaultRowHeight)
opts.CustomHeight = boolPtr(ws.SheetFormatPr.CustomHeight)
opts.ZeroHeight = boolPtr(ws.SheetFormatPr.ZeroHeight)
opts.ThickTop = boolPtr(ws.SheetFormatPr.ThickTop)
opts.ThickBottom = boolPtr(ws.SheetFormatPr.ThickBottom)
} }
return err return opts, err
}
// GetSheetFormatPr provides a function to get worksheet formatting properties.
//
// Available options:
//
// BaseColWidth(uint8)
// DefaultColWidth(float64)
// DefaultRowHeight(float64)
// CustomHeight(bool)
// ZeroHeight(bool)
// ThickTop(bool)
// ThickBottom(bool)
func (f *File) GetSheetFormatPr(sheet string, opts ...SheetFormatPrOptionsPtr) error {
s, err := f.workSheetReader(sheet)
if err != nil {
return err
}
fp := s.SheetFormatPr
for _, opt := range opts {
opt.getSheetFormatPr(fp)
}
return err
} }

View File

@ -1,501 +1,107 @@
package excelize package excelize
import ( import (
"fmt" "path/filepath"
"testing" "testing"
"github.com/mohae/deepcopy"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
var _ = []SheetPrOption{
CodeName("hello"),
EnableFormatConditionsCalculation(false),
Published(false),
FitToPage(true),
TabColorIndexed(42),
TabColorRGB("#FFFF00"),
TabColorTheme(ColorMappingTypeLight2),
TabColorTint(0.5),
AutoPageBreaks(true),
OutlineSummaryBelow(true),
}
var _ = []SheetPrOptionPtr{
(*CodeName)(nil),
(*EnableFormatConditionsCalculation)(nil),
(*Published)(nil),
(*FitToPage)(nil),
(*TabColorIndexed)(nil),
(*TabColorRGB)(nil),
(*TabColorTheme)(nil),
(*TabColorTint)(nil),
(*AutoPageBreaks)(nil),
(*OutlineSummaryBelow)(nil),
}
func ExampleFile_SetSheetPrOptions() {
f := NewFile()
const sheet = "Sheet1"
if err := f.SetSheetPrOptions(sheet,
CodeName("code"),
EnableFormatConditionsCalculation(false),
Published(false),
FitToPage(true),
TabColorIndexed(42),
TabColorRGB("#FFFF00"),
TabColorTheme(ColorMappingTypeLight2),
TabColorTint(0.5),
AutoPageBreaks(true),
OutlineSummaryBelow(false),
); err != nil {
fmt.Println(err)
}
// Output:
}
func ExampleFile_GetSheetPrOptions() {
f := NewFile()
const sheet = "Sheet1"
var (
codeName CodeName
enableFormatConditionsCalculation EnableFormatConditionsCalculation
published Published
fitToPage FitToPage
tabColorIndexed TabColorIndexed
tabColorRGB TabColorRGB
tabColorTheme TabColorTheme
tabColorTint TabColorTint
autoPageBreaks AutoPageBreaks
outlineSummaryBelow OutlineSummaryBelow
)
if err := f.GetSheetPrOptions(sheet,
&codeName,
&enableFormatConditionsCalculation,
&published,
&fitToPage,
&tabColorIndexed,
&tabColorRGB,
&tabColorTheme,
&tabColorTint,
&autoPageBreaks,
&outlineSummaryBelow,
); err != nil {
fmt.Println(err)
}
fmt.Println("Defaults:")
fmt.Printf("- codeName: %q\n", codeName)
fmt.Println("- enableFormatConditionsCalculation:", enableFormatConditionsCalculation)
fmt.Println("- published:", published)
fmt.Println("- fitToPage:", fitToPage)
fmt.Printf("- tabColorIndexed: %d\n", tabColorIndexed)
fmt.Printf("- tabColorRGB: %q\n", tabColorRGB)
fmt.Printf("- tabColorTheme: %d\n", tabColorTheme)
fmt.Printf("- tabColorTint: %f\n", tabColorTint)
fmt.Println("- autoPageBreaks:", autoPageBreaks)
fmt.Println("- outlineSummaryBelow:", outlineSummaryBelow)
// Output:
// Defaults:
// - codeName: ""
// - enableFormatConditionsCalculation: true
// - published: true
// - fitToPage: false
// - tabColorIndexed: -1
// - tabColorRGB: ""
// - tabColorTheme: -1
// - tabColorTint: 0.000000
// - autoPageBreaks: false
// - outlineSummaryBelow: true
}
func TestSheetPrOptions(t *testing.T) {
const sheet = "Sheet1"
testData := []struct {
container SheetPrOptionPtr
nonDefault SheetPrOption
}{
{new(CodeName), CodeName("xx")},
{new(EnableFormatConditionsCalculation), EnableFormatConditionsCalculation(false)},
{new(Published), Published(false)},
{new(FitToPage), FitToPage(true)},
{new(TabColorIndexed), TabColorIndexed(42)},
{new(TabColorRGB), TabColorRGB("FFFF00")},
{new(TabColorTheme), TabColorTheme(ColorMappingTypeLight2)},
{new(TabColorTint), TabColorTint(0.5)},
{new(AutoPageBreaks), AutoPageBreaks(true)},
{new(OutlineSummaryBelow), OutlineSummaryBelow(false)},
}
for i, test := range testData {
t.Run(fmt.Sprintf("TestData%d", i), func(t *testing.T) {
opts := test.nonDefault
t.Logf("option %T", opts)
def := deepcopy.Copy(test.container).(SheetPrOptionPtr)
val1 := deepcopy.Copy(def).(SheetPrOptionPtr)
val2 := deepcopy.Copy(def).(SheetPrOptionPtr)
f := NewFile()
// Get the default value
assert.NoError(t, f.GetSheetPrOptions(sheet, def), opts)
// Get again and check
assert.NoError(t, f.GetSheetPrOptions(sheet, val1), opts)
if !assert.Equal(t, val1, def, opts) {
t.FailNow()
}
// Set the same value
assert.NoError(t, f.SetSheetPrOptions(sheet, val1), opts)
// Get again and check
assert.NoError(t, f.GetSheetPrOptions(sheet, val1), opts)
if !assert.Equal(t, val1, def, "%T: value should not have changed", opts) {
t.FailNow()
}
// Set a different value
assert.NoError(t, f.SetSheetPrOptions(sheet, test.nonDefault), opts)
assert.NoError(t, f.GetSheetPrOptions(sheet, val1), opts)
// Get again and compare
assert.NoError(t, f.GetSheetPrOptions(sheet, val2), opts)
if !assert.Equal(t, val1, val2, "%T: value should not have changed", opts) {
t.FailNow()
}
// Value should not be the same as the default
if !assert.NotEqual(t, def, val1, "%T: value should have changed from default", opts) {
t.FailNow()
}
// Restore the default value
assert.NoError(t, f.SetSheetPrOptions(sheet, def), opts)
assert.NoError(t, f.GetSheetPrOptions(sheet, val1), opts)
if !assert.Equal(t, def, val1) {
t.FailNow()
}
})
}
}
func TestSetSheetPrOptions(t *testing.T) {
f := NewFile()
assert.NoError(t, f.SetSheetPrOptions("Sheet1", TabColorRGB("")))
// Test SetSheetPrOptions on not exists worksheet.
assert.EqualError(t, f.SetSheetPrOptions("SheetN"), "sheet SheetN does not exist")
}
func TestGetSheetPrOptions(t *testing.T) {
f := NewFile()
// Test GetSheetPrOptions on not exists worksheet.
assert.EqualError(t, f.GetSheetPrOptions("SheetN"), "sheet SheetN does not exist")
}
var _ = []PageMarginsOptions{
PageMarginBottom(1.0),
PageMarginFooter(1.0),
PageMarginHeader(1.0),
PageMarginLeft(1.0),
PageMarginRight(1.0),
PageMarginTop(1.0),
}
var _ = []PageMarginsOptionsPtr{
(*PageMarginBottom)(nil),
(*PageMarginFooter)(nil),
(*PageMarginHeader)(nil),
(*PageMarginLeft)(nil),
(*PageMarginRight)(nil),
(*PageMarginTop)(nil),
}
func ExampleFile_SetPageMargins() {
f := NewFile()
const sheet = "Sheet1"
if err := f.SetPageMargins(sheet,
PageMarginBottom(1.0),
PageMarginFooter(1.0),
PageMarginHeader(1.0),
PageMarginLeft(1.0),
PageMarginRight(1.0),
PageMarginTop(1.0),
); err != nil {
fmt.Println(err)
}
// Output:
}
func ExampleFile_GetPageMargins() {
f := NewFile()
const sheet = "Sheet1"
var (
marginBottom PageMarginBottom
marginFooter PageMarginFooter
marginHeader PageMarginHeader
marginLeft PageMarginLeft
marginRight PageMarginRight
marginTop PageMarginTop
)
if err := f.GetPageMargins(sheet,
&marginBottom,
&marginFooter,
&marginHeader,
&marginLeft,
&marginRight,
&marginTop,
); err != nil {
fmt.Println(err)
}
fmt.Println("Defaults:")
fmt.Println("- marginBottom:", marginBottom)
fmt.Println("- marginFooter:", marginFooter)
fmt.Println("- marginHeader:", marginHeader)
fmt.Println("- marginLeft:", marginLeft)
fmt.Println("- marginRight:", marginRight)
fmt.Println("- marginTop:", marginTop)
// Output:
// Defaults:
// - marginBottom: 0.75
// - marginFooter: 0.3
// - marginHeader: 0.3
// - marginLeft: 0.7
// - marginRight: 0.7
// - marginTop: 0.75
}
func TestPageMarginsOption(t *testing.T) {
const sheet = "Sheet1"
testData := []struct {
container PageMarginsOptionsPtr
nonDefault PageMarginsOptions
}{
{new(PageMarginTop), PageMarginTop(1.0)},
{new(PageMarginBottom), PageMarginBottom(1.0)},
{new(PageMarginLeft), PageMarginLeft(1.0)},
{new(PageMarginRight), PageMarginRight(1.0)},
{new(PageMarginHeader), PageMarginHeader(1.0)},
{new(PageMarginFooter), PageMarginFooter(1.0)},
}
for i, test := range testData {
t.Run(fmt.Sprintf("TestData%d", i), func(t *testing.T) {
opts := test.nonDefault
t.Logf("option %T", opts)
def := deepcopy.Copy(test.container).(PageMarginsOptionsPtr)
val1 := deepcopy.Copy(def).(PageMarginsOptionsPtr)
val2 := deepcopy.Copy(def).(PageMarginsOptionsPtr)
f := NewFile()
// Get the default value
assert.NoError(t, f.GetPageMargins(sheet, def), opts)
// Get again and check
assert.NoError(t, f.GetPageMargins(sheet, val1), opts)
if !assert.Equal(t, val1, def, opts) {
t.FailNow()
}
// Set the same value
assert.NoError(t, f.SetPageMargins(sheet, val1), opts)
// Get again and check
assert.NoError(t, f.GetPageMargins(sheet, val1), opts)
if !assert.Equal(t, val1, def, "%T: value should not have changed", opts) {
t.FailNow()
}
// Set a different value
assert.NoError(t, f.SetPageMargins(sheet, test.nonDefault), opts)
assert.NoError(t, f.GetPageMargins(sheet, val1), opts)
// Get again and compare
assert.NoError(t, f.GetPageMargins(sheet, val2), opts)
if !assert.Equal(t, val1, val2, "%T: value should not have changed", opts) {
t.FailNow()
}
// Value should not be the same as the default
if !assert.NotEqual(t, def, val1, "%T: value should have changed from default", opts) {
t.FailNow()
}
// Restore the default value
assert.NoError(t, f.SetPageMargins(sheet, def), opts)
assert.NoError(t, f.GetPageMargins(sheet, val1), opts)
if !assert.Equal(t, def, val1) {
t.FailNow()
}
})
}
}
func TestSetPageMargins(t *testing.T) { func TestSetPageMargins(t *testing.T) {
f := NewFile() f := NewFile()
assert.NoError(t, f.SetPageMargins("Sheet1", nil))
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
ws.(*xlsxWorksheet).PageMargins = nil
ws.(*xlsxWorksheet).PrintOptions = nil
expected := PageLayoutMarginsOptions{
Bottom: float64Ptr(1.0),
Footer: float64Ptr(1.0),
Header: float64Ptr(1.0),
Left: float64Ptr(1.0),
Right: float64Ptr(1.0),
Top: float64Ptr(1.0),
Horizontally: boolPtr(true),
Vertically: boolPtr(true),
}
assert.NoError(t, f.SetPageMargins("Sheet1", &expected))
opts, err := f.GetPageMargins("Sheet1")
assert.NoError(t, err)
assert.Equal(t, expected, opts)
// Test set page margins on not exists worksheet. // Test set page margins on not exists worksheet.
assert.EqualError(t, f.SetPageMargins("SheetN"), "sheet SheetN does not exist") assert.EqualError(t, f.SetPageMargins("SheetN", nil), "sheet SheetN does not exist")
} }
func TestGetPageMargins(t *testing.T) { func TestGetPageMargins(t *testing.T) {
f := NewFile() f := NewFile()
// Test get page margins on not exists worksheet. // Test get page margins on not exists worksheet.
assert.EqualError(t, f.GetPageMargins("SheetN"), "sheet SheetN does not exist") _, err := f.GetPageMargins("SheetN")
assert.EqualError(t, err, "sheet SheetN does not exist")
} }
func ExampleFile_SetSheetFormatPr() { func TestDebug(t *testing.T) {
f := NewFile() f := NewFile()
const sheet = "Sheet1" assert.NoError(t, f.SetSheetProps("Sheet1", nil))
if err := f.SetSheetFormatPr(sheet,
BaseColWidth(1.0),
DefaultColWidth(1.0),
DefaultRowHeight(1.0),
CustomHeight(true),
ZeroHeight(true),
ThickTop(true),
ThickBottom(true),
); err != nil {
fmt.Println(err)
}
// Output:
}
func ExampleFile_GetSheetFormatPr() {
f := NewFile()
const sheet = "Sheet1"
var (
baseColWidth BaseColWidth
defaultColWidth DefaultColWidth
defaultRowHeight DefaultRowHeight
customHeight CustomHeight
zeroHeight ZeroHeight
thickTop ThickTop
thickBottom ThickBottom
)
if err := f.GetSheetFormatPr(sheet,
&baseColWidth,
&defaultColWidth,
&defaultRowHeight,
&customHeight,
&zeroHeight,
&thickTop,
&thickBottom,
); err != nil {
fmt.Println(err)
}
fmt.Println("Defaults:")
fmt.Println("- baseColWidth:", baseColWidth)
fmt.Println("- defaultColWidth:", defaultColWidth)
fmt.Println("- defaultRowHeight:", defaultRowHeight)
fmt.Println("- customHeight:", customHeight)
fmt.Println("- zeroHeight:", zeroHeight)
fmt.Println("- thickTop:", thickTop)
fmt.Println("- thickBottom:", thickBottom)
// Output:
// Defaults:
// - baseColWidth: 0
// - defaultColWidth: 0
// - defaultRowHeight: 15
// - customHeight: false
// - zeroHeight: false
// - thickTop: false
// - thickBottom: false
}
func TestSheetFormatPrOptions(t *testing.T) {
const sheet = "Sheet1"
testData := []struct {
container SheetFormatPrOptionsPtr
nonDefault SheetFormatPrOptions
}{
{new(BaseColWidth), BaseColWidth(1.0)},
{new(DefaultColWidth), DefaultColWidth(1.0)},
{new(DefaultRowHeight), DefaultRowHeight(1.0)},
{new(CustomHeight), CustomHeight(true)},
{new(ZeroHeight), ZeroHeight(true)},
{new(ThickTop), ThickTop(true)},
{new(ThickBottom), ThickBottom(true)},
}
for i, test := range testData {
t.Run(fmt.Sprintf("TestData%d", i), func(t *testing.T) {
opts := test.nonDefault
t.Logf("option %T", opts)
def := deepcopy.Copy(test.container).(SheetFormatPrOptionsPtr)
val1 := deepcopy.Copy(def).(SheetFormatPrOptionsPtr)
val2 := deepcopy.Copy(def).(SheetFormatPrOptionsPtr)
f := NewFile()
// Get the default value
assert.NoError(t, f.GetSheetFormatPr(sheet, def), opts)
// Get again and check
assert.NoError(t, f.GetSheetFormatPr(sheet, val1), opts)
if !assert.Equal(t, val1, def, opts) {
t.FailNow()
}
// Set the same value
assert.NoError(t, f.SetSheetFormatPr(sheet, val1), opts)
// Get again and check
assert.NoError(t, f.GetSheetFormatPr(sheet, val1), opts)
if !assert.Equal(t, val1, def, "%T: value should not have changed", opts) {
t.FailNow()
}
// Set a different value
assert.NoError(t, f.SetSheetFormatPr(sheet, test.nonDefault), opts)
assert.NoError(t, f.GetSheetFormatPr(sheet, val1), opts)
// Get again and compare
assert.NoError(t, f.GetSheetFormatPr(sheet, val2), opts)
if !assert.Equal(t, val1, val2, "%T: value should not have changed", opts) {
t.FailNow()
}
// Value should not be the same as the default
if !assert.NotEqual(t, def, val1, "%T: value should have changed from default", opts) {
t.FailNow()
}
// Restore the default value
assert.NoError(t, f.SetSheetFormatPr(sheet, def), opts)
assert.NoError(t, f.GetSheetFormatPr(sheet, val1), opts)
if !assert.Equal(t, def, val1) {
t.FailNow()
}
})
}
}
func TestSetSheetFormatPr(t *testing.T) {
f := NewFile()
assert.NoError(t, f.GetSheetFormatPr("Sheet1"))
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml") ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok) assert.True(t, ok)
ws.(*xlsxWorksheet).PageMargins = nil
ws.(*xlsxWorksheet).PrintOptions = nil
ws.(*xlsxWorksheet).SheetPr = nil
ws.(*xlsxWorksheet).SheetFormatPr = nil ws.(*xlsxWorksheet).SheetFormatPr = nil
assert.NoError(t, f.SetSheetFormatPr("Sheet1", BaseColWidth(1.0))) // w := uint8(10)
// Test set formatting properties on not exists worksheet. // f.SetSheetProps("Sheet1", &SheetPropsOptions{BaseColWidth: &w})
assert.EqualError(t, f.SetSheetFormatPr("SheetN"), "sheet SheetN does not exist") f.SetPageMargins("Sheet1", &PageLayoutMarginsOptions{Horizontally: boolPtr(true)})
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDebug.xlsx")))
} }
func TestGetSheetFormatPr(t *testing.T) { func TestSetSheetProps(t *testing.T) {
f := NewFile() f := NewFile()
assert.NoError(t, f.GetSheetFormatPr("Sheet1")) assert.NoError(t, f.SetSheetProps("Sheet1", nil))
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml") ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok) assert.True(t, ok)
ws.(*xlsxWorksheet).SheetPr = nil
ws.(*xlsxWorksheet).SheetFormatPr = nil ws.(*xlsxWorksheet).SheetFormatPr = nil
var ( baseColWidth := uint8(8)
baseColWidth BaseColWidth expected := SheetPropsOptions{
defaultColWidth DefaultColWidth CodeName: stringPtr("code"),
defaultRowHeight DefaultRowHeight EnableFormatConditionsCalculation: boolPtr(true),
customHeight CustomHeight Published: boolPtr(true),
zeroHeight ZeroHeight AutoPageBreaks: boolPtr(true),
thickTop ThickTop FitToPage: boolPtr(true),
thickBottom ThickBottom TabColorIndexed: intPtr(1),
) TabColorRGB: stringPtr("#FFFF00"),
assert.NoError(t, f.GetSheetFormatPr("Sheet1", TabColorTheme: intPtr(1),
&baseColWidth, TabColorTint: float64Ptr(1),
&defaultColWidth, OutlineSummaryBelow: boolPtr(true),
&defaultRowHeight, BaseColWidth: &baseColWidth,
&customHeight, DefaultColWidth: float64Ptr(10),
&zeroHeight, DefaultRowHeight: float64Ptr(10),
&thickTop, CustomHeight: boolPtr(true),
&thickBottom, ZeroHeight: boolPtr(true),
)) ThickTop: boolPtr(true),
// Test get formatting properties on not exists worksheet. ThickBottom: boolPtr(true),
assert.EqualError(t, f.GetSheetFormatPr("SheetN"), "sheet SheetN does not exist") }
assert.NoError(t, f.SetSheetProps("Sheet1", &expected))
opts, err := f.GetSheetProps("Sheet1")
assert.NoError(t, err)
assert.Equal(t, expected, opts)
ws.(*xlsxWorksheet).SheetPr = nil
assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{FitToPage: boolPtr(true)}))
ws.(*xlsxWorksheet).SheetPr = nil
assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{TabColorRGB: stringPtr("#FFFF00")}))
ws.(*xlsxWorksheet).SheetPr = nil
assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{TabColorTheme: intPtr(1)}))
ws.(*xlsxWorksheet).SheetPr = nil
assert.NoError(t, f.SetSheetProps("Sheet1", &SheetPropsOptions{TabColorTint: float64Ptr(1)}))
// Test SetSheetProps on not exists worksheet.
assert.EqualError(t, f.SetSheetProps("SheetN", nil), "sheet SheetN does not exist")
}
func TestGetSheetProps(t *testing.T) {
f := NewFile()
// Test GetSheetProps on not exists worksheet.
_, err := f.GetSheetProps("SheetN")
assert.EqualError(t, err, "sheet SheetN does not exist")
} }

View File

@ -13,150 +13,6 @@ package excelize
import "fmt" import "fmt"
// SheetViewOption is an option of a view of a worksheet. See
// SetSheetViewOptions().
type SheetViewOption interface {
setSheetViewOption(view *xlsxSheetView)
}
// SheetViewOptionPtr is a writable SheetViewOption. See
// GetSheetViewOptions().
type SheetViewOptionPtr interface {
SheetViewOption
getSheetViewOption(view *xlsxSheetView)
}
type (
// DefaultGridColor is a SheetViewOption. It specifies a flag indicating
// that the consuming application should use the default grid lines color
// (system dependent). Overrides any color specified in colorId.
DefaultGridColor bool
// ShowFormulas is a SheetViewOption. It specifies a flag indicating
// whether this sheet should display formulas.
ShowFormulas bool
// ShowGridLines is a SheetViewOption. It specifies a flag indicating
// whether this sheet should display gridlines.
ShowGridLines bool
// ShowRowColHeaders is a SheetViewOption. It specifies a flag indicating
// whether the sheet should display row and column headings.
ShowRowColHeaders bool
// ShowZeros is a SheetViewOption. It specifies a flag indicating whether
// to "show a zero in cells that have zero value". When using a formula to
// reference another cell which is empty, the referenced value becomes 0
// when the flag is true. (Default setting is true.)
ShowZeros bool
// RightToLeft is a SheetViewOption. It specifies a flag indicating whether
// the sheet is in 'right to left' display mode. When in this mode, Column
// A is on the far right, Column B ;is one column left of Column A, and so
// on. Also, information in cells is displayed in the Right to Left format.
RightToLeft bool
// ShowRuler is a SheetViewOption. It specifies a flag indicating this
// sheet should display ruler.
ShowRuler bool
// View is a SheetViewOption. It specifies a flag indicating how sheet is
// displayed, by default it uses empty string available options: normal,
// pageLayout, pageBreakPreview
View string
// TopLeftCell is a SheetViewOption. It specifies a location of the top
// left visible cell Location of the top left visible cell in the bottom
// right pane (when in Left-to-Right mode).
TopLeftCell string
// ZoomScale is a SheetViewOption. It specifies a window zoom magnification
// for current view representing percent values. This attribute is
// restricted to values ranging from 10 to 400. Horizontal & Vertical
// scale together.
ZoomScale float64
)
// Defaults for each option are described in XML schema for CT_SheetView
func (o DefaultGridColor) setSheetViewOption(view *xlsxSheetView) {
view.DefaultGridColor = boolPtr(bool(o))
}
func (o *DefaultGridColor) getSheetViewOption(view *xlsxSheetView) {
*o = DefaultGridColor(defaultTrue(view.DefaultGridColor)) // Excel default: true
}
func (o ShowFormulas) setSheetViewOption(view *xlsxSheetView) {
view.ShowFormulas = bool(o) // Excel default: false
}
func (o *ShowFormulas) getSheetViewOption(view *xlsxSheetView) {
*o = ShowFormulas(view.ShowFormulas) // Excel default: false
}
func (o ShowGridLines) setSheetViewOption(view *xlsxSheetView) {
view.ShowGridLines = boolPtr(bool(o))
}
func (o *ShowGridLines) getSheetViewOption(view *xlsxSheetView) {
*o = ShowGridLines(defaultTrue(view.ShowGridLines)) // Excel default: true
}
func (o ShowRowColHeaders) setSheetViewOption(view *xlsxSheetView) {
view.ShowRowColHeaders = boolPtr(bool(o))
}
func (o *ShowRowColHeaders) getSheetViewOption(view *xlsxSheetView) {
*o = ShowRowColHeaders(defaultTrue(view.ShowRowColHeaders)) // Excel default: true
}
func (o ShowZeros) setSheetViewOption(view *xlsxSheetView) {
view.ShowZeros = boolPtr(bool(o))
}
func (o *ShowZeros) getSheetViewOption(view *xlsxSheetView) {
*o = ShowZeros(defaultTrue(view.ShowZeros)) // Excel default: true
}
func (o RightToLeft) setSheetViewOption(view *xlsxSheetView) {
view.RightToLeft = bool(o) // Excel default: false
}
func (o *RightToLeft) getSheetViewOption(view *xlsxSheetView) {
*o = RightToLeft(view.RightToLeft)
}
func (o ShowRuler) setSheetViewOption(view *xlsxSheetView) {
view.ShowRuler = boolPtr(bool(o))
}
func (o *ShowRuler) getSheetViewOption(view *xlsxSheetView) {
*o = ShowRuler(defaultTrue(view.ShowRuler)) // Excel default: true
}
func (o View) setSheetViewOption(view *xlsxSheetView) {
view.View = string(o)
}
func (o *View) getSheetViewOption(view *xlsxSheetView) {
if view.View != "" {
*o = View(view.View)
return
}
*o = "normal"
}
func (o TopLeftCell) setSheetViewOption(view *xlsxSheetView) {
view.TopLeftCell = string(o)
}
func (o *TopLeftCell) getSheetViewOption(view *xlsxSheetView) {
*o = TopLeftCell(view.TopLeftCell)
}
func (o ZoomScale) setSheetViewOption(view *xlsxSheetView) {
// This attribute is restricted to values ranging from 10 to 400.
if float64(o) >= 10 && float64(o) <= 400 {
view.ZoomScale = float64(o)
}
}
func (o *ZoomScale) getSheetViewOption(view *xlsxSheetView) {
*o = ZoomScale(view.ZoomScale)
}
// getSheetView returns the SheetView object // getSheetView returns the SheetView object
func (f *File) getSheetView(sheet string, viewIndex int) (*xlsxSheetView, error) { func (f *File) getSheetView(sheet string, viewIndex int) (*xlsxSheetView, error) {
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
@ -180,65 +36,100 @@ func (f *File) getSheetView(sheet string, viewIndex int) (*xlsxSheetView, error)
return &(ws.SheetViews.SheetView[viewIndex]), err return &(ws.SheetViews.SheetView[viewIndex]), err
} }
// SetSheetViewOptions sets sheet view options. The viewIndex may be negative // setSheetView set sheet view by given options.
// and if so is counted backward (-1 is the last view). func (view *xlsxSheetView) setSheetView(opts *ViewOptions) {
// if opts.DefaultGridColor != nil {
// Available options: view.DefaultGridColor = opts.DefaultGridColor
// }
// DefaultGridColor(bool) if opts.RightToLeft != nil {
// ShowFormulas(bool) view.RightToLeft = *opts.RightToLeft
// ShowGridLines(bool) }
// ShowRowColHeaders(bool) if opts.ShowFormulas != nil {
// ShowZeros(bool) view.ShowFormulas = *opts.ShowFormulas
// RightToLeft(bool) }
// ShowRuler(bool) if opts.ShowGridLines != nil {
// View(string) view.ShowGridLines = opts.ShowGridLines
// TopLeftCell(string) }
// ZoomScale(float64) if opts.ShowRowColHeaders != nil {
// view.ShowRowColHeaders = opts.ShowRowColHeaders
// Example: }
// if opts.ShowRuler != nil {
// err = f.SetSheetViewOptions("Sheet1", -1, ShowGridLines(false)) view.ShowRuler = opts.ShowRuler
func (f *File) SetSheetViewOptions(sheet string, viewIndex int, opts ...SheetViewOption) error { }
if opts.ShowZeros != nil {
view.ShowZeros = opts.ShowZeros
}
if opts.TopLeftCell != nil {
view.TopLeftCell = *opts.TopLeftCell
}
if opts.View != nil {
if _, ok := map[string]interface{}{
"normal": nil,
"pageLayout": nil,
"pageBreakPreview": nil,
}[*opts.View]; ok {
view.View = *opts.View
}
}
if opts.ZoomScale != nil && *opts.ZoomScale >= 10 && *opts.ZoomScale <= 400 {
view.ZoomScale = *opts.ZoomScale
}
}
// SetSheetView sets sheet view options. The viewIndex may be negative and if
// so is counted backward (-1 is the last view).
func (f *File) SetSheetView(sheet string, viewIndex int, opts *ViewOptions) error {
view, err := f.getSheetView(sheet, viewIndex) view, err := f.getSheetView(sheet, viewIndex)
if err != nil { if err != nil {
return err return err
} }
if opts == nil {
for _, opt := range opts {
opt.setSheetViewOption(view)
}
return nil
}
// GetSheetViewOptions gets the value of sheet view options. The viewIndex may
// be negative and if so is counted backward (-1 is the last view).
//
// Available options:
//
// DefaultGridColor(bool)
// ShowFormulas(bool)
// ShowGridLines(bool)
// ShowRowColHeaders(bool)
// ShowZeros(bool)
// RightToLeft(bool)
// ShowRuler(bool)
// View(string)
// TopLeftCell(string)
// ZoomScale(float64)
//
// Example:
//
// var showGridLines excelize.ShowGridLines
// err = f.GetSheetViewOptions("Sheet1", -1, &showGridLines)
func (f *File) GetSheetViewOptions(sheet string, viewIndex int, opts ...SheetViewOptionPtr) error {
view, err := f.getSheetView(sheet, viewIndex)
if err != nil {
return err return err
} }
view.setSheetView(opts)
for _, opt := range opts {
opt.getSheetViewOption(view)
}
return nil return nil
} }
// GetSheetView gets the value of sheet view options. The viewIndex may be
// negative and if so is counted backward (-1 is the last view).
func (f *File) GetSheetView(sheet string, viewIndex int) (ViewOptions, error) {
opts := ViewOptions{
DefaultGridColor: boolPtr(true),
ShowFormulas: boolPtr(true),
ShowGridLines: boolPtr(true),
ShowRowColHeaders: boolPtr(true),
ShowRuler: boolPtr(true),
ShowZeros: boolPtr(true),
View: stringPtr("normal"),
ZoomScale: float64Ptr(100),
}
view, err := f.getSheetView(sheet, viewIndex)
if err != nil {
return opts, err
}
if view.DefaultGridColor != nil {
opts.DefaultGridColor = view.DefaultGridColor
}
opts.RightToLeft = boolPtr(view.RightToLeft)
opts.ShowFormulas = boolPtr(view.ShowFormulas)
if view.ShowGridLines != nil {
opts.ShowGridLines = view.ShowGridLines
}
if view.ShowRowColHeaders != nil {
opts.ShowRowColHeaders = view.ShowRowColHeaders
}
if view.ShowRuler != nil {
opts.ShowRuler = view.ShowRuler
}
if view.ShowZeros != nil {
opts.ShowZeros = view.ShowZeros
}
opts.TopLeftCell = stringPtr(view.TopLeftCell)
if view.View != "" {
opts.View = stringPtr(view.View)
}
if view.ZoomScale >= 10 && view.ZoomScale <= 400 {
opts.ZoomScale = float64Ptr(view.ZoomScale)
}
return opts, err
}

View File

@ -1,218 +1,50 @@
package excelize package excelize
import ( import (
"fmt"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
var _ = []SheetViewOption{ func TestSetView(t *testing.T) {
DefaultGridColor(true),
ShowFormulas(false),
ShowGridLines(true),
ShowRowColHeaders(true),
ShowZeros(true),
RightToLeft(false),
ShowRuler(false),
View("pageLayout"),
TopLeftCell("B2"),
ZoomScale(100),
// SheetViewOptionPtr are also SheetViewOption
new(DefaultGridColor),
new(ShowFormulas),
new(ShowGridLines),
new(ShowRowColHeaders),
new(ShowZeros),
new(RightToLeft),
new(ShowRuler),
new(View),
new(TopLeftCell),
new(ZoomScale),
}
var _ = []SheetViewOptionPtr{
(*DefaultGridColor)(nil),
(*ShowFormulas)(nil),
(*ShowGridLines)(nil),
(*ShowRowColHeaders)(nil),
(*ShowZeros)(nil),
(*RightToLeft)(nil),
(*ShowRuler)(nil),
(*View)(nil),
(*TopLeftCell)(nil),
(*ZoomScale)(nil),
}
func ExampleFile_SetSheetViewOptions() {
f := NewFile() f := NewFile()
const sheet = "Sheet1" assert.NoError(t, f.SetSheetView("Sheet1", -1, nil))
if err := f.SetSheetViewOptions(sheet, 0,
DefaultGridColor(false),
ShowFormulas(true),
ShowGridLines(true),
ShowRowColHeaders(true),
RightToLeft(false),
ShowRuler(false),
View("pageLayout"),
TopLeftCell("C3"),
ZoomScale(80),
); err != nil {
fmt.Println(err)
}
var zoomScale ZoomScale
fmt.Println("Default:")
fmt.Println("- zoomScale: 80")
if err := f.SetSheetViewOptions(sheet, 0, ZoomScale(500)); err != nil {
fmt.Println(err)
}
if err := f.GetSheetViewOptions(sheet, 0, &zoomScale); err != nil {
fmt.Println(err)
}
fmt.Println("Used out of range value:")
fmt.Println("- zoomScale:", zoomScale)
if err := f.SetSheetViewOptions(sheet, 0, ZoomScale(123)); err != nil {
fmt.Println(err)
}
if err := f.GetSheetViewOptions(sheet, 0, &zoomScale); err != nil {
fmt.Println(err)
}
fmt.Println("Used correct value:")
fmt.Println("- zoomScale:", zoomScale)
// Output:
// Default:
// - zoomScale: 80
// Used out of range value:
// - zoomScale: 80
// Used correct value:
// - zoomScale: 123
}
func ExampleFile_GetSheetViewOptions() {
f := NewFile()
const sheet = "Sheet1"
var (
defaultGridColor DefaultGridColor
showFormulas ShowFormulas
showGridLines ShowGridLines
showRowColHeaders ShowRowColHeaders
showZeros ShowZeros
rightToLeft RightToLeft
showRuler ShowRuler
view View
topLeftCell TopLeftCell
zoomScale ZoomScale
)
if err := f.GetSheetViewOptions(sheet, 0,
&defaultGridColor,
&showFormulas,
&showGridLines,
&showRowColHeaders,
&showZeros,
&rightToLeft,
&showRuler,
&view,
&topLeftCell,
&zoomScale,
); err != nil {
fmt.Println(err)
}
fmt.Println("Default:")
fmt.Println("- defaultGridColor:", defaultGridColor)
fmt.Println("- showFormulas:", showFormulas)
fmt.Println("- showGridLines:", showGridLines)
fmt.Println("- showRowColHeaders:", showRowColHeaders)
fmt.Println("- showZeros:", showZeros)
fmt.Println("- rightToLeft:", rightToLeft)
fmt.Println("- showRuler:", showRuler)
fmt.Println("- view:", view)
fmt.Println("- topLeftCell:", `"`+topLeftCell+`"`)
fmt.Println("- zoomScale:", zoomScale)
if err := f.SetSheetViewOptions(sheet, 0, ShowGridLines(false)); err != nil {
fmt.Println(err)
}
if err := f.GetSheetViewOptions(sheet, 0, &showGridLines); err != nil {
fmt.Println(err)
}
if err := f.SetSheetViewOptions(sheet, 0, ShowZeros(false)); err != nil {
fmt.Println(err)
}
if err := f.GetSheetViewOptions(sheet, 0, &showZeros); err != nil {
fmt.Println(err)
}
if err := f.SetSheetViewOptions(sheet, 0, View("pageLayout")); err != nil {
fmt.Println(err)
}
if err := f.GetSheetViewOptions(sheet, 0, &view); err != nil {
fmt.Println(err)
}
if err := f.SetSheetViewOptions(sheet, 0, TopLeftCell("B2")); err != nil {
fmt.Println(err)
}
if err := f.GetSheetViewOptions(sheet, 0, &topLeftCell); err != nil {
fmt.Println(err)
}
fmt.Println("After change:")
fmt.Println("- showGridLines:", showGridLines)
fmt.Println("- showZeros:", showZeros)
fmt.Println("- view:", view)
fmt.Println("- topLeftCell:", topLeftCell)
// Output:
// Default:
// - defaultGridColor: true
// - showFormulas: false
// - showGridLines: true
// - showRowColHeaders: true
// - showZeros: true
// - rightToLeft: false
// - showRuler: true
// - view: normal
// - topLeftCell: ""
// - zoomScale: 0
// After change:
// - showGridLines: false
// - showZeros: false
// - view: pageLayout
// - topLeftCell: B2
}
func TestSheetViewOptionsErrors(t *testing.T) {
f := NewFile()
const sheet = "Sheet1"
assert.NoError(t, f.GetSheetViewOptions(sheet, 0))
assert.NoError(t, f.GetSheetViewOptions(sheet, -1))
assert.Error(t, f.GetSheetViewOptions(sheet, 1))
assert.Error(t, f.GetSheetViewOptions(sheet, -2))
assert.NoError(t, f.SetSheetViewOptions(sheet, 0))
assert.NoError(t, f.SetSheetViewOptions(sheet, -1))
assert.Error(t, f.SetSheetViewOptions(sheet, 1))
assert.Error(t, f.SetSheetViewOptions(sheet, -2))
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml") ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok) assert.True(t, ok)
ws.(*xlsxWorksheet).SheetViews = nil ws.(*xlsxWorksheet).SheetViews = nil
assert.NoError(t, f.GetSheetViewOptions(sheet, 0)) expected := ViewOptions{
DefaultGridColor: boolPtr(false),
RightToLeft: boolPtr(false),
ShowFormulas: boolPtr(false),
ShowGridLines: boolPtr(false),
ShowRowColHeaders: boolPtr(false),
ShowRuler: boolPtr(false),
ShowZeros: boolPtr(false),
TopLeftCell: stringPtr("A1"),
View: stringPtr("normal"),
ZoomScale: float64Ptr(120),
}
assert.NoError(t, f.SetSheetView("Sheet1", 0, &expected))
opts, err := f.GetSheetView("Sheet1", 0)
assert.NoError(t, err)
assert.Equal(t, expected, opts)
// Test set sheet view options with invalid view index.
assert.EqualError(t, f.SetSheetView("Sheet1", 1, nil), "view index 1 out of range")
assert.EqualError(t, f.SetSheetView("Sheet1", -2, nil), "view index -2 out of range")
// Test set sheet view options on not exists worksheet.
assert.EqualError(t, f.SetSheetView("SheetN", 0, nil), "sheet SheetN does not exist")
}
func TestGetView(t *testing.T) {
f := NewFile()
_, err := f.getSheetView("SheetN", 0)
assert.EqualError(t, err, "sheet SheetN does not exist")
// Test get sheet view options with invalid view index.
_, err = f.GetSheetView("Sheet1", 1)
assert.EqualError(t, err, "view index 1 out of range")
_, err = f.GetSheetView("Sheet1", -2)
assert.EqualError(t, err, "view index -2 out of range")
// Test get sheet view options on not exists worksheet.
_, err = f.GetSheetView("SheetN", 0)
assert.EqualError(t, err, "sheet SheetN does not exist")
} }

View File

@ -387,8 +387,9 @@ func (f *File) addSparklineGroupByStyle(ID int) *xlsxX14SparklineGroup {
// Markers | Toggle sparkline markers // Markers | Toggle sparkline markers
// ColorAxis | An RGB Color is specified as RRGGBB // ColorAxis | An RGB Color is specified as RRGGBB
// Axis | Show sparkline axis // Axis | Show sparkline axis
func (f *File) AddSparkline(sheet string, opts *SparklineOption) (err error) { func (f *File) AddSparkline(sheet string, opts *SparklineOptions) error {
var ( var (
err error
ws *xlsxWorksheet ws *xlsxWorksheet
sparkType string sparkType string
sparkTypes map[string]string sparkTypes map[string]string
@ -401,7 +402,7 @@ func (f *File) AddSparkline(sheet string, opts *SparklineOption) (err error) {
// parameter validation // parameter validation
if ws, err = f.parseFormatAddSparklineSet(sheet, opts); err != nil { if ws, err = f.parseFormatAddSparklineSet(sheet, opts); err != nil {
return return err
} }
// Handle the sparkline type // Handle the sparkline type
sparkType = "line" sparkType = "line"
@ -409,7 +410,7 @@ func (f *File) AddSparkline(sheet string, opts *SparklineOption) (err error) {
if opts.Type != "" { if opts.Type != "" {
if specifiedSparkTypes, ok = sparkTypes[opts.Type]; !ok { if specifiedSparkTypes, ok = sparkTypes[opts.Type]; !ok {
err = ErrSparklineType err = ErrSparklineType
return return err
} }
sparkType = specifiedSparkTypes sparkType = specifiedSparkTypes
} }
@ -435,7 +436,7 @@ func (f *File) AddSparkline(sheet string, opts *SparklineOption) (err error) {
f.addSparkline(opts, group) f.addSparkline(opts, group)
if ws.ExtLst.Ext != "" { // append mode ext if ws.ExtLst.Ext != "" { // append mode ext
if err = f.appendSparkline(ws, group, groups); err != nil { if err = f.appendSparkline(ws, group, groups); err != nil {
return return err
} }
} else { } else {
groups = &xlsxX14SparklineGroups{ groups = &xlsxX14SparklineGroups{
@ -443,23 +444,23 @@ func (f *File) AddSparkline(sheet string, opts *SparklineOption) (err error) {
SparklineGroups: []*xlsxX14SparklineGroup{group}, SparklineGroups: []*xlsxX14SparklineGroup{group},
} }
if sparklineGroupsBytes, err = xml.Marshal(groups); err != nil { if sparklineGroupsBytes, err = xml.Marshal(groups); err != nil {
return return err
} }
if extBytes, err = xml.Marshal(&xlsxWorksheetExt{ if extBytes, err = xml.Marshal(&xlsxWorksheetExt{
URI: ExtURISparklineGroups, URI: ExtURISparklineGroups,
Content: string(sparklineGroupsBytes), Content: string(sparklineGroupsBytes),
}); err != nil { }); err != nil {
return return err
} }
ws.ExtLst.Ext = string(extBytes) ws.ExtLst.Ext = string(extBytes)
} }
f.addSheetNameSpace(sheet, NameSpaceSpreadSheetX14) f.addSheetNameSpace(sheet, NameSpaceSpreadSheetX14)
return return err
} }
// parseFormatAddSparklineSet provides a function to validate sparkline // parseFormatAddSparklineSet provides a function to validate sparkline
// properties. // properties.
func (f *File) parseFormatAddSparklineSet(sheet string, opts *SparklineOption) (*xlsxWorksheet, error) { func (f *File) parseFormatAddSparklineSet(sheet string, opts *SparklineOptions) (*xlsxWorksheet, error) {
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return ws, err return ws, err
@ -488,7 +489,7 @@ func (f *File) parseFormatAddSparklineSet(sheet string, opts *SparklineOption) (
// addSparkline provides a function to create a sparkline in a sparkline group // addSparkline provides a function to create a sparkline in a sparkline group
// by given properties. // by given properties.
func (f *File) addSparkline(opts *SparklineOption, group *xlsxX14SparklineGroup) { func (f *File) addSparkline(opts *SparklineOptions, group *xlsxX14SparklineGroup) {
for idx, location := range opts.Location { for idx, location := range opts.Location {
group.Sparklines.Sparkline = append(group.Sparklines.Sparkline, &xlsxX14Sparkline{ group.Sparklines.Sparkline = append(group.Sparklines.Sparkline, &xlsxX14Sparkline{
F: opts.Range[idx], F: opts.Range[idx],
@ -499,8 +500,9 @@ func (f *File) addSparkline(opts *SparklineOption, group *xlsxX14SparklineGroup)
// appendSparkline provides a function to append sparkline to sparkline // appendSparkline provides a function to append sparkline to sparkline
// groups. // groups.
func (f *File) appendSparkline(ws *xlsxWorksheet, group *xlsxX14SparklineGroup, groups *xlsxX14SparklineGroups) (err error) { func (f *File) appendSparkline(ws *xlsxWorksheet, group *xlsxX14SparklineGroup, groups *xlsxX14SparklineGroups) error {
var ( var (
err error
idx int idx int
decodeExtLst *decodeWorksheetExt decodeExtLst *decodeWorksheetExt
decodeSparklineGroups *decodeX14SparklineGroups decodeSparklineGroups *decodeX14SparklineGroups
@ -510,17 +512,17 @@ func (f *File) appendSparkline(ws *xlsxWorksheet, group *xlsxX14SparklineGroup,
decodeExtLst = new(decodeWorksheetExt) decodeExtLst = new(decodeWorksheetExt)
if err = f.xmlNewDecoder(strings.NewReader("<extLst>" + ws.ExtLst.Ext + "</extLst>")). if err = f.xmlNewDecoder(strings.NewReader("<extLst>" + ws.ExtLst.Ext + "</extLst>")).
Decode(decodeExtLst); err != nil && err != io.EOF { Decode(decodeExtLst); err != nil && err != io.EOF {
return return err
} }
for idx, ext = range decodeExtLst.Ext { for idx, ext = range decodeExtLst.Ext {
if ext.URI == ExtURISparklineGroups { if ext.URI == ExtURISparklineGroups {
decodeSparklineGroups = new(decodeX14SparklineGroups) decodeSparklineGroups = new(decodeX14SparklineGroups)
if err = f.xmlNewDecoder(strings.NewReader(ext.Content)). if err = f.xmlNewDecoder(strings.NewReader(ext.Content)).
Decode(decodeSparklineGroups); err != nil && err != io.EOF { Decode(decodeSparklineGroups); err != nil && err != io.EOF {
return return err
} }
if sparklineGroupBytes, err = xml.Marshal(group); err != nil { if sparklineGroupBytes, err = xml.Marshal(group); err != nil {
return return err
} }
if groups == nil { if groups == nil {
groups = &xlsxX14SparklineGroups{} groups = &xlsxX14SparklineGroups{}
@ -528,16 +530,16 @@ func (f *File) appendSparkline(ws *xlsxWorksheet, group *xlsxX14SparklineGroup,
groups.XMLNSXM = NameSpaceSpreadSheetExcel2006Main.Value groups.XMLNSXM = NameSpaceSpreadSheetExcel2006Main.Value
groups.Content = decodeSparklineGroups.Content + string(sparklineGroupBytes) groups.Content = decodeSparklineGroups.Content + string(sparklineGroupBytes)
if sparklineGroupsBytes, err = xml.Marshal(groups); err != nil { if sparklineGroupsBytes, err = xml.Marshal(groups); err != nil {
return return err
} }
decodeExtLst.Ext[idx].Content = string(sparklineGroupsBytes) decodeExtLst.Ext[idx].Content = string(sparklineGroupsBytes)
} }
} }
if extLstBytes, err = xml.Marshal(decodeExtLst); err != nil { if extLstBytes, err = xml.Marshal(decodeExtLst); err != nil {
return return err
} }
ws.ExtLst = &xlsxExtLst{ ws.ExtLst = &xlsxExtLst{
Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), "<extLst>"), "</extLst>"), Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), "<extLst>"), "</extLst>"),
} }
return return err
} }

View File

@ -15,7 +15,10 @@ func TestAddSparkline(t *testing.T) {
style, err := f.NewStyle(`{"font":{"bold":true}}`) style, err := f.NewStyle(`{"font":{"bold":true}}`)
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "B1", style)) assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "B1", style))
assert.NoError(t, f.SetSheetViewOptions("Sheet1", 0, ZoomScale(150))) viewOpts, err := f.GetSheetView("Sheet1", 0)
assert.NoError(t, err)
viewOpts.ZoomScale = float64Ptr(150)
assert.NoError(t, f.SetSheetView("Sheet1", 0, &viewOpts))
assert.NoError(t, f.SetColWidth("Sheet1", "A", "A", 14)) assert.NoError(t, f.SetColWidth("Sheet1", "A", "A", 14))
assert.NoError(t, f.SetColWidth("Sheet1", "B", "B", 50)) assert.NoError(t, f.SetColWidth("Sheet1", "B", "B", 50))
@ -24,34 +27,34 @@ func TestAddSparkline(t *testing.T) {
assert.NoError(t, f.SetCellValue("Sheet1", "B1", "Description")) assert.NoError(t, f.SetCellValue("Sheet1", "B1", "Description"))
assert.NoError(t, f.SetCellValue("Sheet1", "B2", `A default "line" sparkline.`)) assert.NoError(t, f.SetCellValue("Sheet1", "B2", `A default "line" sparkline.`))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A2"}, Location: []string{"A2"},
Range: []string{"Sheet3!A1:J1"}, Range: []string{"Sheet3!A1:J1"},
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B3", `A default "column" sparkline.`)) assert.NoError(t, f.SetCellValue("Sheet1", "B3", `A default "column" sparkline.`))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A3"}, Location: []string{"A3"},
Range: []string{"Sheet3!A2:J2"}, Range: []string{"Sheet3!A2:J2"},
Type: "column", Type: "column",
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B4", `A default "win/loss" sparkline.`)) assert.NoError(t, f.SetCellValue("Sheet1", "B4", `A default "win/loss" sparkline.`))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A4"}, Location: []string{"A4"},
Range: []string{"Sheet3!A3:J3"}, Range: []string{"Sheet3!A3:J3"},
Type: "win_loss", Type: "win_loss",
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B6", "Line with markers.")) assert.NoError(t, f.SetCellValue("Sheet1", "B6", "Line with markers."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A6"}, Location: []string{"A6"},
Range: []string{"Sheet3!A1:J1"}, Range: []string{"Sheet3!A1:J1"},
Markers: true, Markers: true,
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B7", "Line with high and low points.")) assert.NoError(t, f.SetCellValue("Sheet1", "B7", "Line with high and low points."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A7"}, Location: []string{"A7"},
Range: []string{"Sheet3!A1:J1"}, Range: []string{"Sheet3!A1:J1"},
High: true, High: true,
@ -59,7 +62,7 @@ func TestAddSparkline(t *testing.T) {
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B8", "Line with first and last point markers.")) assert.NoError(t, f.SetCellValue("Sheet1", "B8", "Line with first and last point markers."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A8"}, Location: []string{"A8"},
Range: []string{"Sheet3!A1:J1"}, Range: []string{"Sheet3!A1:J1"},
First: true, First: true,
@ -67,28 +70,28 @@ func TestAddSparkline(t *testing.T) {
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B9", "Line with negative point markers.")) assert.NoError(t, f.SetCellValue("Sheet1", "B9", "Line with negative point markers."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A9"}, Location: []string{"A9"},
Range: []string{"Sheet3!A1:J1"}, Range: []string{"Sheet3!A1:J1"},
Negative: true, Negative: true,
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B10", "Line with axis.")) assert.NoError(t, f.SetCellValue("Sheet1", "B10", "Line with axis."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A10"}, Location: []string{"A10"},
Range: []string{"Sheet3!A1:J1"}, Range: []string{"Sheet3!A1:J1"},
Axis: true, Axis: true,
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B12", "Column with default style (1).")) assert.NoError(t, f.SetCellValue("Sheet1", "B12", "Column with default style (1)."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A12"}, Location: []string{"A12"},
Range: []string{"Sheet3!A2:J2"}, Range: []string{"Sheet3!A2:J2"},
Type: "column", Type: "column",
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B13", "Column with style 2.")) assert.NoError(t, f.SetCellValue("Sheet1", "B13", "Column with style 2."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A13"}, Location: []string{"A13"},
Range: []string{"Sheet3!A2:J2"}, Range: []string{"Sheet3!A2:J2"},
Type: "column", Type: "column",
@ -96,7 +99,7 @@ func TestAddSparkline(t *testing.T) {
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B14", "Column with style 3.")) assert.NoError(t, f.SetCellValue("Sheet1", "B14", "Column with style 3."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A14"}, Location: []string{"A14"},
Range: []string{"Sheet3!A2:J2"}, Range: []string{"Sheet3!A2:J2"},
Type: "column", Type: "column",
@ -104,7 +107,7 @@ func TestAddSparkline(t *testing.T) {
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B15", "Column with style 4.")) assert.NoError(t, f.SetCellValue("Sheet1", "B15", "Column with style 4."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A15"}, Location: []string{"A15"},
Range: []string{"Sheet3!A2:J2"}, Range: []string{"Sheet3!A2:J2"},
Type: "column", Type: "column",
@ -112,7 +115,7 @@ func TestAddSparkline(t *testing.T) {
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B16", "Column with style 5.")) assert.NoError(t, f.SetCellValue("Sheet1", "B16", "Column with style 5."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A16"}, Location: []string{"A16"},
Range: []string{"Sheet3!A2:J2"}, Range: []string{"Sheet3!A2:J2"},
Type: "column", Type: "column",
@ -120,7 +123,7 @@ func TestAddSparkline(t *testing.T) {
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B17", "Column with style 6.")) assert.NoError(t, f.SetCellValue("Sheet1", "B17", "Column with style 6."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A17"}, Location: []string{"A17"},
Range: []string{"Sheet3!A2:J2"}, Range: []string{"Sheet3!A2:J2"},
Type: "column", Type: "column",
@ -128,7 +131,7 @@ func TestAddSparkline(t *testing.T) {
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B18", "Column with a user defined color.")) assert.NoError(t, f.SetCellValue("Sheet1", "B18", "Column with a user defined color."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A18"}, Location: []string{"A18"},
Range: []string{"Sheet3!A2:J2"}, Range: []string{"Sheet3!A2:J2"},
Type: "column", Type: "column",
@ -136,14 +139,14 @@ func TestAddSparkline(t *testing.T) {
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B20", "A win/loss sparkline.")) assert.NoError(t, f.SetCellValue("Sheet1", "B20", "A win/loss sparkline."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A20"}, Location: []string{"A20"},
Range: []string{"Sheet3!A3:J3"}, Range: []string{"Sheet3!A3:J3"},
Type: "win_loss", Type: "win_loss",
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B21", "A win/loss sparkline with negative points highlighted.")) assert.NoError(t, f.SetCellValue("Sheet1", "B21", "A win/loss sparkline with negative points highlighted."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A21"}, Location: []string{"A21"},
Range: []string{"Sheet3!A3:J3"}, Range: []string{"Sheet3!A3:J3"},
Type: "win_loss", Type: "win_loss",
@ -151,7 +154,7 @@ func TestAddSparkline(t *testing.T) {
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B23", "A left to right column (the default).")) assert.NoError(t, f.SetCellValue("Sheet1", "B23", "A left to right column (the default)."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A23"}, Location: []string{"A23"},
Range: []string{"Sheet3!A4:J4"}, Range: []string{"Sheet3!A4:J4"},
Type: "column", Type: "column",
@ -159,7 +162,7 @@ func TestAddSparkline(t *testing.T) {
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B24", "A right to left column.")) assert.NoError(t, f.SetCellValue("Sheet1", "B24", "A right to left column."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A24"}, Location: []string{"A24"},
Range: []string{"Sheet3!A4:J4"}, Range: []string{"Sheet3!A4:J4"},
Type: "column", Type: "column",
@ -168,7 +171,7 @@ func TestAddSparkline(t *testing.T) {
})) }))
assert.NoError(t, f.SetCellValue("Sheet1", "B25", "Sparkline and text in one cell.")) assert.NoError(t, f.SetCellValue("Sheet1", "B25", "Sparkline and text in one cell."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A25"}, Location: []string{"A25"},
Range: []string{"Sheet3!A4:J4"}, Range: []string{"Sheet3!A4:J4"},
Type: "column", Type: "column",
@ -177,34 +180,34 @@ func TestAddSparkline(t *testing.T) {
assert.NoError(t, f.SetCellValue("Sheet1", "A25", "Growth")) assert.NoError(t, f.SetCellValue("Sheet1", "A25", "Growth"))
assert.NoError(t, f.SetCellValue("Sheet1", "B27", "A grouped sparkline. Changes are applied to all three.")) assert.NoError(t, f.SetCellValue("Sheet1", "B27", "A grouped sparkline. Changes are applied to all three."))
assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A27", "A28", "A29"}, Location: []string{"A27", "A28", "A29"},
Range: []string{"Sheet3!A5:J5", "Sheet3!A6:J6", "Sheet3!A7:J7"}, Range: []string{"Sheet3!A5:J5", "Sheet3!A6:J6", "Sheet3!A7:J7"},
Markers: true, Markers: true,
})) }))
// Sheet2 sections // Sheet2 sections
assert.NoError(t, f.AddSparkline("Sheet2", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet2", &SparklineOptions{
Location: []string{"F3"}, Location: []string{"F3"},
Range: []string{"Sheet2!A3:E3"}, Range: []string{"Sheet2!A3:E3"},
Type: "win_loss", Type: "win_loss",
Negative: true, Negative: true,
})) }))
assert.NoError(t, f.AddSparkline("Sheet2", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet2", &SparklineOptions{
Location: []string{"F1"}, Location: []string{"F1"},
Range: []string{"Sheet2!A1:E1"}, Range: []string{"Sheet2!A1:E1"},
Markers: true, Markers: true,
})) }))
assert.NoError(t, f.AddSparkline("Sheet2", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet2", &SparklineOptions{
Location: []string{"F2"}, Location: []string{"F2"},
Range: []string{"Sheet2!A2:E2"}, Range: []string{"Sheet2!A2:E2"},
Type: "column", Type: "column",
Style: 12, Style: 12,
})) }))
assert.NoError(t, f.AddSparkline("Sheet2", &SparklineOption{ assert.NoError(t, f.AddSparkline("Sheet2", &SparklineOptions{
Location: []string{"F3"}, Location: []string{"F3"},
Range: []string{"Sheet2!A3:E3"}, Range: []string{"Sheet2!A3:E3"},
Type: "win_loss", Type: "win_loss",
@ -215,39 +218,39 @@ func TestAddSparkline(t *testing.T) {
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddSparkline.xlsx"))) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddSparkline.xlsx")))
// Test error exceptions // Test error exceptions
assert.EqualError(t, f.AddSparkline("SheetN", &SparklineOption{ assert.EqualError(t, f.AddSparkline("SheetN", &SparklineOptions{
Location: []string{"F3"}, Location: []string{"F3"},
Range: []string{"Sheet2!A3:E3"}, Range: []string{"Sheet2!A3:E3"},
}), "sheet SheetN does not exist") }), "sheet SheetN does not exist")
assert.EqualError(t, f.AddSparkline("Sheet1", nil), ErrParameterRequired.Error()) assert.EqualError(t, f.AddSparkline("Sheet1", nil), ErrParameterRequired.Error())
assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Range: []string{"Sheet2!A3:E3"}, Range: []string{"Sheet2!A3:E3"},
}), ErrSparklineLocation.Error()) }), ErrSparklineLocation.Error())
assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"F3"}, Location: []string{"F3"},
}), ErrSparklineRange.Error()) }), ErrSparklineRange.Error())
assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"F2", "F3"}, Location: []string{"F2", "F3"},
Range: []string{"Sheet2!A3:E3"}, Range: []string{"Sheet2!A3:E3"},
}), ErrSparkline.Error()) }), ErrSparkline.Error())
assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"F3"}, Location: []string{"F3"},
Range: []string{"Sheet2!A3:E3"}, Range: []string{"Sheet2!A3:E3"},
Type: "unknown_type", Type: "unknown_type",
}), ErrSparklineType.Error()) }), ErrSparklineType.Error())
assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"F3"}, Location: []string{"F3"},
Range: []string{"Sheet2!A3:E3"}, Range: []string{"Sheet2!A3:E3"},
Style: -1, Style: -1,
}), ErrSparklineStyle.Error()) }), ErrSparklineStyle.Error())
assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"F3"}, Location: []string{"F3"},
Range: []string{"Sheet2!A3:E3"}, Range: []string{"Sheet2!A3:E3"},
Style: -1, Style: -1,
@ -265,7 +268,7 @@ func TestAddSparkline(t *testing.T) {
</x14:sparklineGroups> </x14:sparklineGroups>
</ext> </ext>
</extLst>` </extLst>`
assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOption{ assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{
Location: []string{"A2"}, Location: []string{"A2"},
Range: []string{"Sheet3!A1:J1"}, Range: []string{"Sheet3!A1:J1"},
}), "XML syntax error on line 6: element <sparklineGroup> closed by </sparklines>") }), "XML syntax error on line 6: element <sparklineGroup> closed by </sparklines>")

View File

@ -139,8 +139,8 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
// called after the rows are written but before Flush. // called after the rows are written but before Flush.
// //
// See File.AddTable for details on the table format. // See File.AddTable for details on the table format.
func (sw *StreamWriter) AddTable(hCell, vCell, format string) error { func (sw *StreamWriter) AddTable(hCell, vCell, opts string) error {
formatSet, err := parseFormatTableSet(format) options, err := parseTableOptions(opts)
if err != nil { if err != nil {
return err return err
} }
@ -177,7 +177,7 @@ func (sw *StreamWriter) AddTable(hCell, vCell, format string) error {
tableID := sw.File.countTables() + 1 tableID := sw.File.countTables() + 1
name := formatSet.TableName name := options.TableName
if name == "" { if name == "" {
name = "Table" + strconv.Itoa(tableID) name = "Table" + strconv.Itoa(tableID)
} }
@ -196,11 +196,11 @@ func (sw *StreamWriter) AddTable(hCell, vCell, format string) error {
TableColumn: tableColumn, TableColumn: tableColumn,
}, },
TableStyleInfo: &xlsxTableStyleInfo{ TableStyleInfo: &xlsxTableStyleInfo{
Name: formatSet.TableStyle, Name: options.TableStyle,
ShowFirstColumn: formatSet.ShowFirstColumn, ShowFirstColumn: options.ShowFirstColumn,
ShowLastColumn: formatSet.ShowLastColumn, ShowLastColumn: options.ShowLastColumn,
ShowRowStripes: formatSet.ShowRowStripes, ShowRowStripes: options.ShowRowStripes,
ShowColumnStripes: formatSet.ShowColumnStripes, ShowColumnStripes: options.ShowColumnStripes,
}, },
} }

View File

@ -173,7 +173,7 @@ func TestStreamTable(t *testing.T) {
assert.NoError(t, streamWriter.AddTable("A1", "C1", ``)) assert.NoError(t, streamWriter.AddTable("A1", "C1", ``))
// Test add table with illegal formatset. // Test add table with illegal options.
assert.EqualError(t, streamWriter.AddTable("B26", "A21", `{x}`), "invalid character 'x' looking for beginning of object key string") assert.EqualError(t, streamWriter.AddTable("B26", "A21", `{x}`), "invalid character 'x' looking for beginning of object key string")
// Test add table with illegal cell reference. // Test add table with illegal cell reference.
assert.EqualError(t, streamWriter.AddTable("A", "B1", `{}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) assert.EqualError(t, streamWriter.AddTable("A", "B1", `{}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())

View File

@ -2859,13 +2859,13 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error {
// max_color - Same as min_color, see above. // max_color - Same as min_color, see above.
// //
// bar_color - Used for data_bar. Same as min_color, see above. // bar_color - Used for data_bar. Same as min_color, see above.
func (f *File) SetConditionalFormat(sheet, reference, formatSet string) error { func (f *File) SetConditionalFormat(sheet, reference, opts string) error {
var format []*formatConditional var format []*conditionalOptions
err := json.Unmarshal([]byte(formatSet), &format) err := json.Unmarshal([]byte(opts), &format)
if err != nil { if err != nil {
return err return err
} }
drawContFmtFunc := map[string]func(p int, ct string, fmtCond *formatConditional) *xlsxCfRule{ drawContFmtFunc := map[string]func(p int, ct string, fmtCond *conditionalOptions) *xlsxCfRule{
"cellIs": drawCondFmtCellIs, "cellIs": drawCondFmtCellIs,
"top10": drawCondFmtTop10, "top10": drawCondFmtTop10,
"aboveAverage": drawCondFmtAboveAverage, "aboveAverage": drawCondFmtAboveAverage,
@ -2909,8 +2909,8 @@ func (f *File) SetConditionalFormat(sheet, reference, formatSet string) error {
// extractCondFmtCellIs provides a function to extract conditional format // extractCondFmtCellIs provides a function to extract conditional format
// settings for cell value (include between, not between, equal, not equal, // settings for cell value (include between, not between, equal, not equal,
// greater than and less than) by given conditional formatting rule. // greater than and less than) by given conditional formatting rule.
func extractCondFmtCellIs(c *xlsxCfRule) *formatConditional { func extractCondFmtCellIs(c *xlsxCfRule) *conditionalOptions {
format := formatConditional{Type: "cell", Criteria: operatorType[c.Operator], Format: *c.DxfID} format := conditionalOptions{Type: "cell", Criteria: operatorType[c.Operator], Format: *c.DxfID}
if len(c.Formula) == 2 { if len(c.Formula) == 2 {
format.Minimum, format.Maximum = c.Formula[0], c.Formula[1] format.Minimum, format.Maximum = c.Formula[0], c.Formula[1]
return &format return &format
@ -2922,8 +2922,8 @@ func extractCondFmtCellIs(c *xlsxCfRule) *formatConditional {
// extractCondFmtTop10 provides a function to extract conditional format // extractCondFmtTop10 provides a function to extract conditional format
// settings for top N (default is top 10) by given conditional formatting // settings for top N (default is top 10) by given conditional formatting
// rule. // rule.
func extractCondFmtTop10(c *xlsxCfRule) *formatConditional { func extractCondFmtTop10(c *xlsxCfRule) *conditionalOptions {
format := formatConditional{ format := conditionalOptions{
Type: "top", Type: "top",
Criteria: "=", Criteria: "=",
Format: *c.DxfID, Format: *c.DxfID,
@ -2939,8 +2939,8 @@ func extractCondFmtTop10(c *xlsxCfRule) *formatConditional {
// extractCondFmtAboveAverage provides a function to extract conditional format // extractCondFmtAboveAverage provides a function to extract conditional format
// settings for above average and below average by given conditional formatting // settings for above average and below average by given conditional formatting
// rule. // rule.
func extractCondFmtAboveAverage(c *xlsxCfRule) *formatConditional { func extractCondFmtAboveAverage(c *xlsxCfRule) *conditionalOptions {
return &formatConditional{ return &conditionalOptions{
Type: "average", Type: "average",
Criteria: "=", Criteria: "=",
Format: *c.DxfID, Format: *c.DxfID,
@ -2951,8 +2951,8 @@ func extractCondFmtAboveAverage(c *xlsxCfRule) *formatConditional {
// extractCondFmtDuplicateUniqueValues provides a function to extract // extractCondFmtDuplicateUniqueValues provides a function to extract
// conditional format settings for duplicate and unique values by given // conditional format settings for duplicate and unique values by given
// conditional formatting rule. // conditional formatting rule.
func extractCondFmtDuplicateUniqueValues(c *xlsxCfRule) *formatConditional { func extractCondFmtDuplicateUniqueValues(c *xlsxCfRule) *conditionalOptions {
return &formatConditional{ return &conditionalOptions{
Type: map[string]string{ Type: map[string]string{
"duplicateValues": "duplicate", "duplicateValues": "duplicate",
"uniqueValues": "unique", "uniqueValues": "unique",
@ -2965,8 +2965,8 @@ func extractCondFmtDuplicateUniqueValues(c *xlsxCfRule) *formatConditional {
// extractCondFmtColorScale provides a function to extract conditional format // extractCondFmtColorScale provides a function to extract conditional format
// settings for color scale (include 2 color scale and 3 color scale) by given // settings for color scale (include 2 color scale and 3 color scale) by given
// conditional formatting rule. // conditional formatting rule.
func extractCondFmtColorScale(c *xlsxCfRule) *formatConditional { func extractCondFmtColorScale(c *xlsxCfRule) *conditionalOptions {
var format formatConditional var format conditionalOptions
format.Type, format.Criteria = "2_color_scale", "=" format.Type, format.Criteria = "2_color_scale", "="
values := len(c.ColorScale.Cfvo) values := len(c.ColorScale.Cfvo)
colors := len(c.ColorScale.Color) colors := len(c.ColorScale.Color)
@ -3000,8 +3000,8 @@ func extractCondFmtColorScale(c *xlsxCfRule) *formatConditional {
// extractCondFmtDataBar provides a function to extract conditional format // extractCondFmtDataBar provides a function to extract conditional format
// settings for data bar by given conditional formatting rule. // settings for data bar by given conditional formatting rule.
func extractCondFmtDataBar(c *xlsxCfRule) *formatConditional { func extractCondFmtDataBar(c *xlsxCfRule) *conditionalOptions {
format := formatConditional{Type: "data_bar", Criteria: "="} format := conditionalOptions{Type: "data_bar", Criteria: "="}
if c.DataBar != nil { if c.DataBar != nil {
format.MinType = c.DataBar.Cfvo[0].Type format.MinType = c.DataBar.Cfvo[0].Type
format.MaxType = c.DataBar.Cfvo[1].Type format.MaxType = c.DataBar.Cfvo[1].Type
@ -3012,8 +3012,8 @@ func extractCondFmtDataBar(c *xlsxCfRule) *formatConditional {
// extractCondFmtExp provides a function to extract conditional format settings // extractCondFmtExp provides a function to extract conditional format settings
// for expression by given conditional formatting rule. // for expression by given conditional formatting rule.
func extractCondFmtExp(c *xlsxCfRule) *formatConditional { func extractCondFmtExp(c *xlsxCfRule) *conditionalOptions {
format := formatConditional{Type: "formula", Format: *c.DxfID} format := conditionalOptions{Type: "formula", Format: *c.DxfID}
if len(c.Formula) > 0 { if len(c.Formula) > 0 {
format.Criteria = c.Formula[0] format.Criteria = c.Formula[0]
} }
@ -3023,7 +3023,7 @@ func extractCondFmtExp(c *xlsxCfRule) *formatConditional {
// GetConditionalFormats returns conditional format settings by given worksheet // GetConditionalFormats returns conditional format settings by given worksheet
// name. // name.
func (f *File) GetConditionalFormats(sheet string) (map[string]string, error) { func (f *File) GetConditionalFormats(sheet string) (map[string]string, error) {
extractContFmtFunc := map[string]func(c *xlsxCfRule) *formatConditional{ extractContFmtFunc := map[string]func(c *xlsxCfRule) *conditionalOptions{
"cellIs": extractCondFmtCellIs, "cellIs": extractCondFmtCellIs,
"top10": extractCondFmtTop10, "top10": extractCondFmtTop10,
"aboveAverage": extractCondFmtAboveAverage, "aboveAverage": extractCondFmtAboveAverage,
@ -3040,14 +3040,14 @@ func (f *File) GetConditionalFormats(sheet string) (map[string]string, error) {
return conditionalFormats, err return conditionalFormats, err
} }
for _, cf := range ws.ConditionalFormatting { for _, cf := range ws.ConditionalFormatting {
var format []*formatConditional var opts []*conditionalOptions
for _, cr := range cf.CfRule { for _, cr := range cf.CfRule {
if extractFunc, ok := extractContFmtFunc[cr.Type]; ok { if extractFunc, ok := extractContFmtFunc[cr.Type]; ok {
format = append(format, extractFunc(cr)) opts = append(opts, extractFunc(cr))
} }
} }
formatSet, _ := json.Marshal(format) options, _ := json.Marshal(opts)
conditionalFormats[cf.SQRef] = string(formatSet) conditionalFormats[cf.SQRef] = string(options)
} }
return conditionalFormats, err return conditionalFormats, err
} }
@ -3071,7 +3071,7 @@ func (f *File) UnsetConditionalFormat(sheet, reference string) error {
// drawCondFmtCellIs provides a function to create conditional formatting rule // drawCondFmtCellIs provides a function to create conditional formatting rule
// for cell value (include between, not between, equal, not equal, greater // for cell value (include between, not between, equal, not equal, greater
// than and less than) by given priority, criteria type and format settings. // than and less than) by given priority, criteria type and format settings.
func drawCondFmtCellIs(p int, ct string, format *formatConditional) *xlsxCfRule { func drawCondFmtCellIs(p int, ct string, format *conditionalOptions) *xlsxCfRule {
c := &xlsxCfRule{ c := &xlsxCfRule{
Priority: p + 1, Priority: p + 1,
Type: validType[format.Type], Type: validType[format.Type],
@ -3094,7 +3094,7 @@ func drawCondFmtCellIs(p int, ct string, format *formatConditional) *xlsxCfRule
// drawCondFmtTop10 provides a function to create conditional formatting rule // drawCondFmtTop10 provides a function to create conditional formatting rule
// for top N (default is top 10) by given priority, criteria type and format // for top N (default is top 10) by given priority, criteria type and format
// settings. // settings.
func drawCondFmtTop10(p int, ct string, format *formatConditional) *xlsxCfRule { func drawCondFmtTop10(p int, ct string, format *conditionalOptions) *xlsxCfRule {
c := &xlsxCfRule{ c := &xlsxCfRule{
Priority: p + 1, Priority: p + 1,
Bottom: format.Type == "bottom", Bottom: format.Type == "bottom",
@ -3113,7 +3113,7 @@ func drawCondFmtTop10(p int, ct string, format *formatConditional) *xlsxCfRule {
// drawCondFmtAboveAverage provides a function to create conditional // drawCondFmtAboveAverage provides a function to create conditional
// formatting rule for above average and below average by given priority, // formatting rule for above average and below average by given priority,
// criteria type and format settings. // criteria type and format settings.
func drawCondFmtAboveAverage(p int, ct string, format *formatConditional) *xlsxCfRule { func drawCondFmtAboveAverage(p int, ct string, format *conditionalOptions) *xlsxCfRule {
return &xlsxCfRule{ return &xlsxCfRule{
Priority: p + 1, Priority: p + 1,
Type: validType[format.Type], Type: validType[format.Type],
@ -3125,7 +3125,7 @@ func drawCondFmtAboveAverage(p int, ct string, format *formatConditional) *xlsxC
// drawCondFmtDuplicateUniqueValues provides a function to create conditional // drawCondFmtDuplicateUniqueValues provides a function to create conditional
// formatting rule for duplicate and unique values by given priority, criteria // formatting rule for duplicate and unique values by given priority, criteria
// type and format settings. // type and format settings.
func drawCondFmtDuplicateUniqueValues(p int, ct string, format *formatConditional) *xlsxCfRule { func drawCondFmtDuplicateUniqueValues(p int, ct string, format *conditionalOptions) *xlsxCfRule {
return &xlsxCfRule{ return &xlsxCfRule{
Priority: p + 1, Priority: p + 1,
Type: validType[format.Type], Type: validType[format.Type],
@ -3136,7 +3136,7 @@ func drawCondFmtDuplicateUniqueValues(p int, ct string, format *formatConditiona
// drawCondFmtColorScale provides a function to create conditional formatting // drawCondFmtColorScale provides a function to create conditional formatting
// rule for color scale (include 2 color scale and 3 color scale) by given // rule for color scale (include 2 color scale and 3 color scale) by given
// priority, criteria type and format settings. // priority, criteria type and format settings.
func drawCondFmtColorScale(p int, ct string, format *formatConditional) *xlsxCfRule { func drawCondFmtColorScale(p int, ct string, format *conditionalOptions) *xlsxCfRule {
minValue := format.MinValue minValue := format.MinValue
if minValue == "" { if minValue == "" {
minValue = "0" minValue = "0"
@ -3173,7 +3173,7 @@ func drawCondFmtColorScale(p int, ct string, format *formatConditional) *xlsxCfR
// drawCondFmtDataBar provides a function to create conditional formatting // drawCondFmtDataBar provides a function to create conditional formatting
// rule for data bar by given priority, criteria type and format settings. // rule for data bar by given priority, criteria type and format settings.
func drawCondFmtDataBar(p int, ct string, format *formatConditional) *xlsxCfRule { func drawCondFmtDataBar(p int, ct string, format *conditionalOptions) *xlsxCfRule {
return &xlsxCfRule{ return &xlsxCfRule{
Priority: p + 1, Priority: p + 1,
Type: validType[format.Type], Type: validType[format.Type],
@ -3186,7 +3186,7 @@ func drawCondFmtDataBar(p int, ct string, format *formatConditional) *xlsxCfRule
// drawCondFmtExp provides a function to create conditional formatting rule // drawCondFmtExp provides a function to create conditional formatting rule
// for expression by given priority, criteria type and format settings. // for expression by given priority, criteria type and format settings.
func drawCondFmtExp(p int, ct string, format *formatConditional) *xlsxCfRule { func drawCondFmtExp(p int, ct string, format *conditionalOptions) *xlsxCfRule {
return &xlsxCfRule{ return &xlsxCfRule{
Priority: p + 1, Priority: p + 1,
Type: validType[format.Type], Type: validType[format.Type],

View File

@ -192,9 +192,9 @@ func TestGetConditionalFormats(t *testing.T) {
f := NewFile() f := NewFile()
err := f.SetConditionalFormat("Sheet1", "A1:A2", format) err := f.SetConditionalFormat("Sheet1", "A1:A2", format)
assert.NoError(t, err) assert.NoError(t, err)
formatSet, err := f.GetConditionalFormats("Sheet1") opts, err := f.GetConditionalFormats("Sheet1")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, format, formatSet["A1:A2"]) assert.Equal(t, format, opts["A1:A2"])
} }
// Test get conditional formats on no exists worksheet // Test get conditional formats on no exists worksheet
f := NewFile() f := NewFile()

View File

@ -20,12 +20,12 @@ import (
"strings" "strings"
) )
// parseFormatTableSet provides a function to parse the format settings of the // parseTableOptions provides a function to parse the format settings of the
// table with default value. // table with default value.
func parseFormatTableSet(formatSet string) (*formatTable, error) { func parseTableOptions(opts string) (*tableOptions, error) {
format := formatTable{ShowRowStripes: true} options := tableOptions{ShowRowStripes: true}
err := json.Unmarshal(parseFormatSet(formatSet), &format) err := json.Unmarshal(fallbackOptions(opts), &options)
return &format, err return &options, err
} }
// AddTable provides the method to add table in a worksheet by given worksheet // AddTable provides the method to add table in a worksheet by given worksheet
@ -57,8 +57,8 @@ func parseFormatTableSet(formatSet string) (*formatTable, error) {
// TableStyleLight1 - TableStyleLight21 // TableStyleLight1 - TableStyleLight21
// TableStyleMedium1 - TableStyleMedium28 // TableStyleMedium1 - TableStyleMedium28
// TableStyleDark1 - TableStyleDark11 // TableStyleDark1 - TableStyleDark11
func (f *File) AddTable(sheet, hCell, vCell, format string) error { func (f *File) AddTable(sheet, hCell, vCell, opts string) error {
formatSet, err := parseFormatTableSet(format) options, err := parseTableOptions(opts)
if err != nil { if err != nil {
return err return err
} }
@ -91,7 +91,7 @@ func (f *File) AddTable(sheet, hCell, vCell, format string) error {
return err return err
} }
f.addSheetNameSpace(sheet, SourceRelationship) f.addSheetNameSpace(sheet, SourceRelationship)
if err = f.addTable(sheet, tableXML, hCol, hRow, vCol, vRow, tableID, formatSet); err != nil { if err = f.addTable(sheet, tableXML, hCol, hRow, vCol, vRow, tableID, options); err != nil {
return err return err
} }
f.addContentTypePart(tableID, "table") f.addContentTypePart(tableID, "table")
@ -160,7 +160,7 @@ func (f *File) setTableHeader(sheet string, x1, y1, x2 int) ([]*xlsxTableColumn,
// addTable provides a function to add table by given worksheet name, // addTable provides a function to add table by given worksheet name,
// range reference and format set. // range reference and format set.
func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, formatSet *formatTable) error { func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, opts *tableOptions) error {
// Correct the minimum number of rows, the table at least two lines. // Correct the minimum number of rows, the table at least two lines.
if y1 == y2 { if y1 == y2 {
y2++ y2++
@ -172,7 +172,7 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, formatSet
return err return err
} }
tableColumns, _ := f.setTableHeader(sheet, x1, y1, x2) tableColumns, _ := f.setTableHeader(sheet, x1, y1, x2)
name := formatSet.TableName name := opts.TableName
if name == "" { if name == "" {
name = "Table" + strconv.Itoa(i) name = "Table" + strconv.Itoa(i)
} }
@ -190,11 +190,11 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, formatSet
TableColumn: tableColumns, TableColumn: tableColumns,
}, },
TableStyleInfo: &xlsxTableStyleInfo{ TableStyleInfo: &xlsxTableStyleInfo{
Name: formatSet.TableStyle, Name: opts.TableStyle,
ShowFirstColumn: formatSet.ShowFirstColumn, ShowFirstColumn: opts.ShowFirstColumn,
ShowLastColumn: formatSet.ShowLastColumn, ShowLastColumn: opts.ShowLastColumn,
ShowRowStripes: formatSet.ShowRowStripes, ShowRowStripes: opts.ShowRowStripes,
ShowColumnStripes: formatSet.ShowColumnStripes, ShowColumnStripes: opts.ShowColumnStripes,
}, },
} }
table, _ := xml.Marshal(t) table, _ := xml.Marshal(t)
@ -202,12 +202,12 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, formatSet
return nil return nil
} }
// parseAutoFilterSet provides a function to parse the settings of the auto // parseAutoFilterOptions provides a function to parse the settings of the auto
// filter. // filter.
func parseAutoFilterSet(formatSet string) (*formatAutoFilter, error) { func parseAutoFilterOptions(opts string) (*autoFilterOptions, error) {
format := formatAutoFilter{} options := autoFilterOptions{}
err := json.Unmarshal([]byte(formatSet), &format) err := json.Unmarshal([]byte(opts), &options)
return &format, err return &options, err
} }
// AutoFilter provides the method to add auto filter in a worksheet by given // AutoFilter provides the method to add auto filter in a worksheet by given
@ -279,7 +279,7 @@ func parseAutoFilterSet(formatSet string) (*formatAutoFilter, error) {
// x < 2000 // x < 2000
// col < 2000 // col < 2000
// Price < 2000 // Price < 2000
func (f *File) AutoFilter(sheet, hCell, vCell, format string) error { func (f *File) AutoFilter(sheet, hCell, vCell, opts string) error {
hCol, hRow, err := CellNameToCoordinates(hCell) hCol, hRow, err := CellNameToCoordinates(hCell)
if err != nil { if err != nil {
return err return err
@ -297,7 +297,7 @@ func (f *File) AutoFilter(sheet, hCell, vCell, format string) error {
vRow, hRow = hRow, vRow vRow, hRow = hRow, vRow
} }
formatSet, _ := parseAutoFilterSet(format) options, _ := parseAutoFilterOptions(opts)
cellStart, _ := CoordinatesToCellName(hCol, hRow, true) cellStart, _ := CoordinatesToCellName(hCol, hRow, true)
cellEnd, _ := CoordinatesToCellName(vCol, vRow, true) cellEnd, _ := CoordinatesToCellName(vCol, vRow, true)
ref, filterDB := cellStart+":"+cellEnd, "_xlnm._FilterDatabase" ref, filterDB := cellStart+":"+cellEnd, "_xlnm._FilterDatabase"
@ -328,12 +328,12 @@ func (f *File) AutoFilter(sheet, hCell, vCell, format string) error {
} }
} }
refRange := vCol - hCol refRange := vCol - hCol
return f.autoFilter(sheet, ref, refRange, hCol, formatSet) return f.autoFilter(sheet, ref, refRange, hCol, options)
} }
// autoFilter provides a function to extract the tokens from the filter // autoFilter provides a function to extract the tokens from the filter
// expression. The tokens are mainly non-whitespace groups. // expression. The tokens are mainly non-whitespace groups.
func (f *File) autoFilter(sheet, ref string, refRange, col int, formatSet *formatAutoFilter) error { func (f *File) autoFilter(sheet, ref string, refRange, col int, opts *autoFilterOptions) error {
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return err return err
@ -346,28 +346,28 @@ func (f *File) autoFilter(sheet, ref string, refRange, col int, formatSet *forma
Ref: ref, Ref: ref,
} }
ws.AutoFilter = filter ws.AutoFilter = filter
if formatSet.Column == "" || formatSet.Expression == "" { if opts.Column == "" || opts.Expression == "" {
return nil return nil
} }
fsCol, err := ColumnNameToNumber(formatSet.Column) fsCol, err := ColumnNameToNumber(opts.Column)
if err != nil { if err != nil {
return err return err
} }
offset := fsCol - col offset := fsCol - col
if offset < 0 || offset > refRange { if offset < 0 || offset > refRange {
return fmt.Errorf("incorrect index of column '%s'", formatSet.Column) return fmt.Errorf("incorrect index of column '%s'", opts.Column)
} }
filter.FilterColumn = append(filter.FilterColumn, &xlsxFilterColumn{ filter.FilterColumn = append(filter.FilterColumn, &xlsxFilterColumn{
ColID: offset, ColID: offset,
}) })
re := regexp.MustCompile(`"(?:[^"]|"")*"|\S+`) re := regexp.MustCompile(`"(?:[^"]|"")*"|\S+`)
token := re.FindAllString(formatSet.Expression, -1) token := re.FindAllString(opts.Expression, -1)
if len(token) != 3 && len(token) != 7 { if len(token) != 3 && len(token) != 7 {
return fmt.Errorf("incorrect number of tokens in criteria '%s'", formatSet.Expression) return fmt.Errorf("incorrect number of tokens in criteria '%s'", opts.Expression)
} }
expressions, tokens, err := f.parseFilterExpression(formatSet.Expression, token) expressions, tokens, err := f.parseFilterExpression(opts.Expression, token)
if err != nil { if err != nil {
return err return err
} }

View File

@ -31,7 +31,7 @@ func TestAddTable(t *testing.T) {
// Test add table in not exist worksheet. // Test add table in not exist worksheet.
assert.EqualError(t, f.AddTable("SheetN", "B26", "A21", `{}`), "sheet SheetN does not exist") assert.EqualError(t, f.AddTable("SheetN", "B26", "A21", `{}`), "sheet SheetN does not exist")
// Test add table with illegal formatset. // Test add table with illegal options.
assert.EqualError(t, f.AddTable("Sheet1", "B26", "A21", `{x}`), "invalid character 'x' looking for beginning of object key string") assert.EqualError(t, f.AddTable("Sheet1", "B26", "A21", `{x}`), "invalid character 'x' looking for beginning of object key string")
// Test add table with illegal cell reference. // Test add table with illegal cell reference.
assert.EqualError(t, f.AddTable("Sheet1", "A", "B1", `{}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) assert.EqualError(t, f.AddTable("Sheet1", "A", "B1", `{}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
@ -108,19 +108,19 @@ func TestAutoFilterError(t *testing.T) {
}) })
} }
assert.EqualError(t, f.autoFilter("SheetN", "A1", 1, 1, &formatAutoFilter{ assert.EqualError(t, f.autoFilter("SheetN", "A1", 1, 1, &autoFilterOptions{
Column: "A", Column: "A",
Expression: "", Expression: "",
}), "sheet SheetN does not exist") }), "sheet SheetN does not exist")
assert.EqualError(t, f.autoFilter("Sheet1", "A1", 1, 1, &formatAutoFilter{ assert.EqualError(t, f.autoFilter("Sheet1", "A1", 1, 1, &autoFilterOptions{
Column: "-", Column: "-",
Expression: "-", Expression: "-",
}), newInvalidColumnNameError("-").Error()) }), newInvalidColumnNameError("-").Error())
assert.EqualError(t, f.autoFilter("Sheet1", "A1", 1, 100, &formatAutoFilter{ assert.EqualError(t, f.autoFilter("Sheet1", "A1", 1, 100, &autoFilterOptions{
Column: "A", Column: "A",
Expression: "-", Expression: "-",
}), `incorrect index of column 'A'`) }), `incorrect index of column 'A'`)
assert.EqualError(t, f.autoFilter("Sheet1", "A1", 1, 1, &formatAutoFilter{ assert.EqualError(t, f.autoFilter("Sheet1", "A1", 1, 1, &autoFilterOptions{
Column: "A", Column: "A",
Expression: "-", Expression: "-",
}), `incorrect number of tokens in criteria '-'`) }), `incorrect number of tokens in criteria '-'`)

View File

@ -21,26 +21,38 @@ import (
"strings" "strings"
) )
// WorkbookPrOption is an option of a view of a workbook. See SetWorkbookPrOptions(). // SetWorkbookProps provides a function to sets workbook properties.
type WorkbookPrOption interface { func (f *File) SetWorkbookProps(opts *WorkbookPropsOptions) error {
setWorkbookPrOption(pr *xlsxWorkbookPr) wb := f.workbookReader()
if wb.WorkbookPr == nil {
wb.WorkbookPr = new(xlsxWorkbookPr)
}
if opts == nil {
return nil
}
if opts.Date1904 != nil {
wb.WorkbookPr.Date1904 = *opts.Date1904
}
if opts.FilterPrivacy != nil {
wb.WorkbookPr.FilterPrivacy = *opts.FilterPrivacy
}
if opts.CodeName != nil {
wb.WorkbookPr.CodeName = *opts.CodeName
}
return nil
} }
// WorkbookPrOptionPtr is a writable WorkbookPrOption. See GetWorkbookPrOptions(). // GetWorkbookProps provides a function to gets workbook properties.
type WorkbookPrOptionPtr interface { func (f *File) GetWorkbookProps() (WorkbookPropsOptions, error) {
WorkbookPrOption wb, opts := f.workbookReader(), WorkbookPropsOptions{}
getWorkbookPrOption(pr *xlsxWorkbookPr) if wb.WorkbookPr != nil {
opts.Date1904 = boolPtr(wb.WorkbookPr.Date1904)
opts.FilterPrivacy = boolPtr(wb.WorkbookPr.FilterPrivacy)
opts.CodeName = stringPtr(wb.WorkbookPr.CodeName)
}
return opts, nil
} }
type (
// Date1904 is an option used for WorkbookPrOption, that indicates whether
// to use a 1900 or 1904 date system when converting serial date-times in
// the workbook to dates
Date1904 bool
// FilterPrivacy is an option used for WorkbookPrOption
FilterPrivacy bool
)
// setWorkbook update workbook property of the spreadsheet. Maximum 31 // setWorkbook update workbook property of the spreadsheet. Maximum 31
// characters are allowed in sheet title. // characters are allowed in sheet title.
func (f *File) setWorkbook(name string, sheetID, rid int) { func (f *File) setWorkbook(name string, sheetID, rid int) {
@ -116,84 +128,3 @@ func (f *File) workBookWriter() {
f.saveFileList(f.getWorkbookPath(), replaceRelationshipsBytes(f.replaceNameSpaceBytes(f.getWorkbookPath(), output))) f.saveFileList(f.getWorkbookPath(), replaceRelationshipsBytes(f.replaceNameSpaceBytes(f.getWorkbookPath(), output)))
} }
} }
// SetWorkbookPrOptions provides a function to sets workbook properties.
//
// Available options:
//
// Date1904(bool)
// FilterPrivacy(bool)
// CodeName(string)
func (f *File) SetWorkbookPrOptions(opts ...WorkbookPrOption) error {
wb := f.workbookReader()
pr := wb.WorkbookPr
if pr == nil {
pr = new(xlsxWorkbookPr)
wb.WorkbookPr = pr
}
for _, opt := range opts {
opt.setWorkbookPrOption(pr)
}
return nil
}
// setWorkbookPrOption implements the WorkbookPrOption interface.
func (o Date1904) setWorkbookPrOption(pr *xlsxWorkbookPr) {
pr.Date1904 = bool(o)
}
// setWorkbookPrOption implements the WorkbookPrOption interface.
func (o FilterPrivacy) setWorkbookPrOption(pr *xlsxWorkbookPr) {
pr.FilterPrivacy = bool(o)
}
// setWorkbookPrOption implements the WorkbookPrOption interface.
func (o CodeName) setWorkbookPrOption(pr *xlsxWorkbookPr) {
pr.CodeName = string(o)
}
// GetWorkbookPrOptions provides a function to gets workbook properties.
//
// Available options:
//
// Date1904(bool)
// FilterPrivacy(bool)
// CodeName(string)
func (f *File) GetWorkbookPrOptions(opts ...WorkbookPrOptionPtr) error {
wb := f.workbookReader()
pr := wb.WorkbookPr
for _, opt := range opts {
opt.getWorkbookPrOption(pr)
}
return nil
}
// getWorkbookPrOption implements the WorkbookPrOption interface and get the
// date1904 of the workbook.
func (o *Date1904) getWorkbookPrOption(pr *xlsxWorkbookPr) {
if pr == nil {
*o = false
return
}
*o = Date1904(pr.Date1904)
}
// getWorkbookPrOption implements the WorkbookPrOption interface and get the
// filter privacy of the workbook.
func (o *FilterPrivacy) getWorkbookPrOption(pr *xlsxWorkbookPr) {
if pr == nil {
*o = false
return
}
*o = FilterPrivacy(pr.FilterPrivacy)
}
// getWorkbookPrOption implements the WorkbookPrOption interface and get the
// code name of the workbook.
func (o *CodeName) getWorkbookPrOption(pr *xlsxWorkbookPr) {
if pr == nil {
*o = ""
return
}
*o = CodeName(pr.CodeName)
}

View File

@ -1,69 +1,23 @@
package excelize package excelize
import ( import (
"fmt"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func ExampleFile_SetWorkbookPrOptions() { func TestWorkbookProps(t *testing.T) {
f := NewFile()
if err := f.SetWorkbookPrOptions(
Date1904(false),
FilterPrivacy(false),
CodeName("code"),
); err != nil {
fmt.Println(err)
}
// Output:
}
func ExampleFile_GetWorkbookPrOptions() {
f := NewFile()
var (
date1904 Date1904
filterPrivacy FilterPrivacy
codeName CodeName
)
if err := f.GetWorkbookPrOptions(&date1904); err != nil {
fmt.Println(err)
}
if err := f.GetWorkbookPrOptions(&filterPrivacy); err != nil {
fmt.Println(err)
}
if err := f.GetWorkbookPrOptions(&codeName); err != nil {
fmt.Println(err)
}
fmt.Println("Defaults:")
fmt.Printf("- date1904: %t\n", date1904)
fmt.Printf("- filterPrivacy: %t\n", filterPrivacy)
fmt.Printf("- codeName: %q\n", codeName)
// Output:
// Defaults:
// - date1904: false
// - filterPrivacy: true
// - codeName: ""
}
func TestWorkbookPr(t *testing.T) {
f := NewFile() f := NewFile()
assert.NoError(t, f.SetWorkbookProps(nil))
wb := f.workbookReader() wb := f.workbookReader()
wb.WorkbookPr = nil wb.WorkbookPr = nil
var date1904 Date1904 expected := WorkbookPropsOptions{
assert.NoError(t, f.GetWorkbookPrOptions(&date1904)) Date1904: boolPtr(true),
assert.Equal(t, false, bool(date1904)) FilterPrivacy: boolPtr(true),
CodeName: stringPtr("code"),
wb.WorkbookPr = nil }
var codeName CodeName assert.NoError(t, f.SetWorkbookProps(&expected))
assert.NoError(t, f.GetWorkbookPrOptions(&codeName)) opts, err := f.GetWorkbookProps()
assert.Equal(t, "", string(codeName)) assert.NoError(t, err)
assert.NoError(t, f.SetWorkbookPrOptions(CodeName("code"))) assert.Equal(t, expected, opts)
assert.NoError(t, f.GetWorkbookPrOptions(&codeName))
assert.Equal(t, "code", string(codeName))
wb.WorkbookPr = nil
var filterPrivacy FilterPrivacy
assert.NoError(t, f.GetWorkbookPrOptions(&filterPrivacy))
assert.Equal(t, false, bool(filterPrivacy))
} }

View File

@ -518,8 +518,8 @@ type cPageMargins struct {
T float64 `xml:"t,attr"` T float64 `xml:"t,attr"`
} }
// formatChartAxis directly maps the format settings of the chart axis. // chartAxisOptions directly maps the format settings of the chart axis.
type formatChartAxis struct { type chartAxisOptions struct {
None bool `json:"none"` None bool `json:"none"`
Crossing string `json:"crossing"` Crossing string `json:"crossing"`
MajorGridlines bool `json:"major_grid_lines"` MajorGridlines bool `json:"major_grid_lines"`
@ -543,26 +543,27 @@ type formatChartAxis struct {
Italic bool `json:"italic"` Italic bool `json:"italic"`
Underline bool `json:"underline"` Underline bool `json:"underline"`
} `json:"num_font"` } `json:"num_font"`
LogBase float64 `json:"logbase"` LogBase float64 `json:"logbase"`
NameLayout formatLayout `json:"name_layout"` NameLayout layoutOptions `json:"name_layout"`
} }
type formatChartDimension struct { // chartDimensionOptions directly maps the dimension of the chart.
type chartDimensionOptions struct {
Width int `json:"width"` Width int `json:"width"`
Height int `json:"height"` Height int `json:"height"`
} }
// formatChart directly maps the format settings of the chart. // chartOptions directly maps the format settings of the chart.
type formatChart struct { type chartOptions struct {
Type string `json:"type"` Type string `json:"type"`
Series []formatChartSeries `json:"series"` Series []chartSeriesOptions `json:"series"`
Format formatPicture `json:"format"` Format pictureOptions `json:"format"`
Dimension formatChartDimension `json:"dimension"` Dimension chartDimensionOptions `json:"dimension"`
Legend formatChartLegend `json:"legend"` Legend chartLegendOptions `json:"legend"`
Title formatChartTitle `json:"title"` Title chartTitleOptions `json:"title"`
VaryColors bool `json:"vary_colors"` VaryColors bool `json:"vary_colors"`
XAxis formatChartAxis `json:"x_axis"` XAxis chartAxisOptions `json:"x_axis"`
YAxis formatChartAxis `json:"y_axis"` YAxis chartAxisOptions `json:"y_axis"`
Chartarea struct { Chartarea struct {
Border struct { Border struct {
None bool `json:"none"` None bool `json:"none"`
@ -594,7 +595,7 @@ type formatChart struct {
Fill struct { Fill struct {
Color string `json:"color"` Color string `json:"color"`
} `json:"fill"` } `json:"fill"`
Layout formatLayout `json:"layout"` Layout layoutOptions `json:"layout"`
} `json:"plotarea"` } `json:"plotarea"`
ShowBlanksAs string `json:"show_blanks_as"` ShowBlanksAs string `json:"show_blanks_as"`
ShowHiddenData bool `json:"show_hidden_data"` ShowHiddenData bool `json:"show_hidden_data"`
@ -603,19 +604,19 @@ type formatChart struct {
order int order int
} }
// formatChartLegend directly maps the format settings of the chart legend. // chartLegendOptions directly maps the format settings of the chart legend.
type formatChartLegend struct { type chartLegendOptions struct {
None bool `json:"none"` None bool `json:"none"`
DeleteSeries []int `json:"delete_series"` DeleteSeries []int `json:"delete_series"`
Font Font `json:"font"` Font Font `json:"font"`
Layout formatLayout `json:"layout"` Layout layoutOptions `json:"layout"`
Position string `json:"position"` Position string `json:"position"`
ShowLegendEntry bool `json:"show_legend_entry"` ShowLegendEntry bool `json:"show_legend_entry"`
ShowLegendKey bool `json:"show_legend_key"` ShowLegendKey bool `json:"show_legend_key"`
} }
// formatChartSeries directly maps the format settings of the chart series. // chartSeriesOptions directly maps the format settings of the chart series.
type formatChartSeries struct { type chartSeriesOptions struct {
Name string `json:"name"` Name string `json:"name"`
Categories string `json:"categories"` Categories string `json:"categories"`
Values string `json:"values"` Values string `json:"values"`
@ -640,16 +641,16 @@ type formatChartSeries struct {
} `json:"marker"` } `json:"marker"`
} }
// formatChartTitle directly maps the format settings of the chart title. // chartTitleOptions directly maps the format settings of the chart title.
type formatChartTitle struct { type chartTitleOptions struct {
None bool `json:"none"` None bool `json:"none"`
Name string `json:"name"` Name string `json:"name"`
Overlay bool `json:"overlay"` Overlay bool `json:"overlay"`
Layout formatLayout `json:"layout"` Layout layoutOptions `json:"layout"`
} }
// formatLayout directly maps the format settings of the element layout. // layoutOptions directly maps the format settings of the element layout.
type formatLayout struct { type layoutOptions struct {
X float64 `json:"x"` X float64 `json:"x"`
Y float64 `json:"y"` Y float64 `json:"y"`
Width float64 `json:"width"` Width float64 `json:"width"`

View File

@ -72,8 +72,8 @@ type xlsxPhoneticRun struct {
T string `xml:"t"` T string `xml:"t"`
} }
// formatComment directly maps the format settings of the comment. // commentOptions directly maps the format settings of the comment.
type formatComment struct { type commentOptions struct {
Author string `json:"author"` Author string `json:"author"`
Text string `json:"text"` Text string `json:"text"`
} }

View File

@ -493,8 +493,8 @@ type xdrTxBody struct {
P []*aP `xml:"a:p"` P []*aP `xml:"a:p"`
} }
// formatPicture directly maps the format settings of the picture. // pictureOptions directly maps the format settings of the picture.
type formatPicture struct { type pictureOptions struct {
FPrintsWithSheet bool `json:"print_obj"` FPrintsWithSheet bool `json:"print_obj"`
FLocksWithSheet bool `json:"locked"` FLocksWithSheet bool `json:"locked"`
NoChangeAspect bool `json:"lock_aspect_ratio"` NoChangeAspect bool `json:"lock_aspect_ratio"`
@ -508,33 +508,33 @@ type formatPicture struct {
Positioning string `json:"positioning"` Positioning string `json:"positioning"`
} }
// formatShape directly maps the format settings of the shape. // shapeOptions directly maps the format settings of the shape.
type formatShape struct { type shapeOptions struct {
Macro string `json:"macro"` Macro string `json:"macro"`
Type string `json:"type"` Type string `json:"type"`
Width int `json:"width"` Width int `json:"width"`
Height int `json:"height"` Height int `json:"height"`
Format formatPicture `json:"format"` Format pictureOptions `json:"format"`
Color formatShapeColor `json:"color"` Color shapeColorOptions `json:"color"`
Line formatLine `json:"line"` Line lineOptions `json:"line"`
Paragraph []formatShapeParagraph `json:"paragraph"` Paragraph []shapeParagraphOptions `json:"paragraph"`
} }
// formatShapeParagraph directly maps the format settings of the paragraph in // shapeParagraphOptions directly maps the format settings of the paragraph in
// the shape. // the shape.
type formatShapeParagraph struct { type shapeParagraphOptions struct {
Font Font `json:"font"` Font Font `json:"font"`
Text string `json:"text"` Text string `json:"text"`
} }
// formatShapeColor directly maps the color settings of the shape. // shapeColorOptions directly maps the color settings of the shape.
type formatShapeColor struct { type shapeColorOptions struct {
Line string `json:"line"` Line string `json:"line"`
Fill string `json:"fill"` Fill string `json:"fill"`
Effect string `json:"effect"` Effect string `json:"effect"`
} }
// formatLine directly maps the line settings of the shape. // lineOptions directly maps the line settings of the shape.
type formatLine struct { type lineOptions struct {
Width float64 `json:"width"` Width float64 `json:"width"`
} }

View File

@ -196,8 +196,8 @@ type xlsxTableStyleInfo struct {
ShowColumnStripes bool `xml:"showColumnStripes,attr"` ShowColumnStripes bool `xml:"showColumnStripes,attr"`
} }
// formatTable directly maps the format settings of the table. // tableOptions directly maps the format settings of the table.
type formatTable struct { type tableOptions struct {
TableName string `json:"table_name"` TableName string `json:"table_name"`
TableStyle string `json:"table_style"` TableStyle string `json:"table_style"`
ShowFirstColumn bool `json:"show_first_column"` ShowFirstColumn bool `json:"show_first_column"`
@ -206,8 +206,8 @@ type formatTable struct {
ShowColumnStripes bool `json:"show_column_stripes"` ShowColumnStripes bool `json:"show_column_stripes"`
} }
// formatAutoFilter directly maps the auto filter settings. // autoFilterOptions directly maps the auto filter settings.
type formatAutoFilter struct { type autoFilterOptions struct {
Column string `json:"column"` Column string `json:"column"`
Expression string `json:"expression"` Expression string `json:"expression"`
FilterList []struct { FilterList []struct {

View File

@ -308,8 +308,15 @@ type xlsxCustomWorkbookView struct {
// DefinedName directly maps the name for a cell or cell range on a // DefinedName directly maps the name for a cell or cell range on a
// worksheet. // worksheet.
type DefinedName struct { type DefinedName struct {
Name string Name string `json:"name,omitempty"`
Comment string Comment string `json:"comment,omitempty"`
RefersTo string RefersTo string `json:"refers_to,omitempty"`
Scope string Scope string `json:"scope,omitempty"`
}
// WorkbookPropsOptions directly maps the settings of workbook proprieties.
type WorkbookPropsOptions struct {
Date1904 *bool `json:"date_1994,omitempty"`
FilterPrivacy *bool `json:"filter_privacy,omitempty"`
CodeName *string `json:"code_name,omitempty"`
} }

View File

@ -454,7 +454,11 @@ type DataValidation struct {
// b (Boolean) | Cell containing a boolean. // b (Boolean) | Cell containing a boolean.
// d (Date) | Cell contains a date in the ISO 8601 format. // d (Date) | Cell contains a date in the ISO 8601 format.
// e (Error) | Cell containing an error. // e (Error) | Cell containing an error.
// inlineStr (Inline String) | Cell containing an (inline) rich string, i.e., one not in the shared string table. If this cell type is used, then the cell value is in the is element rather than the v element in the cell (c element). // inlineStr (Inline String) | Cell containing an (inline) rich string, i.e.,
// | one not in the shared string table. If this
// | cell type is used, then the cell value is in
// | the is element rather than the v element in
// | the cell (c element).
// n (Number) | Cell containing a number. // n (Number) | Cell containing a number.
// s (Shared String) | Cell containing a shared string. // s (Shared String) | Cell containing a shared string.
// str (String) | Cell containing a formula string. // str (String) | Cell containing a formula string.
@ -777,39 +781,39 @@ type xlsxX14Sparkline struct {
Sqref string `xml:"xm:sqref"` Sqref string `xml:"xm:sqref"`
} }
// SparklineOption directly maps the settings of the sparkline. // SparklineOptions directly maps the settings of the sparkline.
type SparklineOption struct { type SparklineOptions struct {
Location []string Location []string `json:"location"`
Range []string Range []string `json:"range"`
Max int Max int `json:"max"`
CustMax int CustMax int `json:"cust_max"`
Min int Min int `json:"min"`
CustMin int CustMin int `json:"cust_min"`
Type string Type string `json:"hype"`
Weight float64 Weight float64 `json:"weight"`
DateAxis bool DateAxis bool `json:"date_axis"`
Markers bool Markers bool `json:"markers"`
High bool High bool `json:"high"`
Low bool Low bool `json:"low"`
First bool First bool `json:"first"`
Last bool Last bool `json:"last"`
Negative bool Negative bool `json:"negative"`
Axis bool Axis bool `json:"axis"`
Hidden bool Hidden bool `json:"hidden"`
Reverse bool Reverse bool `json:"reverse"`
Style int Style int `json:"style"`
SeriesColor string SeriesColor string `json:"series_color"`
NegativeColor string NegativeColor string `json:"negative_color"`
MarkersColor string MarkersColor string `json:"markers_color"`
FirstColor string FirstColor string `json:"first_color"`
LastColor string LastColor string `json:"last_color"`
HightColor string HightColor string `json:"hight_color"`
LowColor string LowColor string `json:"low_color"`
EmptyCells string EmptyCells string `json:"empty_cells"`
} }
// formatPanes directly maps the settings of the panes. // panesOptions directly maps the settings of the panes.
type formatPanes struct { type panesOptions struct {
Freeze bool `json:"freeze"` Freeze bool `json:"freeze"`
Split bool `json:"split"` Split bool `json:"split"`
XSplit int `json:"x_split"` XSplit int `json:"x_split"`
@ -823,8 +827,8 @@ type formatPanes struct {
} `json:"panes"` } `json:"panes"`
} }
// formatConditional directly maps the conditional format settings of the cells. // conditionalOptions directly maps the conditional format settings of the cells.
type formatConditional struct { type conditionalOptions struct {
Type string `json:"type"` Type string `json:"type"`
AboveAverage bool `json:"above_average,omitempty"` AboveAverage bool `json:"above_average,omitempty"`
Percent bool `json:"percent,omitempty"` Percent bool `json:"percent,omitempty"`
@ -848,47 +852,163 @@ type formatConditional struct {
BarColor string `json:"bar_color,omitempty"` BarColor string `json:"bar_color,omitempty"`
} }
// FormatSheetProtection directly maps the settings of worksheet protection. // SheetProtectionOptions directly maps the settings of worksheet protection.
type FormatSheetProtection struct { type SheetProtectionOptions struct {
AlgorithmName string AlgorithmName string `json:"algorithm_name,omitempty"`
AutoFilter bool AutoFilter bool `json:"auto_filter,omitempty"`
DeleteColumns bool DeleteColumns bool `json:"delete_columns,omitempty"`
DeleteRows bool DeleteRows bool `json:"delete_rows,omitempty"`
EditObjects bool EditObjects bool `json:"edit_objects,omitempty"`
EditScenarios bool EditScenarios bool `json:"edit_scenarios,omitempty"`
FormatCells bool FormatCells bool `json:"format_cells,omitempty"`
FormatColumns bool FormatColumns bool `json:"format_columns,omitempty"`
FormatRows bool FormatRows bool `json:"format_rows,omitempty"`
InsertColumns bool InsertColumns bool `json:"insert_columns,omitempty"`
InsertHyperlinks bool InsertHyperlinks bool `json:"insert_hyperlinks,omitempty"`
InsertRows bool InsertRows bool `json:"insert_rows,omitempty"`
Password string Password string `json:"password,omitempty"`
PivotTables bool PivotTables bool `json:"pivot_tables,omitempty"`
SelectLockedCells bool SelectLockedCells bool `json:"select_locked_cells,omitempty"`
SelectUnlockedCells bool SelectUnlockedCells bool `json:"select_unlocked_cells,omitempty"`
Sort bool Sort bool `json:"sort,omitempty"`
} }
// FormatHeaderFooter directly maps the settings of header and footer. // HeaderFooterOptions directly maps the settings of header and footer.
type FormatHeaderFooter struct { type HeaderFooterOptions struct {
AlignWithMargins bool AlignWithMargins bool `json:"align_with_margins,omitempty"`
DifferentFirst bool DifferentFirst bool `json:"different_first,omitempty"`
DifferentOddEven bool DifferentOddEven bool `json:"different_odd_even,omitempty"`
ScaleWithDoc bool ScaleWithDoc bool `json:"scale_with_doc,omitempty"`
OddHeader string OddHeader string `json:"odd_header,omitempty"`
OddFooter string OddFooter string `json:"odd_footer,omitempty"`
EvenHeader string EvenHeader string `json:"even_header,omitempty"`
EvenFooter string EvenFooter string `json:"even_footer,omitempty"`
FirstHeader string FirstHeader string `json:"first_header,omitempty"`
FirstFooter string FirstFooter string `json:"first_footer,omitempty"`
} }
// FormatPageMargins directly maps the settings of page margins // PageLayoutMarginsOptions directly maps the settings of page layout margins.
type FormatPageMargins struct { type PageLayoutMarginsOptions struct {
Bottom string Bottom *float64 `json:"bottom,omitempty"`
Footer string Footer *float64 `json:"footer,omitempty"`
Header string Header *float64 `json:"header,omitempty"`
Left string Left *float64 `json:"left,omitempty"`
Right string Right *float64 `json:"right,omitempty"`
Top string Top *float64 `json:"top,omitempty"`
Horizontally *bool `json:"horizontally,omitempty"`
Vertically *bool `json:"vertically,omitempty"`
}
// PageLayoutOptions directly maps the settings of page layout.
type PageLayoutOptions struct {
// Size defines the paper size of the worksheet.
Size *int `json:"size,omitempty"`
// Orientation defines the orientation of page layout for a worksheet.
Orientation *string `json:"orientation,omitempty"`
// FirstPageNumber specified the first printed page number. If no value is
// specified, then 'automatic' is assumed.
FirstPageNumber *uint `json:"first_page_number,omitempty"`
// AdjustTo defines the print scaling. This attribute is restricted to
// value ranging from 10 (10%) to 400 (400%). This setting is overridden
// when fitToWidth and/or fitToHeight are in use.
AdjustTo *uint `json:"adjust_to,omitempty"`
// FitToHeight specified the number of vertical pages to fit on.
FitToHeight *int `json:"fit_to_height,omitempty"`
// FitToWidth specified the number of horizontal pages to fit on.
FitToWidth *int `json:"fit_to_width,omitempty"`
// BlackAndWhite specified print black and white.
BlackAndWhite *bool `json:"black_and_white,omitempty"`
}
// ViewOptions directly maps the settings of sheet view.
type ViewOptions struct {
// DefaultGridColor indicating that the consuming application should use
// the default grid lines color(system dependent). Overrides any color
// specified in colorId.
DefaultGridColor *bool `json:"default_grid_color,omitempty"`
// RightToLeft indicating whether the sheet is in 'right to left' display
// mode. When in this mode, Column A is on the far right, Column B; is one
// column left of Column A, and so on. Also, information in cells is
// displayed in the Right to Left format.
RightToLeft *bool `json:"right_to_left,omitempty"`
// ShowFormulas indicating whether this sheet should display formulas.
ShowFormulas *bool `json:"show_formulas,omitempty"`
// ShowGridLines indicating whether this sheet should display grid lines.
ShowGridLines *bool `json:"show_grid_lines,omitempty"`
// ShowRowColHeaders indicating whether the sheet should display row and
// column headings.
ShowRowColHeaders *bool `json:"show_row_col_headers,omitempty"`
// ShowRuler indicating this sheet should display ruler.
ShowRuler *bool `json:"show_ruler,omitempty"`
// ShowZeros indicating whether to "show a zero in cells that have zero
// value". When using a formula to reference another cell which is empty,
// the referenced value becomes 0 when the flag is true. (Default setting
// is true.)
ShowZeros *bool `json:"show_zeros,omitempty"`
// TopLeftCell specifies a location of the top left visible cell Location
// of the top left visible cell in the bottom right pane (when in
// Left-to-Right mode).
TopLeftCell *string `json:"top_left_cell,omitempty"`
// View indicating how sheet is displayed, by default it uses empty string
// available options: normal, pageLayout, pageBreakPreview
View *string `json:"low_color,omitempty"`
// ZoomScale specifies a window zoom magnification for current view
// representing percent values. This attribute is restricted to values
// ranging from 10 to 400. Horizontal & Vertical scale together.
ZoomScale *float64 `json:"zoom_scale,omitempty"`
}
// SheetPropsOptions directly maps the settings of sheet view.
type SheetPropsOptions struct {
// Specifies a stable name of the sheet, which should not change over time,
// and does not change from user input. This name should be used by code
// to reference a particular sheet.
CodeName *string `json:"code_name,omitempty"`
// EnableFormatConditionsCalculation indicating whether the conditional
// formatting calculations shall be evaluated. If set to false, then the
// min/max values of color scales or data bars or threshold values in Top N
// rules shall not be updated. Essentially the conditional
// formatting "calc" is off.
EnableFormatConditionsCalculation *bool `json:"enable_format_conditions_calculation,omitempty"`
// Published indicating whether the worksheet is published.
Published *bool `json:"published,omitempty"`
// AutoPageBreaks indicating whether the sheet displays Automatic Page
// Breaks.
AutoPageBreaks *bool `json:"auto_page_breaks,omitempty"`
// FitToPage indicating whether the Fit to Page print option is enabled.
FitToPage *bool `json:"fit_to_page,omitempty"`
// TabColorIndexed represents the indexed color value.
TabColorIndexed *int `json:"tab_color_indexed,omitempty"`
// TabColorRGB represents the standard Alpha Red Green Blue color value.
TabColorRGB *string `json:"tab_color_rgb,omitempty"`
// TabColorTheme represents the zero-based index into the collection,
// referencing a particular value expressed in the Theme part.
TabColorTheme *int `json:"tab_color_theme,omitempty"`
// TabColorTint specifies the tint value applied to the color.
TabColorTint *float64 `json:"tab_color_tint,omitempty"`
// OutlineSummaryBelow indicating whether summary rows appear below detail
// in an outline, when applying an outline.
OutlineSummaryBelow *bool `json:"outline_summary_below,omitempty"`
// BaseColWidth specifies the number of characters of the maximum digit
// width of the normal style's font. This value does not include margin
// padding or extra padding for grid lines. It is only the number of
// characters.
BaseColWidth *uint8 `json:"base_col_width,omitempty"`
// DefaultColWidth specifies the default column width measured as the
// number of characters of the maximum digit width of the normal style's
// font.
DefaultColWidth *float64 `json:"default_col_width,omitempty"`
// DefaultRowHeight specifies the default row height measured in point
// size. Optimization so we don't have to write the height on all rows.
// This can be written out if most rows have custom height, to achieve the
// optimization.
DefaultRowHeight *float64 `json:"default_row_height,omitempty"`
// CustomHeight specifies the custom height.
CustomHeight *bool `json:"custom_height,omitempty"`
// ZeroHeight specifies if rows are hidden.
ZeroHeight *bool `json:"zero_height,omitempty"`
// ThickTop specifies if rows have a thick top border by default.
ThickTop *bool `json:"thick_top,omitempty"`
// ThickBottom specifies if rows have a thick bottom border by default.
ThickBottom *bool `json:"thick_bottom,omitempty"`
} }