sheet.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. package xlsx
  2. import (
  3. "fmt"
  4. "strconv"
  5. )
  6. // Sheet is a high level structure intended to provide user access to
  7. // the contents of a particular sheet within an XLSX file.
  8. type Sheet struct {
  9. Name string
  10. File *File
  11. Rows []*Row
  12. Cols []*Col
  13. MaxRow int
  14. MaxCol int
  15. Hidden bool
  16. Selected bool
  17. SheetViews []SheetView
  18. SheetFormat SheetFormat
  19. }
  20. type SheetView struct {
  21. Pane *Pane
  22. }
  23. type Pane struct {
  24. XSplit float64
  25. YSplit float64
  26. TopLeftCell string
  27. ActivePane string
  28. State string // Either "split" or "frozen"
  29. }
  30. type SheetFormat struct {
  31. DefaultColWidth float64
  32. DefaultRowHeight float64
  33. }
  34. // Add a new Row to a Sheet
  35. func (s *Sheet) AddRow() *Row {
  36. row := &Row{Sheet: s}
  37. s.Rows = append(s.Rows, row)
  38. if len(s.Rows) > s.MaxRow {
  39. s.MaxRow = len(s.Rows)
  40. }
  41. return row
  42. }
  43. // Make sure we always have as many Cols as we do cells.
  44. func (s *Sheet) maybeAddCol(cellCount int) {
  45. if cellCount > s.MaxCol {
  46. col := &Col{
  47. style: NewStyle(),
  48. Min: cellCount,
  49. Max: cellCount,
  50. Hidden: false,
  51. Collapsed: false}
  52. s.Cols = append(s.Cols, col)
  53. s.MaxCol = cellCount
  54. }
  55. }
  56. // Make sure we always have as many Cols as we do cells.
  57. func (s *Sheet) Col(idx int) *Col {
  58. s.maybeAddCol(idx + 1)
  59. return s.Cols[idx]
  60. }
  61. // Get a Cell by passing it's cartesian coordinates (zero based) as
  62. // row and column integer indexes.
  63. //
  64. // For example:
  65. //
  66. // cell := sheet.Cell(0,0)
  67. //
  68. // ... would set the variable "cell" to contain a Cell struct
  69. // containing the data from the field "A1" on the spreadsheet.
  70. func (sh *Sheet) Cell(row, col int) *Cell {
  71. if len(sh.Rows) > row && sh.Rows[row] != nil && len(sh.Rows[row].Cells) > col {
  72. return sh.Rows[row].Cells[col]
  73. }
  74. return new(Cell)
  75. }
  76. //Set the width of a single column or multiple columns.
  77. func (s *Sheet) SetColWidth(startcol, endcol int, width float64) error {
  78. if startcol > endcol {
  79. return fmt.Errorf("Could not set width for range %d-%d: startcol must be less than endcol.", startcol, endcol)
  80. }
  81. col := &Col{
  82. style: NewStyle(),
  83. Min: startcol + 1,
  84. Max: endcol + 1,
  85. Hidden: false,
  86. Collapsed: false,
  87. Width: width}
  88. s.Cols = append(s.Cols, col)
  89. if endcol+1 > s.MaxCol {
  90. s.MaxCol = endcol + 1
  91. }
  92. return nil
  93. }
  94. // Dump sheet to its XML representation, intended for internal use only
  95. func (s *Sheet) makeXLSXSheet(refTable *RefTable, styles *xlsxStyleSheet) *xlsxWorksheet {
  96. worksheet := newXlsxWorksheet()
  97. xSheet := xlsxSheetData{}
  98. maxRow := 0
  99. maxCell := 0
  100. if s.Selected {
  101. worksheet.SheetViews.SheetView[0].TabSelected = true
  102. }
  103. if s.SheetFormat.DefaultRowHeight != 0 {
  104. worksheet.SheetFormatPr.DefaultRowHeight = s.SheetFormat.DefaultRowHeight
  105. }
  106. worksheet.SheetFormatPr.DefaultColWidth = s.SheetFormat.DefaultColWidth
  107. colsXfIdList := make([]int, len(s.Cols))
  108. worksheet.Cols = xlsxCols{Col: []xlsxCol{}}
  109. for c, col := range s.Cols {
  110. XfId := 0
  111. style := col.GetStyle()
  112. //col's style always not nil
  113. if style != nil {
  114. xNumFmt := styles.newNumFmt(col.numFmt)
  115. XfId = handleStyleForXLSX(style, xNumFmt.NumFmtId, styles)
  116. }
  117. colsXfIdList[c] = XfId
  118. var customWidth int
  119. if col.Width == 0 {
  120. col.Width = ColWidth
  121. } else {
  122. customWidth = 1
  123. }
  124. worksheet.Cols.Col = append(worksheet.Cols.Col,
  125. xlsxCol{Min: col.Min,
  126. Max: col.Max,
  127. Hidden: col.Hidden,
  128. Width: col.Width,
  129. CustomWidth: customWidth,
  130. Collapsed: col.Collapsed,
  131. Style: XfId,
  132. })
  133. }
  134. for r, row := range s.Rows {
  135. if r > maxRow {
  136. maxRow = r
  137. }
  138. xRow := xlsxRow{}
  139. xRow.R = r + 1
  140. if row.isCustom {
  141. xRow.CustomHeight = true
  142. xRow.Ht = fmt.Sprintf("%g", row.Height)
  143. }
  144. for c, cell := range row.Cells {
  145. XfId := colsXfIdList[c]
  146. // generate NumFmtId and add new NumFmt
  147. xNumFmt := styles.newNumFmt(cell.NumFmt)
  148. style := cell.style
  149. if style != nil {
  150. XfId = handleStyleForXLSX(style, xNumFmt.NumFmtId, styles)
  151. } else if len(cell.NumFmt) > 0 && s.Cols[c].numFmt != cell.NumFmt {
  152. XfId = handleNumFmtIdForXLSX(xNumFmt.NumFmtId, styles)
  153. }
  154. if c > maxCell {
  155. maxCell = c
  156. }
  157. xC := xlsxC{}
  158. xC.R = fmt.Sprintf("%s%d", numericToLetters(c), r+1)
  159. switch cell.cellType {
  160. case CellTypeString:
  161. if len(cell.Value) > 0 {
  162. xC.V = strconv.Itoa(refTable.AddString(cell.Value))
  163. }
  164. xC.T = "s"
  165. xC.S = XfId
  166. case CellTypeBool:
  167. xC.V = cell.Value
  168. xC.T = "b"
  169. xC.S = XfId
  170. case CellTypeNumeric:
  171. xC.V = cell.Value
  172. xC.S = XfId
  173. case CellTypeDate:
  174. xC.V = cell.Value
  175. xC.S = XfId
  176. case CellTypeFormula:
  177. xC.V = cell.Value
  178. xC.F = &xlsxF{Content: cell.formula}
  179. xC.S = XfId
  180. case CellTypeError:
  181. xC.V = cell.Value
  182. xC.F = &xlsxF{Content: cell.formula}
  183. xC.T = "e"
  184. xC.S = XfId
  185. case CellTypeGeneral:
  186. xC.V = cell.Value
  187. xC.S = XfId
  188. }
  189. xRow.C = append(xRow.C, xC)
  190. if cell.HMerge > 0 || cell.VMerge > 0 {
  191. // r == rownum, c == colnum
  192. mc := xlsxMergeCell{}
  193. start := fmt.Sprintf("%s%d", numericToLetters(c), r+1)
  194. endcol := c + cell.HMerge
  195. endrow := r + cell.VMerge + 1
  196. end := fmt.Sprintf("%s%d", numericToLetters(endcol), endrow)
  197. mc.Ref = start + ":" + end
  198. if worksheet.MergeCells == nil {
  199. worksheet.MergeCells = &xlsxMergeCells{}
  200. }
  201. worksheet.MergeCells.Cells = append(worksheet.MergeCells.Cells, mc)
  202. }
  203. }
  204. xSheet.Row = append(xSheet.Row, xRow)
  205. }
  206. if worksheet.MergeCells != nil {
  207. worksheet.MergeCells.Count = len(worksheet.MergeCells.Cells)
  208. }
  209. worksheet.SheetData = xSheet
  210. dimension := xlsxDimension{}
  211. dimension.Ref = fmt.Sprintf("A1:%s%d",
  212. numericToLetters(maxCell), maxRow+1)
  213. if dimension.Ref == "A1:A1" {
  214. dimension.Ref = "A1"
  215. }
  216. worksheet.Dimension = dimension
  217. return worksheet
  218. }
  219. func handleStyleForXLSX(style *Style, NumFmtId int, styles *xlsxStyleSheet) (XfId int) {
  220. xFont, xFill, xBorder, xCellStyleXf, xCellXf := style.makeXLSXStyleElements()
  221. fontId := styles.addFont(xFont)
  222. fillId := styles.addFill(xFill)
  223. // HACK - adding light grey fill, as in OO and Google
  224. greyfill := xlsxFill{}
  225. greyfill.PatternFill.PatternType = "lightGray"
  226. styles.addFill(greyfill)
  227. borderId := styles.addBorder(xBorder)
  228. xCellStyleXf.FontId = fontId
  229. xCellStyleXf.FillId = fillId
  230. xCellStyleXf.BorderId = borderId
  231. xCellStyleXf.NumFmtId = builtInNumFmtIndex_GENERAL
  232. xCellXf.FontId = fontId
  233. xCellXf.FillId = fillId
  234. xCellXf.BorderId = borderId
  235. xCellXf.NumFmtId = NumFmtId
  236. // apply the numFmtId when it is not the default cellxf
  237. if xCellXf.NumFmtId > 0 {
  238. xCellXf.ApplyNumberFormat = true
  239. }
  240. xCellStyleXf.Alignment.Horizontal = style.Alignment.Horizontal
  241. xCellStyleXf.Alignment.Vertical = style.Alignment.Vertical
  242. xCellXf.Alignment.Horizontal = style.Alignment.Horizontal
  243. xCellXf.Alignment.Vertical = style.Alignment.Vertical
  244. styles.addCellStyleXf(xCellStyleXf)
  245. XfId = styles.addCellXf(xCellXf)
  246. return
  247. }
  248. func handleNumFmtIdForXLSX(NumFmtId int, styles *xlsxStyleSheet) (XfId int) {
  249. xCellXf := makeXLSXCellElement()
  250. xCellXf.NumFmtId = NumFmtId
  251. if xCellXf.NumFmtId > 0 {
  252. xCellXf.ApplyNumberFormat = true
  253. }
  254. XfId = styles.addCellXf(xCellXf)
  255. return
  256. }