Skip to content

Commit

Permalink
Introduce the new exported function AddSlicer for adding table slicer
Browse files Browse the repository at this point in the history
- Fix a v2.8.0 regression bug, generate workbook corruption caused by incorrect MRU colors style parts
- Fix corrupted workbooks generated when adding tables in some cases
- Added several exported extension list child element URI constants
- Move part of the internal constant and variables definition to the template source code file
- Updated unit tests
  • Loading branch information
xuri committed Sep 16, 2023
1 parent 5a039f3 commit e3b7dad
Show file tree
Hide file tree
Showing 25 changed files with 1,464 additions and 340 deletions.
4 changes: 2 additions & 2 deletions chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -499,10 +499,10 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
opts.Format.Locked = boolPtr(false)
}
if opts.Format.ScaleX == 0 {
opts.Format.ScaleX = defaultPictureScale
opts.Format.ScaleX = defaultDrawingScale
}
if opts.Format.ScaleY == 0 {
opts.Format.ScaleY = defaultPictureScale
opts.Format.ScaleY = defaultDrawingScale
}
if opts.Legend.Position == "" {
opts.Legend.Position = defaultChartLegendPosition
Expand Down
8 changes: 4 additions & 4 deletions chart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,8 @@ func TestAddChart(t *testing.T) {
{Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37", Sizes: "Sheet1!$B$37:$D$37"},
}
format := GraphicOptions{
ScaleX: defaultPictureScale,
ScaleY: defaultPictureScale,
ScaleX: defaultDrawingScale,
ScaleY: defaultDrawingScale,
OffsetX: 15,
OffsetY: 10,
PrintObject: boolPtr(true),
Expand Down Expand Up @@ -369,8 +369,8 @@ func TestDeleteChart(t *testing.T) {
{Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37"},
}
format := GraphicOptions{
ScaleX: defaultPictureScale,
ScaleY: defaultPictureScale,
ScaleX: defaultDrawingScale,
ScaleY: defaultDrawingScale,
OffsetX: 15,
OffsetY: 10,
PrintObject: boolPtr(true),
Expand Down
2 changes: 1 addition & 1 deletion drawing.go
Original file line number Diff line number Diff line change
Expand Up @@ -1356,7 +1356,7 @@ func (f *File) addSheetDrawingChart(drawingXML string, rID int, opts *GraphicOpt
absoluteAnchor := xdrCellAnchor{
EditAs: opts.Positioning,
Pos: &xlsxPoint2D{},
Ext: &xlsxExt{},
Ext: &aExt{},
}

graphicFrame := xlsxGraphicFrame{
Expand Down
6 changes: 6 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ func newInvalidCellNameError(cell string) error {
return fmt.Errorf("invalid cell name %q", cell)
}

// newInvalidSlicerNameError defined the error message on receiving the invalid
// slicer name.
func newInvalidSlicerNameError(name string) error {
return fmt.Errorf("invalid slicer name %q", name)
}

// newInvalidExcelDateError defined the error message on receiving the data
// with negative values.
func newInvalidExcelDateError(dateValue float64) error {
Expand Down
42 changes: 35 additions & 7 deletions lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ func (f *File) coordinatesToRangeRef(coordinates []int, abs ...bool) (string, er
}

// getDefinedNameRefTo convert defined name to reference range.
func (f *File) getDefinedNameRefTo(definedNameName string, currentSheet string) (refTo string) {
func (f *File) getDefinedNameRefTo(definedNameName, currentSheet string) (refTo string) {
var workbookRefTo, worksheetRefTo string
for _, definedName := range f.GetDefinedName() {
if definedName.Name == definedNameName {
Expand Down Expand Up @@ -431,17 +431,17 @@ func float64Ptr(f float64) *float64 { return &f }
func stringPtr(s string) *string { return &s }

// Value extracts string data type text from a attribute value.
func (attr *attrValString) Value() string {
if attr != nil && attr.Val != nil {
return *attr.Val
func (avb *attrValString) Value() string {
if avb != nil && avb.Val != nil {
return *avb.Val
}
return ""
}

// Value extracts boolean data type value from a attribute value.
func (attr *attrValBool) Value() bool {
if attr != nil && attr.Val != nil {
return *attr.Val
func (avb *attrValBool) Value() bool {
if avb != nil && avb.Val != nil {
return *avb.Val
}
return false
}
Expand Down Expand Up @@ -517,6 +517,34 @@ func (avb *attrValBool) UnmarshalXML(d *xml.Decoder, start xml.StartElement) err
return nil
}

// MarshalXML encodes ext element with specified namespace attributes on
// serialization.
func (ext xlsxExt) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
start.Attr = ext.xmlns
return e.EncodeElement(decodeExt{URI: ext.URI, Content: ext.Content}, start)
}

// UnmarshalXML extracts ext element attributes namespace by giving XML decoder
// on deserialization.
func (ext *xlsxExt) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
for _, attr := range start.Attr {
if attr.Name.Local == "uri" {
continue
}
if attr.Name.Space == "xmlns" {
attr.Name.Space = ""
attr.Name.Local = "xmlns:" + attr.Name.Local
}
ext.xmlns = append(ext.xmlns, attr)
}
e := &decodeExt{}
if err := d.DecodeElement(&e, &start); err != nil {
return err
}
ext.URI, ext.Content = e.URI, e.Content
return nil
}

// namespaceStrictToTransitional provides a method to convert Strict and
// Transitional namespaces.
func namespaceStrictToTransitional(content []byte) []byte {
Expand Down
9 changes: 9 additions & 0 deletions lib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,15 @@ func TestBoolValUnmarshalXML(t *testing.T) {
assert.EqualError(t, attr.UnmarshalXML(xml.NewDecoder(strings.NewReader("")), xml.StartElement{}), io.EOF.Error())
}

func TestExtUnmarshalXML(t *testing.T) {
f, extLst := NewFile(), decodeExtLst{}
expected := fmt.Sprintf(`<extLst><ext uri="%s" xmlns:x14="%s"/></extLst>`,
ExtURISlicerCachesX14, NameSpaceSpreadSheetX14.Value)
assert.NoError(t, f.xmlNewDecoder(strings.NewReader(expected)).Decode(&extLst))
assert.Len(t, extLst.Ext, 1)
assert.Equal(t, extLst.Ext[0].URI, ExtURISlicerCachesX14)
}

func TestBytesReplace(t *testing.T) {
s := []byte{0x01}
assert.EqualValues(t, s, bytesReplace(s, []byte{}, []byte{}, 0))
Expand Down
32 changes: 27 additions & 5 deletions picture.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ func parseGraphicOptions(opts *GraphicOptions) *GraphicOptions {
return &GraphicOptions{
PrintObject: boolPtr(true),
Locked: boolPtr(true),
ScaleX: defaultPictureScale,
ScaleY: defaultPictureScale,
ScaleX: defaultDrawingScale,
ScaleY: defaultDrawingScale,
}
}
if opts.PrintObject == nil {
Expand All @@ -41,10 +41,10 @@ func parseGraphicOptions(opts *GraphicOptions) *GraphicOptions {
opts.Locked = boolPtr(true)
}
if opts.ScaleX == 0 {
opts.ScaleX = defaultPictureScale
opts.ScaleX = defaultDrawingScale
}
if opts.ScaleY == 0 {
opts.ScaleY = defaultPictureScale
opts.ScaleY = defaultDrawingScale
}
return opts
}
Expand Down Expand Up @@ -440,6 +440,28 @@ func (f *File) addMedia(file []byte, ext string) string {
return media
}

// setContentTypePartRelsExtensions provides a function to set the content
// type for relationship parts and the Main Document part.
func (f *File) setContentTypePartRelsExtensions() error {
var rels bool
content, err := f.contentTypesReader()
if err != nil {
return err
}
for _, v := range content.Defaults {
if v.Extension == "rels" {
rels = true
}
}
if !rels {
content.Defaults = append(content.Defaults, xlsxDefault{
Extension: "rels",
ContentType: ContentTypeRelationships,
})
}
return err
}

// setContentTypePartImageExtensions provides a function to set the content
// type for relationship parts and the Main Document part.
func (f *File) setContentTypePartImageExtensions() error {
Expand Down Expand Up @@ -542,7 +564,7 @@ func (f *File) addContentTypePart(index int, contentType string) error {
PartName: partNames[contentType],
ContentType: contentTypes[contentType],
})
return err
return f.setContentTypePartRelsExtensions()
}

// getSheetRelationshipsTargetByID provides a function to get Target attribute
Expand Down
11 changes: 11 additions & 0 deletions picture_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,17 @@ func TestDrawingResize(t *testing.T) {
assert.EqualError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.jpg"), &GraphicOptions{AutoFit: true}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
}

func TestSetContentTypePartRelsExtensions(t *testing.T) {
f := NewFile()
f.ContentTypes = &xlsxTypes{}
assert.NoError(t, f.setContentTypePartRelsExtensions())

// Test set content type part relationships extensions with unsupported charset content types
f.ContentTypes = nil
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
assert.EqualError(t, f.setContentTypePartRelsExtensions(), "XML syntax error on line 1: invalid UTF-8")
}

func TestSetContentTypePartImageExtensions(t *testing.T) {
f := NewFile()
// Test set content type part image extensions with unsupported charset content types
Expand Down
26 changes: 13 additions & 13 deletions pivotTable.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ type PivotTableField struct {
// options. Note that the same fields can not in Columns, Rows and Filter
// fields at the same time.
//
// For example, create a pivot table on the range reference Sheet1!$G$2:$M$34
// with the range reference Sheet1!$A$1:$E$31 as the data source, summarize by
// sum for sales:
// For example, create a pivot table on the range reference Sheet1!G2:M34 with
// the range reference Sheet1!A1:E31 as the data source, summarize by sum for
// sales:
//
// package main
//
Expand Down Expand Up @@ -242,15 +242,15 @@ func (f *File) adjustRange(rangeStr string) (string, []int, error) {
return rng[0], []int{x1, y1, x2, y2}, nil
}

// getPivotFieldsOrder provides a function to get order list of pivot table
// getTableFieldsOrder provides a function to get order list of pivot table
// fields.
func (f *File) getPivotFieldsOrder(opts *PivotTableOptions) ([]string, error) {
func (f *File) getTableFieldsOrder(sheetName, dataRange string) ([]string, error) {
var order []string
dataRange := f.getDefinedNameRefTo(opts.DataRange, opts.pivotTableSheetName)
if dataRange == "" {
dataRange = opts.DataRange
ref := f.getDefinedNameRefTo(dataRange, sheetName)
if ref == "" {
ref = dataRange
}
dataSheet, coordinates, err := f.adjustRange(dataRange)
dataSheet, coordinates, err := f.adjustRange(ref)
if err != nil {
return order, fmt.Errorf("parameter 'DataRange' parsing error: %s", err.Error())
}
Expand Down Expand Up @@ -279,7 +279,7 @@ func (f *File) addPivotCache(pivotCacheXML string, opts *PivotTableOptions) erro
return fmt.Errorf("parameter 'DataRange' parsing error: %s", err.Error())
}
// data range has been checked
order, _ := f.getPivotFieldsOrder(opts)
order, _ := f.getTableFieldsOrder(opts.pivotTableSheetName, opts.DataRange)
hCell, _ := CoordinatesToCellName(coordinates[0], coordinates[1])
vCell, _ := CoordinatesToCellName(coordinates[2], coordinates[3])
pc := xlsxPivotCacheDefinition{
Expand Down Expand Up @@ -541,7 +541,7 @@ func (f *File) addPivotColFields(pt *xlsxPivotTableDefinition, opts *PivotTableO
// 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 {
order, err := f.getPivotFieldsOrder(opts)
order, err := f.getTableFieldsOrder(opts.pivotTableSheetName, opts.DataRange)
if err != nil {
return err
}
Expand Down Expand Up @@ -647,7 +647,7 @@ func (f *File) countPivotCache() int {
// to a sequential index by given fields and pivot option.
func (f *File) getPivotFieldsIndex(fields []PivotTableField, opts *PivotTableOptions) ([]int, error) {
var pivotFieldsIndex []int
orders, err := f.getPivotFieldsOrder(opts)
orders, err := f.getTableFieldsOrder(opts.pivotTableSheetName, opts.DataRange)
if err != nil {
return pivotFieldsIndex, err
}
Expand Down Expand Up @@ -809,7 +809,7 @@ func (f *File) getPivotTable(sheet, pivotTableXML, pivotCacheRels string) (Pivot
opts.ShowLastColumn = si.ShowLastColumn
opts.PivotTableStyleName = si.Name
}
order, _ := f.getPivotFieldsOrder(&PivotTableOptions{DataRange: dataRange, pivotTableSheetName: pt.Name})
order, _ := f.getTableFieldsOrder(pt.Name, dataRange)
f.extractPivotTableFields(order, pt, &opts)
return opts, err
}
Expand Down
8 changes: 4 additions & 4 deletions pivotTable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,8 @@ func TestPivotTable(t *testing.T) {
// Test adjust range with incorrect range
_, _, err = f.adjustRange("sheet1!")
assert.EqualError(t, err, "parameter is invalid")
// Test get pivot fields order with empty data range
_, err = f.getPivotFieldsOrder(&PivotTableOptions{})
// Test get table fields order with empty data range
_, err = f.getTableFieldsOrder("", "")
assert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`)
// Test add pivot cache with empty data range
assert.EqualError(t, f.addPivotCache("", &PivotTableOptions{}), "parameter 'DataRange' parsing error: parameter is required")
Expand Down Expand Up @@ -367,8 +367,8 @@ func TestAddPivotColFields(t *testing.T) {

func TestGetPivotFieldsOrder(t *testing.T) {
f := NewFile()
// Test get pivot fields order with not exist worksheet
_, err := f.getPivotFieldsOrder(&PivotTableOptions{DataRange: "SheetN!A1:E31"})
// Test get table fields order with not exist worksheet
_, err := f.getTableFieldsOrder("", "SheetN!A1:E31")
assert.EqualError(t, err, "sheet SheetN does not exist")
}

Expand Down
4 changes: 2 additions & 2 deletions rows.go
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ func (f *File) duplicateMergeCells(sheet string, ws *xlsxWorksheet, row, row2 in
// checkRow provides a function to check and fill each column element for all
// rows and make that is continuous in a worksheet of XML. For example:
//
// <row r="15" spans="1:22" x14ac:dyDescent="0.2">
// <row r="15">
// <c r="A15" s="2" />
// <c r="B15" s="2" />
// <c r="F15" s="1" />
Expand All @@ -717,7 +717,7 @@ func (f *File) duplicateMergeCells(sheet string, ws *xlsxWorksheet, row, row2 in
//
// in this case, we should to change it to
//
// <row r="15" spans="1:22" x14ac:dyDescent="0.2">
// <row r="15">
// <c r="A15" s="2" />
// <c r="B15" s="2" />
// <c r="C15" s="2" />
Expand Down
Loading

0 comments on commit e3b7dad

Please sign in to comment.