diff --git a/file.go b/file.go
index 1d34a478..9733d421 100644
--- a/file.go
+++ b/file.go
@@ -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) {
@@ -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++
}
@@ -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++
}
diff --git a/file_test.go b/file_test.go
index 14ec2687..532021f4 100644
--- a/file_test.go
+++ b/file_test.go
@@ -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, `'MySheet'!$A$1:$D`)
+ c.Assert(parts["xl/worksheets/sheet1.xml"], qt.Contains, ``)
+ })
+
// 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")
diff --git a/xmlWorkbook.go b/xmlWorkbook.go
index 18e743fa..6ad59683 100644
--- a/xmlWorkbook.go
+++ b/xmlWorkbook.go
@@ -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"`