214 lines
8.2 KiB
Go
214 lines
8.2 KiB
Go
|
package xlsx
|
||
|
|
||
|
import (
|
||
|
"archive/zip"
|
||
|
"encoding/xml"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// sheet state values as defined by
|
||
|
// http://msdn.microsoft.com/en-us/library/office/documentformat.openxml.spreadsheet.sheetstatevalues.aspx
|
||
|
sheetStateVisible = "visible"
|
||
|
sheetStateHidden = "hidden"
|
||
|
sheetStateVeryHidden = "veryHidden"
|
||
|
)
|
||
|
|
||
|
// xmlxWorkbookRels contains xmlxWorkbookRelations
|
||
|
// which maps sheet id and sheet XML
|
||
|
type xlsxWorkbookRels struct {
|
||
|
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/package/2006/relationships Relationships"`
|
||
|
Relationships []xlsxWorkbookRelation `xml:"Relationship"`
|
||
|
}
|
||
|
|
||
|
// xmlxWorkbookRelation maps sheet id and xl/worksheets/sheet%d.xml
|
||
|
type xlsxWorkbookRelation struct {
|
||
|
Id string `xml:",attr"`
|
||
|
Target string `xml:",attr"`
|
||
|
Type string `xml:",attr"`
|
||
|
}
|
||
|
|
||
|
// xlsxWorkbook directly maps the workbook element from the namespace
|
||
|
// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
|
||
|
// currently I have not checked it for completeness - it does as much
|
||
|
// as I need.
|
||
|
type xlsxWorkbook struct {
|
||
|
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main workbook"`
|
||
|
FileVersion xlsxFileVersion `xml:"fileVersion"`
|
||
|
WorkbookPr xlsxWorkbookPr `xml:"workbookPr"`
|
||
|
WorkbookProtection xlsxWorkbookProtection `xml:"workbookProtection"`
|
||
|
BookViews xlsxBookViews `xml:"bookViews"`
|
||
|
Sheets xlsxSheets `xml:"sheets"`
|
||
|
DefinedNames xlsxDefinedNames `xml:"definedNames"`
|
||
|
CalcPr xlsxCalcPr `xml:"calcPr"`
|
||
|
}
|
||
|
|
||
|
// xlsxWorkbookProtection directly maps the workbookProtection element from the
|
||
|
// namespace http://schemas.openxmlformats.org/spreadsheetml/2006/main
|
||
|
// - currently I have not checked it for completeness - it does as
|
||
|
// much as I need.
|
||
|
type xlsxWorkbookProtection struct {
|
||
|
// We don't need this, yet.
|
||
|
}
|
||
|
|
||
|
// xlsxFileVersion directly maps the fileVersion element from the
|
||
|
// namespace http://schemas.openxmlformats.org/spreadsheetml/2006/main
|
||
|
// - currently I have not checked it for completeness - it does as
|
||
|
// much as I need.
|
||
|
type xlsxFileVersion struct {
|
||
|
AppName string `xml:"appName,attr,omitempty"`
|
||
|
LastEdited string `xml:"lastEdited,attr,omitempty"`
|
||
|
LowestEdited string `xml:"lowestEdited,attr,omitempty"`
|
||
|
RupBuild string `xml:"rupBuild,attr,omitempty"`
|
||
|
}
|
||
|
|
||
|
// xlsxWorkbookPr directly maps the workbookPr element from the
|
||
|
// namespace http://schemas.openxmlformats.org/spreadsheetml/2006/main
|
||
|
// - currently I have not checked it for completeness - it does as
|
||
|
// much as I need.
|
||
|
type xlsxWorkbookPr struct {
|
||
|
DefaultThemeVersion string `xml:"defaultThemeVersion,attr,omitempty"`
|
||
|
BackupFile bool `xml:"backupFile,attr,omitempty"`
|
||
|
ShowObjects string `xml:"showObjects,attr,omitempty"`
|
||
|
Date1904 bool `xml:"date1904,attr"`
|
||
|
}
|
||
|
|
||
|
// xlsxBookViews directly maps the bookViews element from the
|
||
|
// namespace http://schemas.openxmlformats.org/spreadsheetml/2006/main
|
||
|
// - currently I have not checked it for completeness - it does as
|
||
|
// much as I need.
|
||
|
type xlsxBookViews struct {
|
||
|
WorkBookView []xlsxWorkBookView `xml:"workbookView"`
|
||
|
}
|
||
|
|
||
|
// xlsxWorkBookView directly maps the workbookView element from the
|
||
|
// namespace http://schemas.openxmlformats.org/spreadsheetml/2006/main
|
||
|
// - currently I have not checked it for completeness - it does as
|
||
|
// much as I need.
|
||
|
type xlsxWorkBookView struct {
|
||
|
ActiveTab int `xml:"activeTab,attr,omitempty"`
|
||
|
FirstSheet int `xml:"firstSheet,attr,omitempty"`
|
||
|
ShowHorizontalScroll bool `xml:"showHorizontalScroll,attr,omitempty"`
|
||
|
ShowVerticalScroll bool `xml:"showVerticalScroll,attr,omitempty"`
|
||
|
ShowSheetTabs bool `xml:"showSheetTabs,attr,omitempty"`
|
||
|
TabRatio int `xml:"tabRatio,attr,omitempty"`
|
||
|
WindowHeight int `xml:"windowHeight,attr,omitempty"`
|
||
|
WindowWidth int `xml:"windowWidth,attr,omitempty"`
|
||
|
XWindow string `xml:"xWindow,attr,omitempty"`
|
||
|
YWindow string `xml:"yWindow,attr,omitempty"`
|
||
|
}
|
||
|
|
||
|
// xlsxSheets directly maps the sheets element from the namespace
|
||
|
// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
|
||
|
// currently I have not checked it for completeness - it does as much
|
||
|
// as I need.
|
||
|
type xlsxSheets struct {
|
||
|
Sheet []xlsxSheet `xml:"sheet"`
|
||
|
}
|
||
|
|
||
|
// xlsxSheet directly maps the sheet element from the namespace
|
||
|
// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
|
||
|
// currently I have not checked it for completeness - it does as much
|
||
|
// as I need.
|
||
|
type xlsxSheet struct {
|
||
|
Name string `xml:"name,attr,omitempty"`
|
||
|
SheetId string `xml:"sheetId,attr,omitempty"`
|
||
|
Id string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
|
||
|
State string `xml:"state,attr,omitempty"`
|
||
|
}
|
||
|
|
||
|
// xlsxDefinedNames directly maps the definedNames element from the
|
||
|
// namespace http://schemas.openxmlformats.org/spreadsheetml/2006/main
|
||
|
// - currently I have not checked it for completeness - it does as
|
||
|
// much as I need.
|
||
|
type xlsxDefinedNames struct {
|
||
|
DefinedName []xlsxDefinedName `xml:"definedName"`
|
||
|
}
|
||
|
|
||
|
// xlsxDefinedName directly maps the definedName element from the
|
||
|
// namespace http://schemas.openxmlformats.org/spreadsheetml/2006/main
|
||
|
// - currently I have not checked it for completeness - it does as
|
||
|
// much as I need.
|
||
|
// for a descriptions of the attributes see
|
||
|
// https://msdn.microsoft.com/en-us/library/office/documentformat.openxml.spreadsheet.definedname.aspx
|
||
|
type xlsxDefinedName struct {
|
||
|
Data string `xml:",chardata"`
|
||
|
Name string `xml:"name,attr"`
|
||
|
Comment string `xml:"comment,attr,omitempty"`
|
||
|
CustomMenu string `xml:"customMenu,attr,omitempty"`
|
||
|
Description string `xml:"description,attr,omitempty"`
|
||
|
Help string `xml:"help,attr,omitempty"`
|
||
|
ShortcutKey string `xml:"shortcutKey,attr,omitempty"`
|
||
|
StatusBar string `xml:"statusBar,attr,omitempty"`
|
||
|
LocalSheetID int `xml:"localSheetId,attr,omitempty"`
|
||
|
FunctionGroupID int `xml:"functionGroupId,attr,omitempty"`
|
||
|
Function bool `xml:"function,attr,omitempty"`
|
||
|
Hidden bool `xml:"hidden,attr,omitempty"`
|
||
|
VbProcedure bool `xml:"vbProcedure,attr,omitempty"`
|
||
|
PublishToServer bool `xml:"publishToServer,attr,omitempty"`
|
||
|
WorkbookParameter bool `xml:"workbookParameter,attr,omitempty"`
|
||
|
Xlm bool `xml:"xml,attr,omitempty"`
|
||
|
}
|
||
|
|
||
|
// xlsxCalcPr directly maps the calcPr element from the namespace
|
||
|
// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
|
||
|
// currently I have not checked it for completeness - it does as much
|
||
|
// as I need.
|
||
|
type xlsxCalcPr struct {
|
||
|
CalcId string `xml:"calcId,attr,omitempty"`
|
||
|
IterateCount int `xml:"iterateCount,attr,omitempty"`
|
||
|
RefMode string `xml:"refMode,attr,omitempty"`
|
||
|
Iterate bool `xml:"iterate,attr,omitempty"`
|
||
|
IterateDelta float64 `xml:"iterateDelta,attr,omitempty"`
|
||
|
}
|
||
|
|
||
|
// Helper function to lookup the file corresponding to a xlsxSheet object in the worksheets map
|
||
|
func worksheetFileForSheet(sheet xlsxSheet, worksheets map[string]*zip.File, sheetXMLMap map[string]string) *zip.File {
|
||
|
sheetName, ok := sheetXMLMap[sheet.Id]
|
||
|
if !ok {
|
||
|
if sheet.SheetId != "" {
|
||
|
sheetName = fmt.Sprintf("sheet%s", sheet.SheetId)
|
||
|
} else {
|
||
|
sheetName = fmt.Sprintf("sheet%s", sheet.Id)
|
||
|
}
|
||
|
}
|
||
|
return worksheets[sheetName]
|
||
|
}
|
||
|
|
||
|
// getWorksheetFromSheet() is an internal helper function to open a
|
||
|
// sheetN.xml file, referred to by an xlsx.xlsxSheet struct, from the XLSX
|
||
|
// file and unmarshal it an xlsx.xlsxWorksheet struct
|
||
|
func getWorksheetFromSheet(sheet xlsxSheet, worksheets map[string]*zip.File, sheetXMLMap map[string]string, rowLimit int) (*xlsxWorksheet, error) {
|
||
|
var r io.Reader
|
||
|
var decoder *xml.Decoder
|
||
|
var worksheet *xlsxWorksheet
|
||
|
var err error
|
||
|
worksheet = new(xlsxWorksheet)
|
||
|
|
||
|
f := worksheetFileForSheet(sheet, worksheets, sheetXMLMap)
|
||
|
if f == nil {
|
||
|
return nil, fmt.Errorf("Unable to find sheet '%s'", sheet)
|
||
|
}
|
||
|
if rc, err := f.Open(); err != nil {
|
||
|
return nil, err
|
||
|
} else {
|
||
|
defer rc.Close()
|
||
|
r = rc
|
||
|
}
|
||
|
|
||
|
if rowLimit != NoRowLimit {
|
||
|
r, err = truncateSheetXML(r, rowLimit)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
decoder = xml.NewDecoder(r)
|
||
|
err = decoder.Decode(worksheet)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return worksheet, nil
|
||
|
}
|