Skip to content

Commit

Permalink
Merge pull request #798 from martoche/fix-libreoffice-autofilter
Browse files Browse the repository at this point in the history
fix AutoFilter not working in LibreOffice
  • Loading branch information
tealeg authored Apr 4, 2024
2 parents e1849b7 + ea15a71 commit 0618e1b
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 1 deletion.
51 changes: 51 additions & 0 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,41 @@ func addRelationshipNameSpaceToWorksheet(worksheetMarshal string) string {
return newSheetMarshall
}

func cellIDStringWithFixed(cellIDString string) string {
letterPart := strings.Map(letterOnlyMapF, cellIDString)
intPart := strings.Map(intOnlyMapF, cellIDString)

if letterPart != "" && intPart == "" {
return fixedCellRefChar + letterPart
} else if letterPart != "" && intPart != "" {
return fixedCellRefChar + letterPart + fixedCellRefChar + intPart
}

return ""
}

// AutoFilter doesn't work in LibreOffice unless a special "FilterDatabase" tag
// is present in the "DefinedNames" array. See:
// - https://github.com/SheetJS/sheetjs/issues/1165
// - https://bugs.documentfoundation.org/show_bug.cgi?id=118592
func autoFilterDefinedName(sheet *Sheet, sheetIndex int) (*xlsxDefinedName, error) {
if sheet.AutoFilter == nil {
return nil, nil
}

return &xlsxDefinedName{
Data: fmt.Sprintf(
"'%s'!%v:%v",
strings.ReplaceAll(sheet.Name, "'", "''"),
cellIDStringWithFixed(sheet.AutoFilter.TopLeftCell),
cellIDStringWithFixed(sheet.AutoFilter.BottomRightCell),
),
Name: "_xlnm._FilterDatabase",
LocalSheetID: sheetIndex - 1,
Hidden: true,
}, nil
}

// MakeStreamParts constructs a map of file name to XML content
// representing the file in terms of the structure of an XLSX file.
func (f *File) MakeStreamParts() (map[string]string, error) {
Expand Down Expand Up @@ -370,6 +405,14 @@ func (f *File) MakeStreamParts() (map[string]string, error) {
return parts, err
}
}

definedName, err := autoFilterDefinedName(sheet, sheetIndex)
if err != nil {
return parts, err
} else if definedName != nil {
workbook.DefinedNames.DefinedName = append(workbook.DefinedNames.DefinedName, *definedName)
}

sheetIndex++
}

Expand Down Expand Up @@ -511,6 +554,14 @@ func (f *File) MarshallParts(zipWriter *zip.Writer) error {
return wrap(err)
}
}

definedName, err := autoFilterDefinedName(sheet, sheetIndex)
if err != nil {
return wrap(err)
} else if definedName != nil {
workbook.DefinedNames.DefinedName = append(workbook.DefinedNames.DefinedName, *definedName)
}

sheetIndex++
}

Expand Down
19 changes: 19 additions & 0 deletions file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,25 @@ func TestFile(t *testing.T) {
c.Assert(s.Hidden, qt.Equals, true)
})

csRunO(c, "TestMarshalFileWithAutoFilter", func(c *qt.C, option FileOption) {
var f *File
f = NewFile(option)
sheet1, _ := f.AddSheet("MySheet")
sheet1.AutoFilter = &AutoFilter{
TopLeftCell: "A1",
BottomRightCell: "D",
}

row1 := sheet1.AddRow()
cell1 := row1.AddCell()
cell1.SetString("A cell!")

parts, err := f.MakeStreamParts()
c.Assert(err, qt.IsNil)
c.Assert(parts["xl/workbook.xml"], qt.Contains, `<definedNames><definedName name="_xlnm._FilterDatabase" localSheetId="0" hidden="true">&#39;MySheet&#39;!$A$1:$D</definedName></definedNames>`)
c.Assert(parts["xl/worksheets/sheet1.xml"], qt.Contains, `<autoFilter ref="A1:D"></autoFilter>`)
})

// We can save a File as a valid XLSX file at a given path.
csRunO(c, "TestSaveFileWithHyperlinks", func(c *qt.C, option FileOption) {
tmpPath, err := ioutil.TempDir("", "testsavefilewithhyperlinks")
Expand Down
2 changes: 1 addition & 1 deletion xmlWorkbook.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ type xlsxDefinedName struct {
Help string `xml:"help,attr,omitempty"`
ShortcutKey string `xml:"shortcutKey,attr,omitempty"`
StatusBar string `xml:"statusBar,attr,omitempty"`
LocalSheetID int `xml:"localSheetId,attr,omitempty"`
LocalSheetID int `xml:"localSheetId,attr"`
FunctionGroupID int `xml:"functionGroupId,attr,omitempty"`
Function bool `xml:"function,attr,omitempty"`
Hidden bool `xml:"hidden,attr,omitempty"`
Expand Down

0 comments on commit 0618e1b

Please sign in to comment.