This closes #1993, support to set and get pivot table classic layout

- Add new field `ClassicLayout` in the `PivotTableOptions`
- Add a new exported error variable `ErrPivotTableClassicLayout`
- Update unit tests
- Add documentation for the SetDefinedName function, ref #1015
This commit is contained in:
xuri 2024-09-21 15:39:36 +08:00
parent 02189fb016
commit 41c7dd30ce
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
5 changed files with 73 additions and 16 deletions

View File

@ -2126,7 +2126,7 @@ func TestCalcCellValue(t *testing.T) {
"=DOLLAR(1234.56,-2)": "$1,200",
"=DOLLAR(1234.56,-3)": "$1,000",
"=DOLLAR(-1234.56,3)": "($1,234.560)",
"=DOLLAR(-1234.56,-3)": "($1,000)",
"=DOLLAR(-1234.56,-3)": "($1,000)",
// DOLLARDE
"=DOLLARDE(1.01,16)": "1.0625",
// DOLLARFR

View File

@ -97,6 +97,8 @@ var (
// ErrPasswordLengthInvalid defined the error message on invalid password
// length.
ErrPasswordLengthInvalid = errors.New("password length invalid")
// ErrPivotTableClassicLayout
ErrPivotTableClassicLayout = errors.New("cannot enable ClassicLayout and CompactData in the same time")
// ErrSave defined the error message for saving file.
ErrSave = errors.New("no path defined for file, consider File.WriteTo or File.Write")
// ErrSheetIdx defined the error message on receive the invalid worksheet

View File

@ -51,6 +51,7 @@ type PivotTableOptions struct {
UseAutoFormatting bool
PageOverThenDown bool
MergeItem bool
ClassicLayout bool
CompactData bool
ShowError bool
ShowRowHeaders bool
@ -220,6 +221,9 @@ func (f *File) parseFormatPivotTableSet(opts *PivotTableOptions) (*xlsxWorksheet
if !ok {
return dataSheet, pivotTableSheetPath, ErrSheetNotExist{pivotTableSheetName}
}
if opts.CompactData && opts.ClassicLayout {
return nil, "", ErrPivotTableClassicLayout
}
return dataSheet, pivotTableSheetPath, err
}
@ -352,6 +356,7 @@ func (f *File) addPivotTable(cacheID, pivotTableID int, opts *PivotTableOptions)
MergeItem: &opts.MergeItem,
CreatedVersion: pivotTableVersion,
CompactData: &opts.CompactData,
GridDropZones: opts.ClassicLayout,
ShowError: &opts.ShowError,
FieldPrintTitles: opts.FieldPrintTitles,
ItemPrintTitles: opts.ItemPrintTitles,
@ -387,6 +392,12 @@ func (f *File) addPivotTable(cacheID, pivotTableID int, opts *PivotTableOptions)
if pt.Name == "" {
pt.Name = fmt.Sprintf("PivotTable%d", pivotTableID)
}
// set classic layout
if opts.ClassicLayout {
pt.Compact, pt.CompactData = boolPtr(false), boolPtr(false)
}
// pivot fields
_ = f.addPivotFields(&pt, opts)
@ -537,6 +548,14 @@ func (f *File) addPivotColFields(pt *xlsxPivotTableDefinition, opts *PivotTableO
return err
}
// setClassicLayout provides a method to set classic layout for pivot table by
// setting Compact and Outline to false.
func (fld *xlsxPivotField) setClassicLayout(classicLayout bool) {
if classicLayout {
fld.Compact, fld.Outline = boolPtr(false), boolPtr(false)
}
}
// addPivotFields create pivot fields based on the column order of the first
// row in the data region by given pivot table definition and option.
func (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opts *PivotTableOptions) error {
@ -554,8 +573,7 @@ func (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opts *PivotTableOpti
} else {
items = append(items, &xlsxItem{T: "default"})
}
pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, &xlsxPivotField{
fld := &xlsxPivotField{
Name: f.getPivotTableFieldName(name, opts.Rows),
Axis: "axisRow",
DataField: inPivotTableField(opts.Data, name) != -1,
@ -568,11 +586,13 @@ func (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opts *PivotTableOpti
Count: len(items),
Item: items,
},
})
}
fld.setClassicLayout(opts.ClassicLayout)
pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, fld)
continue
}
if inPivotTableField(opts.Filter, name) != -1 {
pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, &xlsxPivotField{
fld := &xlsxPivotField{
Axis: "axisPage",
DataField: inPivotTableField(opts.Data, name) != -1,
Name: f.getPivotTableFieldName(name, opts.Columns),
@ -582,7 +602,9 @@ func (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opts *PivotTableOpti
{T: "default"},
},
},
})
}
fld.setClassicLayout(opts.ClassicLayout)
pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, fld)
continue
}
if inPivotTableField(opts.Columns, name) != -1 {
@ -593,7 +615,7 @@ func (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opts *PivotTableOpti
} else {
items = append(items, &xlsxItem{T: "default"})
}
pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, &xlsxPivotField{
fld := &xlsxPivotField{
Name: f.getPivotTableFieldName(name, opts.Columns),
Axis: "axisCol",
DataField: inPivotTableField(opts.Data, name) != -1,
@ -606,16 +628,22 @@ func (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opts *PivotTableOpti
Count: len(items),
Item: items,
},
})
}
fld.setClassicLayout(opts.ClassicLayout)
pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, fld)
continue
}
if inPivotTableField(opts.Data, name) != -1 {
pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, &xlsxPivotField{
fld := &xlsxPivotField{
DataField: true,
})
}
fld.setClassicLayout(opts.ClassicLayout)
pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, fld)
continue
}
pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, &xlsxPivotField{})
fld := &xlsxPivotField{}
fld.setClassicLayout(opts.ClassicLayout)
pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, fld)
}
return err
}
@ -847,6 +875,7 @@ func (f *File) getPivotTable(sheet, pivotTableXML, pivotCacheRels string) (Pivot
DataRange: fmt.Sprintf("%s!%s", pc.CacheSource.WorksheetSource.Sheet, pc.CacheSource.WorksheetSource.Ref),
PivotTableRange: fmt.Sprintf("%s!%s", sheet, pt.Location.Ref),
Name: pt.Name,
ClassicLayout: pt.GridDropZones,
FieldPrintTitles: pt.FieldPrintTitles,
ItemPrintTitles: pt.ItemPrintTitles,
}

View File

@ -38,12 +38,13 @@ func TestPivotTable(t *testing.T) {
RowGrandTotals: true,
ColGrandTotals: true,
ShowDrill: true,
ClassicLayout: true,
ShowError: true,
ShowRowHeaders: true,
ShowColHeaders: true,
ShowLastColumn: true,
ShowError: true,
ItemPrintTitles: true,
FieldPrintTitles: true,
ItemPrintTitles: true,
PivotTableStyleName: "PivotStyleLight16",
}
assert.NoError(t, f.AddPivotTable(expected))
@ -265,18 +266,25 @@ func TestPivotTable(t *testing.T) {
assert.NoError(t, err)
// Test add pivot table with invalid sheet name
assert.EqualError(t, f.AddPivotTable(&PivotTableOptions{
assert.Error(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet:1!A1:E31",
PivotTableRange: "Sheet:1!G2:M34",
Rows: []PivotTableField{{Data: "Year"}},
}), ErrSheetNameInvalid.Error())
}), ErrSheetNameInvalid)
// Test add pivot table with enable ClassicLayout and CompactData in the same time
assert.Error(t, f.AddPivotTable(&PivotTableOptions{
DataRange: "Sheet1!A1:E31",
PivotTableRange: "Sheet1!G2:M34",
CompactData: true,
ClassicLayout: true,
}), ErrPivotTableClassicLayout)
// Test delete pivot table with not exists worksheet
assert.EqualError(t, f.DeletePivotTable("SheetN", "PivotTable1"), "sheet SheetN does not exist")
// Test delete pivot table with not exists pivot table name
assert.EqualError(t, f.DeletePivotTable("Sheet1", "PivotTableN"), "table PivotTableN does not exist")
// Test adjust range with invalid range
_, _, err = f.adjustRange("")
assert.EqualError(t, err, ErrParameterRequired.Error())
assert.Error(t, err, ErrParameterRequired)
// Test adjust range with incorrect range
_, _, err = f.adjustRange("sheet1!")
assert.EqualError(t, err, "parameter is invalid")

View File

@ -1657,6 +1657,24 @@ func (f *File) GetPageLayout(sheet string) (PageLayoutOptions, error) {
// Comment: "defined name comment",
// Scope: "Sheet2",
// })
//
// If you fill the RefersTo property with only one columns range without a
// comma, it will work as "Columns to repeat at left" only. For example:
//
// err := f.SetDefinedName(&excelize.DefinedName{
// Name: "_xlnm.Print_Titles",
// RefersTo: "Sheet1!$A:$A",
// Scope: "Sheet1",
// })
//
// If you fill the RefersTo property with only one rows range without a comma,
// it will work as "Rows to repeat at top" only. For example:
//
// err := f.SetDefinedName(&excelize.DefinedName{
// Name: "_xlnm.Print_Titles",
// RefersTo: "Sheet1!$1:$1",
// Scope: "Sheet1",
// })
func (f *File) SetDefinedName(definedName *DefinedName) error {
if definedName.Name == "" || definedName.RefersTo == "" {
return ErrParameterInvalid