file.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. package xlsx
  2. import (
  3. "archive/zip"
  4. "bytes"
  5. "encoding/xml"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "os"
  10. "strconv"
  11. "strings"
  12. )
  13. // File is a high level structure providing a slice of Sheet structs
  14. // to the user.
  15. type File struct {
  16. worksheets map[string]*zip.File
  17. referenceTable *RefTable
  18. Date1904 bool
  19. styles *xlsxStyleSheet
  20. Sheets []*Sheet
  21. Sheet map[string]*Sheet
  22. theme *theme
  23. DefinedNames []*xlsxDefinedName
  24. }
  25. // Create a new File
  26. func NewFile() *File {
  27. return &File{
  28. Sheet: make(map[string]*Sheet),
  29. Sheets: make([]*Sheet, 0),
  30. DefinedNames: make([]*xlsxDefinedName, 0),
  31. }
  32. }
  33. // OpenFile() take the name of an XLSX file and returns a populated
  34. // xlsx.File struct for it.
  35. func OpenFile(filename string) (file *File, err error) {
  36. var f *zip.ReadCloser
  37. f, err = zip.OpenReader(filename)
  38. if err != nil {
  39. return nil, err
  40. }
  41. file, err = ReadZip(f)
  42. return
  43. }
  44. // OpenBinary() take bytes of an XLSX file and returns a populated
  45. // xlsx.File struct for it.
  46. func OpenBinary(bs []byte) (*File, error) {
  47. r := bytes.NewReader(bs)
  48. return OpenReaderAt(r, int64(r.Len()))
  49. }
  50. // OpenReaderAt() take io.ReaderAt of an XLSX file and returns a populated
  51. // xlsx.File struct for it.
  52. func OpenReaderAt(r io.ReaderAt, size int64) (*File, error) {
  53. file, err := zip.NewReader(r, size)
  54. if err != nil {
  55. return nil, err
  56. }
  57. return ReadZipReader(file)
  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. // FileToSliceUnmerged is a wrapper around File.ToSliceUnmerged.
  81. // It returns the raw data contained in an Excel XLSX file as three
  82. // dimensional slice. Merged cells will be unmerged. Covered cells become the
  83. // value of its origins.
  84. func FileToSliceUnmerged(path string) ([][][]string, error) {
  85. f, err := OpenFile(path)
  86. if err != nil {
  87. return nil, err
  88. }
  89. return f.ToSliceUnmerged()
  90. }
  91. // Save the File to an xlsx file at the provided path.
  92. func (f *File) Save(path string) (err error) {
  93. target, err := os.Create(path)
  94. if err != nil {
  95. return err
  96. }
  97. err = f.Write(target)
  98. if err != nil {
  99. return err
  100. }
  101. return target.Close()
  102. }
  103. // Write the File to io.Writer as xlsx
  104. func (f *File) Write(writer io.Writer) (err error) {
  105. parts, err := f.MarshallParts()
  106. if err != nil {
  107. return
  108. }
  109. zipWriter := zip.NewWriter(writer)
  110. for partName, part := range parts {
  111. w, err := zipWriter.Create(partName)
  112. if err != nil {
  113. return err
  114. }
  115. _, err = w.Write([]byte(part))
  116. if err != nil {
  117. return err
  118. }
  119. }
  120. return zipWriter.Close()
  121. }
  122. // Add a new Sheet, with the provided name, to a File
  123. func (f *File) AddSheet(sheetName string) (*Sheet, error) {
  124. if _, exists := f.Sheet[sheetName]; exists {
  125. return nil, fmt.Errorf("duplicate sheet name '%s'.", sheetName)
  126. }
  127. if len(sheetName) >= 31 {
  128. return nil, fmt.Errorf("sheet name must be less than 31 characters long. It is currently '%d' characters long", len(sheetName))
  129. }
  130. sheet := &Sheet{
  131. Name: sheetName,
  132. File: f,
  133. Selected: len(f.Sheets) == 0,
  134. }
  135. f.Sheet[sheetName] = sheet
  136. f.Sheets = append(f.Sheets, sheet)
  137. return sheet, nil
  138. }
  139. // Appends an existing Sheet, with the provided name, to a File
  140. func (f *File) AppendSheet(sheet Sheet, sheetName string) (*Sheet, error) {
  141. if _, exists := f.Sheet[sheetName]; exists {
  142. return nil, fmt.Errorf("duplicate sheet name '%s'.", sheetName)
  143. }
  144. sheet.Name = sheetName
  145. sheet.File = f
  146. sheet.Selected = len(f.Sheets) == 0
  147. f.Sheet[sheetName] = &sheet
  148. f.Sheets = append(f.Sheets, &sheet)
  149. return &sheet, nil
  150. }
  151. func (f *File) makeWorkbook() xlsxWorkbook {
  152. return xlsxWorkbook{
  153. FileVersion: xlsxFileVersion{AppName: "Go XLSX"},
  154. WorkbookPr: xlsxWorkbookPr{ShowObjects: "all"},
  155. BookViews: xlsxBookViews{
  156. WorkBookView: []xlsxWorkBookView{
  157. {
  158. ShowHorizontalScroll: true,
  159. ShowSheetTabs: true,
  160. ShowVerticalScroll: true,
  161. TabRatio: 204,
  162. WindowHeight: 8192,
  163. WindowWidth: 16384,
  164. XWindow: "0",
  165. YWindow: "0",
  166. },
  167. },
  168. },
  169. Sheets: xlsxSheets{Sheet: make([]xlsxSheet, len(f.Sheets))},
  170. CalcPr: xlsxCalcPr{
  171. IterateCount: 100,
  172. RefMode: "A1",
  173. Iterate: false,
  174. IterateDelta: 0.001,
  175. },
  176. }
  177. }
  178. // Some tools that read XLSX files have very strict requirements about
  179. // the structure of the input XML. In particular both Numbers on the Mac
  180. // and SAS dislike inline XML namespace declarations, or namespace
  181. // prefixes that don't match the ones that Excel itself uses. This is a
  182. // problem because the Go XML library doesn't multiple namespace
  183. // declarations in a single element of a document. This function is a
  184. // horrible hack to fix that after the XML marshalling is completed.
  185. func replaceRelationshipsNameSpace(workbookMarshal string) string {
  186. newWorkbook := strings.Replace(workbookMarshal, `xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships:id`, `r:id`, -1)
  187. // Dirty hack to fix issues #63 and #91; encoding/xml currently
  188. // "doesn't allow for additional namespaces to be defined in the
  189. // root element of the document," as described by @tealeg in the
  190. // comments for #63.
  191. oldXmlns := `<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`
  192. newXmlns := `<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">`
  193. return strings.Replace(newWorkbook, oldXmlns, newXmlns, 1)
  194. }
  195. // Construct a map of file name to XML content representing the file
  196. // in terms of the structure of an XLSX file.
  197. func (f *File) MarshallParts() (map[string]string, error) {
  198. var parts map[string]string
  199. var refTable *RefTable = NewSharedStringRefTable()
  200. refTable.isWrite = true
  201. var workbookRels WorkBookRels = make(WorkBookRels)
  202. var err error
  203. var workbook xlsxWorkbook
  204. var types xlsxTypes = MakeDefaultContentTypes()
  205. marshal := func(thing interface{}) (string, error) {
  206. body, err := xml.Marshal(thing)
  207. if err != nil {
  208. return "", err
  209. }
  210. return xml.Header + string(body), nil
  211. }
  212. parts = make(map[string]string)
  213. workbook = f.makeWorkbook()
  214. sheetIndex := 1
  215. if f.styles == nil {
  216. f.styles = newXlsxStyleSheet(f.theme)
  217. }
  218. f.styles.reset()
  219. if len(f.Sheets) == 0 {
  220. err := errors.New("Workbook must contains atleast one worksheet")
  221. return nil, err
  222. }
  223. for _, sheet := range f.Sheets {
  224. xSheet := sheet.makeXLSXSheet(refTable, f.styles)
  225. rId := fmt.Sprintf("rId%d", sheetIndex)
  226. sheetId := strconv.Itoa(sheetIndex)
  227. sheetPath := fmt.Sprintf("worksheets/sheet%d.xml", sheetIndex)
  228. partName := "xl/" + sheetPath
  229. types.Overrides = append(
  230. types.Overrides,
  231. xlsxOverride{
  232. PartName: "/" + partName,
  233. ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"})
  234. workbookRels[rId] = sheetPath
  235. workbook.Sheets.Sheet[sheetIndex-1] = xlsxSheet{
  236. Name: sheet.Name,
  237. SheetId: sheetId,
  238. Id: rId,
  239. State: "visible"}
  240. parts[partName], err = marshal(xSheet)
  241. if err != nil {
  242. return parts, err
  243. }
  244. sheetIndex++
  245. }
  246. workbookMarshal, err := marshal(workbook)
  247. if err != nil {
  248. return parts, err
  249. }
  250. workbookMarshal = replaceRelationshipsNameSpace(workbookMarshal)
  251. parts["xl/workbook.xml"] = workbookMarshal
  252. if err != nil {
  253. return parts, err
  254. }
  255. parts["_rels/.rels"] = TEMPLATE__RELS_DOT_RELS
  256. parts["docProps/app.xml"] = TEMPLATE_DOCPROPS_APP
  257. // TODO - do this properly, modification and revision information
  258. parts["docProps/core.xml"] = TEMPLATE_DOCPROPS_CORE
  259. parts["xl/theme/theme1.xml"] = TEMPLATE_XL_THEME_THEME
  260. xSST := refTable.makeXLSXSST()
  261. parts["xl/sharedStrings.xml"], err = marshal(xSST)
  262. if err != nil {
  263. return parts, err
  264. }
  265. xWRel := workbookRels.MakeXLSXWorkbookRels()
  266. parts["xl/_rels/workbook.xml.rels"], err = marshal(xWRel)
  267. if err != nil {
  268. return parts, err
  269. }
  270. parts["[Content_Types].xml"], err = marshal(types)
  271. if err != nil {
  272. return parts, err
  273. }
  274. parts["xl/styles.xml"], err = f.styles.Marshal()
  275. if err != nil {
  276. return parts, err
  277. }
  278. return parts, nil
  279. }
  280. // Return the raw data contained in the File as three
  281. // dimensional slice. The first index represents the sheet number,
  282. // the second the row number, and the third the cell number.
  283. //
  284. // For example:
  285. //
  286. // var mySlice [][][]string
  287. // var value string
  288. // mySlice = xlsx.FileToSlice("myXLSX.xlsx")
  289. // value = mySlice[0][0][0]
  290. //
  291. // Here, value would be set to the raw value of the cell A1 in the
  292. // first sheet in the XLSX file.
  293. func (f *File) ToSlice() (output [][][]string, err error) {
  294. output = [][][]string{}
  295. for _, sheet := range f.Sheets {
  296. s := [][]string{}
  297. for _, row := range sheet.Rows {
  298. if row == nil {
  299. continue
  300. }
  301. r := []string{}
  302. for _, cell := range row.Cells {
  303. str, err := cell.FormattedValue()
  304. if err != nil {
  305. // Recover from strconv.NumError if the value is an empty string,
  306. // and insert an empty string in the output.
  307. if numErr, ok := err.(*strconv.NumError); ok && numErr.Num == "" {
  308. str = ""
  309. } else {
  310. return output, err
  311. }
  312. }
  313. r = append(r, str)
  314. }
  315. s = append(s, r)
  316. }
  317. output = append(output, s)
  318. }
  319. return output, nil
  320. }
  321. // ToSliceUnmerged returns the raw data contained in the File as three
  322. // dimensional slice (s. method ToSlice).
  323. // The covered cells become the value of its origin cell.
  324. // Example: table where A1:A2 merged.
  325. // | 01.01.2011 | Bread | 20 |
  326. // | | Fish | 70 |
  327. // This sheet will be converted to the slice:
  328. // [ [01.01.2011 Bread 20]
  329. // [01.01.2011 Fish 70] ]
  330. func (f *File) ToSliceUnmerged() (output [][][]string, err error) {
  331. output, err = f.ToSlice()
  332. if err != nil {
  333. return nil, err
  334. }
  335. for s, sheet := range f.Sheets {
  336. for r, row := range sheet.Rows {
  337. for c, cell := range row.Cells {
  338. if cell.HMerge > 0 {
  339. for i := c + 1; i <= c+cell.HMerge; i++ {
  340. output[s][r][i] = output[s][r][c]
  341. }
  342. }
  343. if cell.VMerge > 0 {
  344. for i := r + 1; i <= r+cell.VMerge; i++ {
  345. output[s][i][c] = output[s][r][c]
  346. }
  347. }
  348. }
  349. }
  350. }
  351. return output, nil
  352. }