From a9d3ee28693adfe920b3cd047f52862d8a79a00e Mon Sep 17 00:00:00 2001 From: Ri Xu Date: Tue, 30 Aug 2016 11:51:31 +0800 Subject: [PATCH] Init commit. --- README.md | 79 +++++++++++++ excelize.go | 196 +++++++++++++++++++++++++++++++ excelize.png | Bin 0 -> 43470 bytes excelize_test.go | 55 +++++++++ file.go | 54 +++++++++ lib.go | 132 +++++++++++++++++++++ sheet.go | 189 ++++++++++++++++++++++++++++++ templates.go | 40 +++++++ test/Workbook1.xlsx | Bin 0 -> 19085 bytes xmlContentTypes.go | 49 ++++++++ xmlWorkbook.go | 167 +++++++++++++++++++++++++++ xmlWorksheet.go | 276 ++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 1237 insertions(+) create mode 100644 README.md create mode 100644 excelize.go create mode 100644 excelize.png create mode 100644 excelize_test.go create mode 100644 file.go create mode 100644 lib.go create mode 100644 sheet.go create mode 100644 templates.go create mode 100644 test/Workbook1.xlsx create mode 100644 xmlContentTypes.go create mode 100644 xmlWorkbook.go create mode 100644 xmlWorksheet.go diff --git a/README.md b/README.md new file mode 100644 index 00000000..e9fd91b2 --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +* Excelize + +![Excelize](./excelize.png "Excelize") + +** Introduction +Excelize is a library written in pure Golang and providing a set of function that allow you to write to and read from XLSX files. + + + +** Basic Usage + +*** Installation + +``` +go get github.com/luxurioust/excelize +``` + +*** Create XLSX files + +Here is a minimal example usage that will create XLSX file. + +``` +package main + +import ( + "fmt" + "github.com/luxurioust/excelize" +) + +func main() { + xlsx := excelize.CreateFile() + xlsx = excelize.NewSheet(xlsx, 2, "Sheet2") + xlsx = excelize.NewSheet(xlsx, 3, "Sheet3") + xlsx = excelize.SetCellInt(xlsx, "Sheet2", "A23", 10) + xlsx = excelize.SetCellStr(xlsx, "Sheet3", "B20", "Hello") + err := excelize.Save(xlsx, "~/Workbook.xlsx") + if err != nil { + fmt.Println(err) + } +} +``` + +*** Writing XLSX files + +The following constitutes the bare minimum required to write an XLSX document. + +``` +package main + +import ( + "fmt" + "github.com/luxurioust/excelize" +) + +func main() { + xlsx, err := excelize.Openxlsx("~/Workbook.xlsx") + if err != nil { + fmt.Println(err) + } + xlsx = excelize.SetCellInt(xlsx, "Sheet2", "B2", 100) + xlsx = excelize.SetCellStr(xlsx, "Sheet2", "C11", "Hello") + xlsx = excelize.NewSheet(xlsx, 3, "TestSheet") + xlsx = excelize.SetCellInt(xlsx, "Sheet3", "A23", 10) + xlsx = excelize.SetCellStr(xlsx, "Sheet3", "b230", "World") + xlsx = excelize.SetActiveSheet(xlsx, 2) + if err != nil { + fmt.Println(err) + } + err = excelize.Save(xlsx, "~/Workbook.xlsx") +} +``` + +** Contributing + +Contributions are welcome! Open a pull request to fix a bug, or open an issue to discuss a new feature or change. + +** Licenses + +This program is under the terms of the BSD 3-Clause License. See . \ No newline at end of file diff --git a/excelize.go b/excelize.go new file mode 100644 index 00000000..38382314 --- /dev/null +++ b/excelize.go @@ -0,0 +1,196 @@ +package excelize + +import ( + "archive/zip" + "encoding/xml" + "fmt" + "strconv" + "strings" +) + +type FileList struct { + Key string + Value string +} + +// OpenFile() take the name of an XLSX file and returns a populated +// xlsx.File struct for it. +func OpenFile(filename string) (file []FileList, err error) { + var f *zip.ReadCloser + f, err = zip.OpenReader(filename) + if err != nil { + return nil, err + } + file, err = ReadZip(f) + return +} + +// Set int type value of a cell +func SetCellInt(file []FileList, sheet string, axis string, value int) []FileList { + axis = strings.ToUpper(axis) + var xlsx xlsxWorksheet + col := getColIndex(axis) + row := getRowIndex(axis) + xAxis := row - 1 + yAxis := titleToNumber(col) + + name := fmt.Sprintf("xl/worksheets/%s.xml", strings.ToLower(sheet)) + xml.Unmarshal([]byte(readXml(file, name)), &xlsx) + + rows := xAxis + 1 + cell := yAxis + 1 + + xlsx = checkRow(xlsx) + + xlsx = completeRow(xlsx, rows, cell) + xlsx = completeCol(xlsx, rows, cell) + + xlsx.SheetData.Row[xAxis].C[yAxis].T = "" + xlsx.SheetData.Row[xAxis].C[yAxis].V = strconv.Itoa(value) + + output, err := xml.MarshalIndent(xlsx, "", "") + if err != nil { + fmt.Println(err) + } + saveFileList(file, name, replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(output)))) + return file +} + +// Set string type value of a cell +func SetCellStr(file []FileList, sheet string, axis string, value string) []FileList { + axis = strings.ToUpper(axis) + var xlsx xlsxWorksheet + col := getColIndex(axis) + row := getRowIndex(axis) + xAxis := row - 1 + yAxis := titleToNumber(col) + + name := fmt.Sprintf("xl/worksheets/%s.xml", strings.ToLower(sheet)) + xml.Unmarshal([]byte(readXml(file, name)), &xlsx) + + rows := xAxis + 1 + cell := yAxis + 1 + + xlsx = checkRow(xlsx) + xlsx = completeRow(xlsx, rows, cell) + xlsx = completeCol(xlsx, rows, cell) + + xlsx.SheetData.Row[xAxis].C[yAxis].T = "str" + xlsx.SheetData.Row[xAxis].C[yAxis].V = value + + output, err := xml.MarshalIndent(xlsx, "", "") + if err != nil { + fmt.Println(err) + } + saveFileList(file, name, replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(output)))) + return file +} + +// Completion column element tags of XML in a sheet +func completeCol(xlsx xlsxWorksheet, row int, cell int) xlsxWorksheet { + if len(xlsx.SheetData.Row) < cell { + for i := len(xlsx.SheetData.Row); i < cell; i++ { + xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{ + R: i + 1, + }) + } + } + for k, v := range xlsx.SheetData.Row { + if len(v.C) < cell { + start := len(v.C) + for iii := start; iii < cell; iii++ { + xlsx.SheetData.Row[k].C = append(xlsx.SheetData.Row[k].C, xlsxC{ + R: toAlphaString(iii+1) + strconv.Itoa(k+1), + }) + } + } + } + return xlsx +} + +// Completion row element tags of XML in a sheet +func completeRow(xlsx xlsxWorksheet, row int, cell int) xlsxWorksheet { + if len(xlsx.SheetData.Row) < row { + for i := len(xlsx.SheetData.Row); i < row; i++ { + xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{ + R: i + 1, + }) + } + + for ii := 0; ii < row; ii++ { + start := len(xlsx.SheetData.Row[ii].C) + if start == 0 { + for iii := start; iii < cell; iii++ { + xlsx.SheetData.Row[ii].C = append(xlsx.SheetData.Row[ii].C, xlsxC{ + R: toAlphaString(iii+1) + strconv.Itoa(ii+1), + }) + } + } + } + } + return xlsx +} + +// Replace xl/worksheets/sheet%d.xml XML tags to self-closing for compatible Office Excel 2007 +func replaceWorkSheetsRelationshipsNameSpace(workbookMarshal string) string { + oldXmlns := `` + newXmlns := `` + workbookMarshal = strings.Replace(workbookMarshal, oldXmlns, newXmlns, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + return workbookMarshal +} + +// Check XML tags and fix discontinuous case, for example: +// +// +// +// +// +// +// +// +// in this case, we should to change it to +// +// +// +// +// +// +// +// +// +// +// +func checkRow(xlsx xlsxWorksheet) xlsxWorksheet { + for k, v := range xlsx.SheetData.Row { + lenCol := len(v.C) + endR := getColIndex(v.C[lenCol-1].R) + endRow := getRowIndex(v.C[lenCol-1].R) + endCol := titleToNumber(endR) + if lenCol < endCol { + oldRow := xlsx.SheetData.Row[k].C + xlsx.SheetData.Row[k].C = xlsx.SheetData.Row[k].C[:0] + tmp := []xlsxC{} + for i := 0; i <= endCol; i++ { + fixAxis := toAlphaString(i+1) + strconv.Itoa(endRow) + tmp = append(tmp, xlsxC{ + R: fixAxis, + }) + } + xlsx.SheetData.Row[k].C = tmp + for _, y := range oldRow { + colAxis := titleToNumber(getColIndex(y.R)) + xlsx.SheetData.Row[k].C[colAxis] = y + } + } + } + return xlsx +} diff --git a/excelize.png b/excelize.png new file mode 100644 index 0000000000000000000000000000000000000000..b4b452944ca9942a9f3d75a685c40ccab89744de GIT binary patch literal 43470 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 CXyn5H literal 0 HcmV?d00001 diff --git a/excelize_test.go b/excelize_test.go new file mode 100644 index 00000000..c2848a05 --- /dev/null +++ b/excelize_test.go @@ -0,0 +1,55 @@ +package excelize + +import ( + "fmt" + "math/rand" + "sync" + "testing" + "time" +) + +var ( + once sync.Once +) + +func testSetup() { + rand.Seed(time.Now().UnixNano()) +} + +func TestExcelize(t *testing.T) { + // Test update a XLSX file + file, err := OpenFile("./test/Workbook1.xlsx") + if err != nil { + fmt.Println(err) + } + file = SetCellInt(file, "SHEET2", "B2", 100) + file = SetCellStr(file, "SHEET2", "C11", "Knowns") + file = NewSheet(file, 3, "TestSheet") + file = SetCellInt(file, "Sheet3", "A23", 10) + file = SetCellStr(file, "SHEET3", "b230", "10") + file = SetActiveSheet(file, 2) + if err != nil { + fmt.Println(err) + } + for i := 1; i <= 300; i++ { + file = SetCellStr(file, "SHEET3", fmt.Sprintf("c%d", i), randToken(5)) + } + err = Save(file, "./test/Workbook_2.xlsx") + + // Test create a XLSX file + file2 := CreateFile() + file2 = NewSheet(file2, 2, "SHEETxxx") + file2 = NewSheet(file2, 3, "asd") + file2 = SetCellInt(file2, "Sheet2", "A23", 10) + file2 = SetCellStr(file2, "SHEET1", "B20", "10") + err = Save(file2, "./test/Workbook_3.xlsx") + if err != nil { + fmt.Println(err) + } +} + +func randToken(length int) string { + b := make([]byte, length) + rand.Read(b) + return fmt.Sprintf("%x", b) +} diff --git a/file.go b/file.go new file mode 100644 index 00000000..bdefc3b2 --- /dev/null +++ b/file.go @@ -0,0 +1,54 @@ +package excelize + +import ( + "archive/zip" + "bytes" + "fmt" + "os" +) + +// Create a new xlsx file +// +// For example: +// +// xlsx := CreateFile() +// +func CreateFile() []FileList { + var file []FileList + file = saveFileList(file, `_rels/.rels`, TEMPLATE_RELS) + file = saveFileList(file, `docProps/app.xml`, TEMPLATE_DOCPROPS_APP) + file = saveFileList(file, `docProps/core.xml`, TEMPLATE_DOCPROPS_CORE) + file = saveFileList(file, `xl/_rels/workbook.xml.rels`, TEMPLATE_WORKBOOK_RELS) + file = saveFileList(file, `xl/theme/theme1.xml`, TEMPLATE_THEME) + file = saveFileList(file, `xl/worksheets/sheet1.xml`, TEMPLATE_SHEET) + file = saveFileList(file, `xl/styles.xml`, TEMPLATE_STYLES) + file = saveFileList(file, `xl/workbook.xml`, TEMPLATE_WORKBOOK) + file = saveFileList(file, `[Content_Types].xml`, TEMPLATE_CONTENT_TYPES) + return file +} + +// Save after create or update to an xlsx file at the provided path. +func Save(files []FileList, name string) error { + buf := new(bytes.Buffer) + w := zip.NewWriter(buf) + for _, file := range files { + f, err := w.Create(file.Key) + if err != nil { + fmt.Println(err) + } + _, err = f.Write([]byte(file.Value)) + if err != nil { + return err + } + } + err := w.Close() + if err != nil { + return err + } + f, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666) + if err != nil { + return err + } + buf.WriteTo(f) + return err +} diff --git a/lib.go b/lib.go new file mode 100644 index 00000000..e3d7a4ff --- /dev/null +++ b/lib.go @@ -0,0 +1,132 @@ +package excelize + +import ( + "archive/zip" + "bytes" + "io" + "log" + "math" + "os" + "regexp" + "strconv" + "strings" +) + +// ReadZip() takes a pointer to a zip.ReadCloser and returns a +// xlsx.File struct populated with its contents. In most cases +// ReadZip is not used directly, but is called internally by OpenFile. +func ReadZip(f *zip.ReadCloser) ([]FileList, error) { + defer f.Close() + return ReadZipReader(&f.Reader) +} + +// ReadZipReader() can be used to read an XLSX in memory without +// touching the filesystem. +func ReadZipReader(r *zip.Reader) ([]FileList, error) { + var fileList []FileList + for _, v := range r.File { + singleFile := FileList{ + Key: v.Name, + Value: readFile(v), + } + fileList = append(fileList, singleFile) + } + return fileList, nil +} + +// Read XML content as string and replace drawing property in XML namespace of sheet +func readXml(files []FileList, name string) string { + for _, file := range files { + if file.Key == name { + return strings.Replace(file.Value, "` + newXmlns := `` + return strings.Replace(workbookMarshal, oldXmlns, newXmlns, -1) +} + +// replace relationships ID in worksheets/sheet%d.xml +func replaceRelationshipsID(workbookMarshal string) string { + rids := strings.Replace(workbookMarshal, ``, ``, -1) + return strings.Replace(rids, ` 0 { + content.BookViews.WorkBookView[0].ActiveTab = index + } else { + content.BookViews.WorkBookView = append(content.BookViews.WorkBookView, xlsxWorkBookView{ + ActiveTab: index, + }) + } + sheets := len(content.Sheets.Sheet) + output, err := xml.MarshalIndent(content, "", "") + if err != nil { + fmt.Println(err) + } + file = saveFileList(file, `xl/workbook.xml`, workBookCompatibility(replaceRelationshipsNameSpace(string(output)))) + index += 1 + for i := 0; i < sheets; i++ { + xlsx := xlsxWorksheet{} + sheetIndex := i + 1 + path := fmt.Sprintf("xl/worksheets/sheet%d.xml", sheetIndex) + xml.Unmarshal([]byte(readXml(file, path)), &xlsx) + if index == sheetIndex { + if len(xlsx.SheetViews.SheetView) > 0 { + xlsx.SheetViews.SheetView[0].TabSelected = true + } else { + xlsx.SheetViews.SheetView = append(xlsx.SheetViews.SheetView, xlsxSheetView{ + TabSelected: true, + }) + } + } else { + if len(xlsx.SheetViews.SheetView) > 0 { + xlsx.SheetViews.SheetView[0].TabSelected = false + } + } + sheet, err := xml.MarshalIndent(xlsx, "", "") + if err != nil { + fmt.Println(err) + } + file = saveFileList(file, path, replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(sheet)))) + } + return file +} + +// Replace xl/workbook.xml XML tags to self-closing for compatible Office Excel 2007 +func workBookCompatibility(workbookMarshal string) string { + workbookMarshal = strings.Replace(workbookMarshal, `xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships:id="`, `r:id="`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + workbookMarshal = strings.Replace(workbookMarshal, `>`, ` />`, -1) + return workbookMarshal +} diff --git a/templates.go b/templates.go new file mode 100644 index 00000000..3d8e24fe --- /dev/null +++ b/templates.go @@ -0,0 +1,40 @@ +// This file contains default templates for XML files we don't yet +// populated based on content. + +package excelize + +const XMLHeader = "\n" + +const TEMPLATE_DOCPROPS_APP = ` + 0 + Go XLSX +` + +const TEMPLATE_CONTENT_TYPES = ` + + + + + + + + +` + +const TEMPLATE_WORKBOOK = ` +` + +const TEMPLATE_STYLES = ` +` +const TEMPLATE_SHEET = ` +` + +const TEMPLATE_WORKBOOK_RELS = `` + +const TEMPLATE_DOCPROPS_CORE = `xuri2006-09-16T00:00:00Z2006-09-16T00:00:00Z` + +const TEMPLATE_RELS = `` + +const TEMPLATE_THEME = ` + +` diff --git a/test/Workbook1.xlsx b/test/Workbook1.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..cbdeda97b5b1918bc76c0cfa4bdfc24d5e49353c GIT binary patch literal 19085 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 EKOrD*H~;_u literal 0 HcmV?d00001 diff --git a/xmlContentTypes.go b/xmlContentTypes.go new file mode 100644 index 00000000..af42774f --- /dev/null +++ b/xmlContentTypes.go @@ -0,0 +1,49 @@ +package excelize + +import ( + "encoding/xml" +) + +type xlsxTypes struct { + XMLName xml.Name `xml:"http://schemas.openxmlformats.org/package/2006/content-types Types"` + Overrides []xlsxOverride `xml:"Override"` + Defaults []xlsxDefault `xml:"Default"` +} + +type xlsxOverride struct { + PartName string `xml:",attr"` + ContentType string `xml:",attr"` +} + +type xlsxDefault struct { + Extension string `xml:",attr"` + ContentType string `xml:",attr"` +} + +func MakeDefaultContentTypes() (types xlsxTypes) { + types.Overrides = make([]xlsxOverride, 8) + types.Defaults = make([]xlsxDefault, 2) + + types.Overrides[0].PartName = "/_rels/.rels" + types.Overrides[0].ContentType = "application/vnd.openxmlformats-package.relationships+xml" + types.Overrides[1].PartName = "/docProps/app.xml" + types.Overrides[1].ContentType = "application/vnd.openxmlformats-officedocument.extended-properties+xml" + types.Overrides[2].PartName = "/docProps/core.xml" + types.Overrides[2].ContentType = "application/vnd.openxmlformats-package.core-properties+xml" + types.Overrides[3].PartName = "/xl/_rels/workbook.xml.rels" + types.Overrides[3].ContentType = "application/vnd.openxmlformats-package.relationships+xml" + types.Overrides[4].PartName = "/xl/sharedStrings.xml" + types.Overrides[4].ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml" + types.Overrides[5].PartName = "/xl/styles.xml" + types.Overrides[5].ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" + types.Overrides[6].PartName = "/xl/workbook.xml" + types.Overrides[6].ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" + types.Overrides[7].PartName = "/xl/theme/theme1.xml" + types.Overrides[7].ContentType = "application/vnd.openxmlformats-officedocument.theme+xml" + + types.Defaults[0].Extension = "rels" + types.Defaults[0].ContentType = "application/vnd.openxmlformats-package.relationships+xml" + types.Defaults[1].Extension = "xml" + types.Defaults[1].ContentType = "application/xml" + return +} diff --git a/xmlWorkbook.go b/xmlWorkbook.go new file mode 100644 index 00000000..2bbeeed7 --- /dev/null +++ b/xmlWorkbook.go @@ -0,0 +1,167 @@ +package excelize + +import ( + "encoding/xml" +) + +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"` + FileRecoveryPr xlsxFileRecoveryPr `xml:"fileRecoveryPr"` +} + +// xlsxFileRecoveryPr maps sheet recovery information +type xlsxFileRecoveryPr struct { + RepairLoad int `xml:"repairLoad,attr"` +} + +// 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"` +} diff --git a/xmlWorksheet.go b/xmlWorksheet.go new file mode 100644 index 00000000..8c1e8498 --- /dev/null +++ b/xmlWorksheet.go @@ -0,0 +1,276 @@ +package excelize + +import ( + "encoding/xml" +) + +// xlsxWorksheet directly maps the worksheet element in 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 xlsxWorksheet struct { + XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main worksheet"` + SheetPr xlsxSheetPr `xml:"sheetPr"` + Dimension xlsxDimension `xml:"dimension"` + SheetViews xlsxSheetViews `xml:"sheetViews"` + SheetFormatPr xlsxSheetFormatPr `xml:"sheetFormatPr"` + Cols *xlsxCols `xml:"cols,omitempty"` + SheetData xlsxSheetData `xml:"sheetData"` + MergeCells *xlsxMergeCells `xml:"mergeCells,omitempty"` + PrintOptions xlsxPrintOptions `xml:"printOptions"` + PageMargins xlsxPageMargins `xml:"pageMargins"` + PageSetUp xlsxPageSetUp `xml:"pageSetup"` + HeaderFooter xlsxHeaderFooter `xml:"headerFooter"` + Drawing xlsxDrawing `xml:"drawing"` +} + +// xlsxDrawing change r:id to rid in the namespace +type xlsxDrawing struct { + RId string `xml:"rid,attr"` +} + +// xlsxHeaderFooter directly maps the headerFooter element in 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 xlsxHeaderFooter struct { + DifferentFirst bool `xml:"differentFirst,attr"` + DifferentOddEven bool `xml:"differentOddEven,attr"` + OddHeader []xlsxOddHeader `xml:"oddHeader"` + OddFooter []xlsxOddFooter `xml:"oddFooter"` +} + +// xlsxOddHeader directly maps the oddHeader element in 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 xlsxOddHeader struct { + Content string `xml:",chardata"` +} + +// xlsxOddFooter directly maps the oddFooter element in 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 xlsxOddFooter struct { + Content string `xml:",chardata"` +} + +// xlsxPageSetUp directly maps the pageSetup element in 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 xlsxPageSetUp struct { + PaperSize string `xml:"paperSize,attr,omitempty"` + Scale int `xml:"scale,attr"` + FirstPageNumber int `xml:"firstPageNumber,attr"` + FitToWidth int `xml:"fitToWidth,attr"` + FitToHeight int `xml:"fitToHeight,attr"` + PageOrder string `xml:"pageOrder,attr,omitempty"` + Orientation string `xml:"orientation,attr,omitempty"` + UsePrinterDefaults bool `xml:"usePrinterDefaults,attr"` + BlackAndWhite bool `xml:"blackAndWhite,attr"` + Draft bool `xml:"draft,attr"` + CellComments string `xml:"cellComments,attr,omitempty"` + UseFirstPageNumber bool `xml:"useFirstPageNumber,attr"` + HorizontalDPI float32 `xml:"horizontalDpi,attr"` + VerticalDPI float32 `xml:"verticalDpi,attr"` + Copies int `xml:"copies,attr"` +} + +// xlsxPrintOptions directly maps the printOptions element in 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 xlsxPrintOptions struct { + Headings bool `xml:"headings,attr"` + GridLines bool `xml:"gridLines,attr"` + GridLinesSet bool `xml:"gridLinesSet,attr"` + HorizontalCentered bool `xml:"horizontalCentered,attr"` + VerticalCentered bool `xml:"verticalCentered,attr"` +} + +// xlsxPageMargins directly maps the pageMargins element in 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 xlsxPageMargins struct { + Left float64 `xml:"left,attr"` + Right float64 `xml:"right,attr"` + Top float64 `xml:"top,attr"` + Bottom float64 `xml:"bottom,attr"` + Header float64 `xml:"header,attr"` + Footer float64 `xml:"footer,attr"` +} + +// xlsxSheetFormatPr directly maps the sheetFormatPr element in 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 xlsxSheetFormatPr struct { + DefaultColWidth float64 `xml:"defaultColWidth,attr,omitempty"` + DefaultRowHeight float64 `xml:"defaultRowHeight,attr"` + OutlineLevelCol uint8 `xml:"outlineLevelCol,attr,omitempty"` + OutlineLevelRow uint8 `xml:"outlineLevelRow,attr,omitempty"` +} + +// xlsxSheetViews directly maps the sheetViews element in 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 xlsxSheetViews struct { + SheetView []xlsxSheetView `xml:"sheetView"` +} + +// xlsxSheetView directly maps the sheetView element in 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 xlsxSheetView struct { + // WindowProtection bool `xml:"windowProtection,attr"` + // ShowFormulas bool `xml:"showFormulas,attr"` + // ShowGridLines bool `xml:"showGridLines,attr"` + // ShowRowColHeaders bool `xml:"showRowColHeaders,attr"` + // ShowZeros bool `xml:"showZeros,attr"` + // RightToLeft bool `xml:"rightToLeft,attr"` + TabSelected bool `xml:"tabSelected,attr"` + // ShowOutlineSymbols bool `xml:"showOutlineSymbols,attr"` + // DefaultGridColor bool `xml:"defaultGridColor,attr"` + // View string `xml:"view,attr"` + TopLeftCell string `xml:"topLeftCell,attr,omitempty"` + // ColorId int `xml:"colorId,attr"` + // ZoomScale float64 `xml:"zoomScale,attr"` + // ZoomScaleNormal float64 `xml:"zoomScaleNormal,attr"` + // ZoomScalePageLayoutView float64 `xml:"zoomScalePageLayoutView,attr"` + WorkbookViewId int `xml:"workbookViewId,attr"` + Selection []xlsxSelection `xml:"selection"` + Pane *xlsxPane `xml:"pane,omitempty"` +} + +// xlsxSelection directly maps the selection element in 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 xlsxSelection struct { + Pane string `xml:"pane,attr,omitempty"` + ActiveCell string `xml:"activeCell,attr"` + ActiveCellId int `xml:"activeCellId,attr"` + SQRef string `xml:"sqref,attr"` +} + +// xlsxSelection directly maps the selection element in 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 xlsxPane struct { + XSplit float64 `xml:"xSplit,attr"` + YSplit float64 `xml:"ySplit,attr"` + TopLeftCell string `xml:"topLeftCell,attr,omitempty"` + ActivePane string `xml:"activePane,attr,omitempty"` + State string `xml:"state,attr,omitempty"` // Either "split" or "frozen" +} + +// xlsxSheetPr directly maps the sheetPr element in 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 xlsxSheetPr struct { + FilterMode bool `xml:"filterMode,attr"` + PageSetUpPr []xlsxPageSetUpPr `xml:"pageSetUpPr"` +} + +// xlsxPageSetUpPr directly maps the pageSetupPr element in 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 xlsxPageSetUpPr struct { + FitToPage bool `xml:"fitToPage,attr"` +} + +// xlsxCols directly maps the cols element in 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 xlsxCols struct { + Col []xlsxCol `xml:"col"` +} + +// xlsxCol directly maps the col element in 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 xlsxCol struct { + Collapsed bool `xml:"collapsed,attr"` + Hidden bool `xml:"hidden,attr"` + Max int `xml:"max,attr"` + Min int `xml:"min,attr"` + Style int `xml:"style,attr"` + Width float64 `xml:"width,attr"` + CustomWidth int `xml:"customWidth,attr,omitempty"` + OutlineLevel uint8 `xml:"outlineLevel,attr,omitempty"` +} + +// xlsxDimension directly maps the dimension element in 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 xlsxDimension struct { + Ref string `xml:"ref,attr"` +} + +// xlsxSheetData directly maps the sheetData element in 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 xlsxSheetData struct { + XMLName xml.Name `xml:"sheetData"` + Row []xlsxRow `xml:"row"` +} + +// xlsxRow directly maps the row element in 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 xlsxRow struct { + R int `xml:"r,attr"` + Spans string `xml:"spans,attr,omitempty"` + Hidden bool `xml:"hidden,attr,omitempty"` + C []xlsxC `xml:"c"` + Ht string `xml:"ht,attr,omitempty"` + CustomHeight bool `xml:"customHeight,attr,omitempty"` + OutlineLevel uint8 `xml:"outlineLevel,attr,omitempty"` +} + +type xlsxMergeCell struct { + Ref string `xml:"ref,attr"` // ref: horiz "A1:C1", vert "B3:B6", both "D3:G4" +} + +type xlsxMergeCells struct { + XMLName xml.Name //`xml:"mergeCells,omitempty"` + Count int `xml:"count,attr,omitempty"` + Cells []xlsxMergeCell `xml:"mergeCell,omitempty"` +} + +// xlsxC directly maps the c element in 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 xlsxC struct { + R string `xml:"r,attr"` // Cell ID, e.g. A1 + S int `xml:"s,attr,omitempty"` // Style reference. + // Str string `xml:"str,attr,omitempty"` // Style reference. + T string `xml:"t,attr,omitempty"` // Type. + F *xlsxF `xml:"f,omitempty"` // Formula + V string `xml:"v,omitempty"` // Value +} + +// xlsxF directly maps the f element in 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 xlsxF struct { + Content string `xml:",chardata"` + T string `xml:"t,attr,omitempty"` // Formula type + Ref string `xml:"ref,attr,omitempty"` // Shared formula ref + Si int `xml:"si,attr,omitempty"` // Shared formula index +}