lib.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. package xlsx
  2. import (
  3. "archive/zip"
  4. "fmt"
  5. "io"
  6. "os"
  7. "strconv"
  8. "xml"
  9. )
  10. // XLSXReaderError is the standard error type for otherwise undefined
  11. // errors in the XSLX reading process.
  12. type XLSXReaderError struct {
  13. Error string
  14. }
  15. // String() returns a string value from an XLSXReaderError struct in
  16. // order that it might comply with the os.Error interface.
  17. func (e *XLSXReaderError) String() string {
  18. return e.Error
  19. }
  20. // Cell is a high level structure intended to provide user access to
  21. // the contents of Cell within an xlsx.Row.
  22. type Cell struct {
  23. data string
  24. }
  25. // CellInterface defines the public API of the Cell.
  26. type CellInterface interface {
  27. String() string
  28. }
  29. func (c *Cell) String() string {
  30. return c.data
  31. }
  32. // Row is a high level structure indended to provide user access to a
  33. // row within a xlsx.Sheet. An xlsx.Row contains a slice of xlsx.Cell.
  34. type Row struct {
  35. Cells []*Cell
  36. }
  37. // Sheet is a high level structure intended to provide user access to
  38. // the contents of a particular sheet within an XLSX file.
  39. type Sheet struct {
  40. Rows []*Row
  41. }
  42. // File is a high level structure providing a slice of Sheet structs
  43. // to the user.
  44. type File struct {
  45. worksheets map[string] *zip.File
  46. referenceTable []string
  47. Sheets []*Sheet
  48. }
  49. // readRowsFromSheet is an internal helper function that extracts the
  50. // rows from a XSLXWorksheet, poulates them with Cells and resolves
  51. // the value references from the reference table and stores them in
  52. func readRowsFromSheet(worksheet *XLSXWorksheet, reftable []string) []*Row {
  53. var rows []*Row
  54. rows = make([]*Row, len(worksheet.SheetData.Row))
  55. for i, rawrow := range worksheet.SheetData.Row {
  56. row := new(Row)
  57. row.Cells = make([]*Cell, len(rawrow.C))
  58. for j, rawcell := range rawrow.C {
  59. cell := new(Cell)
  60. cell.data = ""
  61. if len(rawcell.V.Data) > 0 {
  62. ref, error := strconv.Atoi(rawcell.V.Data)
  63. if error != nil {
  64. panic(fmt.Sprintf("Invalid reference in Excel Cell (not found in sharedStrings.xml) - the reference was %v\n", rawcell.V.Data))
  65. }
  66. cell.data = reftable[ref]
  67. }
  68. row.Cells[j] = cell
  69. }
  70. rows[i] = row
  71. }
  72. return rows
  73. }
  74. // readSheetsFromZipFile is an internal helper function that loops
  75. // over the Worksheets defined in the XSLXWorkbook and loads them into
  76. // Sheet objects stored in the Sheets slice of a xlsx.File struct.
  77. func readSheetsFromZipFile(f *zip.File, file *File) ([]*Sheet, os.Error) {
  78. var workbook *XLSXWorkbook
  79. var error os.Error
  80. var rc io.ReadCloser
  81. workbook = new(XLSXWorkbook)
  82. rc, error = f.Open()
  83. if error != nil {
  84. return nil, error
  85. }
  86. error = xml.Unmarshal(rc, workbook)
  87. if error != nil {
  88. return nil, error
  89. }
  90. sheets := make([]*Sheet, len(workbook.Sheets.Sheet))
  91. for i, rawsheet := range workbook.Sheets.Sheet {
  92. worksheet, error := getWorksheetFromSheet(rawsheet, file.worksheets)
  93. if error != nil {
  94. return nil, error
  95. }
  96. sheet := new(Sheet)
  97. sheet.Rows = readRowsFromSheet(worksheet, file.referenceTable)
  98. sheets[i] = sheet
  99. }
  100. return sheets, nil
  101. }
  102. // readSharedStringsFromZipFile() is an internal helper function to
  103. // extract a reference table from the sharedStrings.xml file within
  104. // the XLSX zip file.
  105. func readSharedStringsFromZipFile(f *zip.File) ([]string, os.Error) {
  106. var sst *XLSXSST
  107. var error os.Error
  108. var rc io.ReadCloser
  109. var reftable []string
  110. rc, error = f.Open()
  111. if error != nil {
  112. return nil, error
  113. }
  114. sst = new(XLSXSST)
  115. error = xml.Unmarshal(rc, sst)
  116. if error != nil {
  117. return nil, error
  118. }
  119. reftable = MakeSharedStringRefTable(sst)
  120. return reftable, nil
  121. }
  122. // OpenFile() take the name of an XLSX file and returns a populated
  123. // xlsx.File struct for it.
  124. func OpenFile(filename string) (x *File, e os.Error) {
  125. var f *zip.ReadCloser
  126. var error os.Error
  127. var file *File
  128. var v *zip.File
  129. var workbook *zip.File
  130. var sharedStrings *zip.File
  131. var reftable []string
  132. var worksheets map[string]*zip.File
  133. f, error = zip.OpenReader(filename)
  134. if error != nil {
  135. return nil, error
  136. }
  137. file = new(File)
  138. worksheets = make(map[string]*zip.File, len(f.File))
  139. for _, v = range f.File {
  140. switch v.Name {
  141. case "xl/sharedStrings.xml":
  142. sharedStrings = v
  143. case "xl/workbook.xml":
  144. workbook = v
  145. default:
  146. if len(v.Name) > 12 {
  147. if v.Name[0:13] == "xl/worksheets" {
  148. worksheets[v.Name[14:len(v.Name)-4]]= v
  149. }
  150. }
  151. }
  152. }
  153. file.worksheets = worksheets
  154. reftable, error = readSharedStringsFromZipFile(sharedStrings)
  155. if error != nil {
  156. return nil, error
  157. }
  158. if reftable == nil {
  159. error := new(XLSXReaderError)
  160. error.Error = "No valid sharedStrings.xml found in XLSX file"
  161. return nil, error
  162. }
  163. file.referenceTable = reftable
  164. sheets, error := readSheetsFromZipFile(workbook, file)
  165. if error != nil {
  166. return nil, error
  167. }
  168. if sheets == nil {
  169. error := new(XLSXReaderError)
  170. error.Error = "No sheets found in XLSX File"
  171. return nil, error
  172. }
  173. file.Sheets = sheets
  174. f.Close()
  175. return file, nil
  176. }