file.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. package xlsx
  2. import (
  3. "archive/zip"
  4. "encoding/xml"
  5. "fmt"
  6. "io"
  7. "os"
  8. "strconv"
  9. )
  10. // File is a high level structure providing a slice of Sheet structs
  11. // to the user.
  12. type File struct {
  13. worksheets map[string]*zip.File
  14. referenceTable *RefTable
  15. styles *xlsxStyles
  16. Sheets map[string]*Sheet // sheet access by index
  17. }
  18. // Create a new File
  19. func NewFile() (file *File) {
  20. file = &File{};
  21. file.Sheets = make(map[string]*Sheet)
  22. return
  23. }
  24. // OpenFile() take the name of an XLSX file and returns a populated
  25. // xlsx.File struct for it.
  26. func OpenFile(filename string) (*File, error) {
  27. var f *zip.ReadCloser
  28. f, err := zip.OpenReader(filename)
  29. if err != nil {
  30. return nil, err
  31. }
  32. return ReadZip(f)
  33. }
  34. func (f *File) Save(path string) (err error) {
  35. var parts map[string]string
  36. var target *os.File
  37. var zipWriter *zip.Writer
  38. parts, err = f.MarshallParts()
  39. if err != nil {
  40. return
  41. }
  42. target, err = os.Create(path)
  43. if err != nil {
  44. return
  45. }
  46. zipWriter = zip.NewWriter(target)
  47. for partName, part := range parts {
  48. var writer io.Writer
  49. writer, err = zipWriter.Create(partName)
  50. if err != nil {
  51. return
  52. }
  53. _, err = writer.Write([]byte(part))
  54. if err != nil {
  55. return
  56. }
  57. }
  58. err = zipWriter.Close()
  59. if err != nil {
  60. return
  61. }
  62. return target.Close()
  63. }
  64. // Add a new Sheet, with the provided name, to a File
  65. func (f *File) AddSheet(sheetName string) (sheet *Sheet) {
  66. sheet = &Sheet{}
  67. f.Sheets[sheetName] = sheet
  68. return sheet
  69. }
  70. func (f *File) makeWorkbook() xlsxWorkbook {
  71. var workbook xlsxWorkbook
  72. workbook = xlsxWorkbook{}
  73. workbook.FileVersion = xlsxFileVersion{}
  74. workbook.FileVersion.AppName = "Go XLSX"
  75. workbook.WorkbookPr = xlsxWorkbookPr{BackupFile: false}
  76. workbook.BookViews = xlsxBookViews{}
  77. workbook.BookViews.WorkBookView = make([]xlsxWorkBookView, 1)
  78. workbook.BookViews.WorkBookView[0] = xlsxWorkBookView{}
  79. workbook.Sheets = xlsxSheets{}
  80. workbook.Sheets.Sheet = make([]xlsxSheet, len(f.Sheets))
  81. return workbook
  82. }
  83. func (f *File) MarshallParts() (map[string]string, error) {
  84. var parts map[string]string
  85. var refTable *RefTable = NewSharedStringRefTable()
  86. var workbookRels WorkBookRels = make(WorkBookRels)
  87. var err error
  88. var workbook xlsxWorkbook
  89. var types xlsxTypes = MakeDefaultContentTypes()
  90. marshal := func(thing interface{}) (string, error) {
  91. body, err := xml.MarshalIndent(thing, " ", " ")
  92. if err != nil {
  93. return "", err
  94. }
  95. return xml.Header + string(body), nil
  96. }
  97. parts = make(map[string]string)
  98. workbook = f.makeWorkbook()
  99. sheetIndex := 1
  100. for sheetName, sheet := range f.Sheets {
  101. xSheet := sheet.makeXLSXSheet(refTable)
  102. rId := fmt.Sprintf("rId%d", sheetIndex)
  103. sheetId := strconv.Itoa(sheetIndex)
  104. sheetPath := fmt.Sprintf("worksheets/sheet%d.xml", sheetIndex)
  105. partName := "xl/" + sheetPath
  106. types.Overrides = append(
  107. types.Overrides,
  108. xlsxOverride{
  109. PartName: partName,
  110. ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"})
  111. workbookRels[rId] = sheetPath
  112. workbook.Sheets.Sheet[sheetIndex - 1] = xlsxSheet{
  113. Name: sheetName,
  114. SheetId: sheetId,
  115. Id: rId}
  116. parts[partName], err = marshal(xSheet)
  117. if err != nil {
  118. return parts, err
  119. }
  120. sheetIndex++
  121. }
  122. parts["xl/workbook.xml"], err = marshal(workbook)
  123. if err != nil {
  124. return parts, err
  125. }
  126. parts["_rels/.rels"] = `<?xml version="1.0" encoding="UTF-8"?>
  127. <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  128. <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
  129. <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
  130. <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
  131. </Relationships>`
  132. parts["docProps/app.xml"] = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  133. <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
  134. <TotalTime>0</TotalTime>
  135. <Application>Go XLSX</Application>
  136. </Properties>`
  137. // TODO - do this properly, modification and revision information
  138. parts["docProps/core.xml"] = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  139. <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></cp:coreProperties>`
  140. xSST := refTable.makeXLSXSST()
  141. parts["xl/sharedStrings.xml"], err = marshal(xSST)
  142. if err != nil {
  143. return parts, err
  144. }
  145. xWRel := workbookRels.MakeXLSXWorkbookRels()
  146. parts["xl/_rels/workbook.xml.rels"], err = marshal(xWRel)
  147. if err != nil {
  148. return parts, err
  149. }
  150. parts["[Content_Types].xml"], err = marshal(types)
  151. if err != nil {
  152. return parts, err
  153. }
  154. parts["xl/styles.xml"] = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  155. <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
  156. </styleSheet>`
  157. return parts, nil
  158. }