file.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. package xlsx
  2. import (
  3. "archive/zip"
  4. "bytes"
  5. "encoding/xml"
  6. "fmt"
  7. "io"
  8. "os"
  9. "strconv"
  10. "strings"
  11. )
  12. // File is a high level structure providing a slice of Sheet structs
  13. // to the user.
  14. type File struct {
  15. worksheets map[string]*zip.File
  16. referenceTable *RefTable
  17. Date1904 bool
  18. styles *xlsxStyleSheet
  19. Sheets []*Sheet
  20. Sheet map[string]*Sheet
  21. theme *theme
  22. }
  23. // Create a new File
  24. func NewFile() (file *File) {
  25. file = &File{}
  26. file.Sheet = make(map[string]*Sheet)
  27. file.Sheets = make([]*Sheet, 0)
  28. return
  29. }
  30. // OpenFile() take the name of an XLSX file and returns a populated
  31. // xlsx.File struct for it.
  32. func OpenFile(filename string) (file *File, err error) {
  33. var f *zip.ReadCloser
  34. f, err = zip.OpenReader(filename)
  35. if err != nil {
  36. return nil, err
  37. }
  38. file, err = ReadZip(f)
  39. return
  40. }
  41. // OpenBinary() take bytes of an XLSX file and returns a populated
  42. // xlsx.File struct for it.
  43. func OpenBinary(bs []byte) (file *File, err error) {
  44. r := bytes.NewReader(bs)
  45. file, err = OpenReaderAt(r, int64(r.Len()))
  46. return
  47. }
  48. // OpenReaderAt() take io.ReaderAt of an XLSX file and returns a populated
  49. // xlsx.File struct for it.
  50. func OpenReaderAt(r io.ReaderAt, size int64) (file *File, err error) {
  51. var f *zip.Reader
  52. f, err = zip.NewReader(r, size)
  53. if err != nil {
  54. return nil, err
  55. }
  56. file, err = ReadZipReader(f)
  57. return
  58. }
  59. // A convenient wrapper around File.ToSlice, FileToSlice will
  60. // return the raw data contained in an Excel XLSX file as three
  61. // dimensional slice. The first index represents the sheet number,
  62. // the second the row number, and the third the cell number.
  63. //
  64. // For example:
  65. //
  66. // var mySlice [][][]string
  67. // var value string
  68. // mySlice = xlsx.FileToSlice("myXLSX.xlsx")
  69. // value = mySlice[0][0][0]
  70. //
  71. // Here, value would be set to the raw value of the cell A1 in the
  72. // first sheet in the XLSX file.
  73. func FileToSlice(path string) ([][][]string, error) {
  74. f, err := OpenFile(path)
  75. if err != nil {
  76. return nil, err
  77. }
  78. return f.ToSlice()
  79. }
  80. // Save the File to an xlsx file at the provided path.
  81. func (f *File) Save(path string) (err error) {
  82. var target *os.File
  83. target, err = os.Create(path)
  84. if err != nil {
  85. return
  86. }
  87. err = f.Write(target)
  88. if err != nil {
  89. return
  90. }
  91. return target.Close()
  92. }
  93. // Write the File to io.Writer as xlsx
  94. func (f *File) Write(writer io.Writer) (err error) {
  95. var parts map[string]string
  96. var zipWriter *zip.Writer
  97. parts, err = f.MarshallParts()
  98. if err != nil {
  99. return
  100. }
  101. zipWriter = zip.NewWriter(writer)
  102. for partName, part := range parts {
  103. var writer io.Writer
  104. writer, err = zipWriter.Create(partName)
  105. if err != nil {
  106. return
  107. }
  108. _, err = writer.Write([]byte(part))
  109. if err != nil {
  110. return
  111. }
  112. }
  113. err = zipWriter.Close()
  114. return
  115. }
  116. // Add a new Sheet, with the provided name, to a File
  117. func (f *File) AddSheet(sheetName string) (sheet *Sheet, err error) {
  118. if _, exists := f.Sheet[sheetName]; exists {
  119. return nil, fmt.Errorf("Duplicate sheet name '%s'.", sheetName)
  120. }
  121. sheet = &Sheet{Name: sheetName, File: f}
  122. if len(f.Sheets) == 0 {
  123. sheet.Selected = true
  124. }
  125. f.Sheet[sheetName] = sheet
  126. f.Sheets = append(f.Sheets, sheet)
  127. return sheet, nil
  128. }
  129. func (f *File) makeWorkbook() xlsxWorkbook {
  130. var workbook xlsxWorkbook
  131. workbook = xlsxWorkbook{}
  132. workbook.FileVersion = xlsxFileVersion{}
  133. workbook.FileVersion.AppName = "Go XLSX"
  134. workbook.WorkbookPr = xlsxWorkbookPr{
  135. BackupFile: false,
  136. ShowObjects: "all"}
  137. workbook.BookViews = xlsxBookViews{}
  138. workbook.BookViews.WorkBookView = make([]xlsxWorkBookView, 1)
  139. workbook.BookViews.WorkBookView[0] = xlsxWorkBookView{
  140. ActiveTab: 0,
  141. FirstSheet: 0,
  142. ShowHorizontalScroll: true,
  143. ShowSheetTabs: true,
  144. ShowVerticalScroll: true,
  145. TabRatio: 204,
  146. WindowHeight: 8192,
  147. WindowWidth: 16384,
  148. XWindow: "0",
  149. YWindow: "0"}
  150. workbook.Sheets = xlsxSheets{}
  151. workbook.Sheets.Sheet = make([]xlsxSheet, len(f.Sheets))
  152. workbook.CalcPr.IterateCount = 100
  153. workbook.CalcPr.RefMode = "A1"
  154. workbook.CalcPr.Iterate = false
  155. workbook.CalcPr.IterateDelta = 0.001
  156. return workbook
  157. }
  158. // Some tools that read XLSX files have very strict requirements about
  159. // the structure of the input XML. In particular both Numbers on the Mac
  160. // and SAS dislike inline XML namespace declarations, or namespace
  161. // prefixes that don't match the ones that Excel itself uses. This is a
  162. // problem because the Go XML library doesn't multiple namespace
  163. // declarations in a single element of a document. This function is a
  164. // horrible hack to fix that after the XML marshalling is completed.
  165. func replaceRelationshipsNameSpace(workbookMarshal string) string {
  166. newWorkbook := strings.Replace(workbookMarshal, `xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships:id`, `r:id`, -1)
  167. // Dirty hack to fix issues #63 and #91; encoding/xml currently
  168. // "doesn't allow for additional namespaces to be defined in the
  169. // root element of the document," as described by @tealeg in the
  170. // comments for #63.
  171. oldXmlns := `<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`
  172. newXmlns := `<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">`
  173. return strings.Replace(newWorkbook, oldXmlns, newXmlns, 1)
  174. }
  175. // Construct a map of file name to XML content representing the file
  176. // in terms of the structure of an XLSX file.
  177. func (f *File) MarshallParts() (map[string]string, error) {
  178. var parts map[string]string
  179. var refTable *RefTable = NewSharedStringRefTable()
  180. refTable.isWrite = true
  181. var workbookRels WorkBookRels = make(WorkBookRels)
  182. var err error
  183. var workbook xlsxWorkbook
  184. var types xlsxTypes = MakeDefaultContentTypes()
  185. marshal := func(thing interface{}) (string, error) {
  186. body, err := xml.Marshal(thing)
  187. if err != nil {
  188. return "", err
  189. }
  190. return xml.Header + string(body), nil
  191. }
  192. parts = make(map[string]string)
  193. workbook = f.makeWorkbook()
  194. sheetIndex := 1
  195. if f.styles == nil {
  196. f.styles = newXlsxStyleSheet(f.theme)
  197. }
  198. f.styles.reset()
  199. for _, sheet := range f.Sheets {
  200. xSheet := sheet.makeXLSXSheet(refTable, f.styles)
  201. rId := fmt.Sprintf("rId%d", sheetIndex)
  202. sheetId := strconv.Itoa(sheetIndex)
  203. sheetPath := fmt.Sprintf("worksheets/sheet%d.xml", sheetIndex)
  204. partName := "xl/" + sheetPath
  205. types.Overrides = append(
  206. types.Overrides,
  207. xlsxOverride{
  208. PartName: "/" + partName,
  209. ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"})
  210. workbookRels[rId] = sheetPath
  211. workbook.Sheets.Sheet[sheetIndex-1] = xlsxSheet{
  212. Name: sheet.Name,
  213. SheetId: sheetId,
  214. Id: rId,
  215. State: "visible"}
  216. parts[partName], err = marshal(xSheet)
  217. if err != nil {
  218. return parts, err
  219. }
  220. sheetIndex++
  221. }
  222. workbookMarshal, err := marshal(workbook)
  223. if err != nil {
  224. return parts, err
  225. }
  226. workbookMarshal = replaceRelationshipsNameSpace(workbookMarshal)
  227. parts["xl/workbook.xml"] = workbookMarshal
  228. if err != nil {
  229. return parts, err
  230. }
  231. parts["_rels/.rels"] = TEMPLATE__RELS_DOT_RELS
  232. parts["docProps/app.xml"] = TEMPLATE_DOCPROPS_APP
  233. // TODO - do this properly, modification and revision information
  234. parts["docProps/core.xml"] = TEMPLATE_DOCPROPS_CORE
  235. parts["xl/theme/theme1.xml"] = TEMPLATE_XL_THEME_THEME
  236. xSST := refTable.makeXLSXSST()
  237. parts["xl/sharedStrings.xml"], err = marshal(xSST)
  238. if err != nil {
  239. return parts, err
  240. }
  241. xWRel := workbookRels.MakeXLSXWorkbookRels()
  242. parts["xl/_rels/workbook.xml.rels"], err = marshal(xWRel)
  243. if err != nil {
  244. return parts, err
  245. }
  246. parts["[Content_Types].xml"], err = marshal(types)
  247. if err != nil {
  248. return parts, err
  249. }
  250. parts["xl/styles.xml"], err = f.styles.Marshal()
  251. if err != nil {
  252. return parts, err
  253. }
  254. return parts, nil
  255. }
  256. // Return the raw data contained in the File as three
  257. // dimensional slice. The first index represents the sheet number,
  258. // the second the row number, and the third the cell number.
  259. //
  260. // For example:
  261. //
  262. // var mySlice [][][]string
  263. // var value string
  264. // mySlice = xlsx.FileToSlice("myXLSX.xlsx")
  265. // value = mySlice[0][0][0]
  266. //
  267. // Here, value would be set to the raw value of the cell A1 in the
  268. // first sheet in the XLSX file.
  269. func (file *File) ToSlice() (output [][][]string, err error) {
  270. output = [][][]string{}
  271. for _, sheet := range file.Sheets {
  272. s := [][]string{}
  273. for _, row := range sheet.Rows {
  274. if row == nil {
  275. continue
  276. }
  277. r := []string{}
  278. for _, cell := range row.Cells {
  279. r = append(r, cell.String())
  280. }
  281. s = append(s, r)
  282. }
  283. output = append(output, s)
  284. }
  285. return output, nil
  286. }