file.go 8.3 KB

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