sheet.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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. if col.Min == 0 {
  112. col.Min = 1
  113. }
  114. if col.Max == 0 {
  115. col.Max = 1
  116. }
  117. style := col.GetStyle()
  118. //col's style always not nil
  119. if style != nil {
  120. xNumFmt := styles.newNumFmt(col.numFmt)
  121. XfId = handleStyleForXLSX(style, xNumFmt.NumFmtId, styles)
  122. }
  123. colsXfIdList[c] = XfId
  124. var customWidth int
  125. if col.Width == 0 {
  126. col.Width = ColWidth
  127. } else {
  128. customWidth = 1
  129. }
  130. worksheet.Cols.Col = append(worksheet.Cols.Col,
  131. xlsxCol{Min: col.Min,
  132. Max: col.Max,
  133. Hidden: col.Hidden,
  134. Width: col.Width,
  135. CustomWidth: customWidth,
  136. Collapsed: col.Collapsed,
  137. Style: XfId,
  138. })
  139. }
  140. for r, row := range s.Rows {
  141. if r > maxRow {
  142. maxRow = r
  143. }
  144. xRow := xlsxRow{}
  145. xRow.R = r + 1
  146. if row.isCustom {
  147. xRow.CustomHeight = true
  148. xRow.Ht = fmt.Sprintf("%g", row.Height)
  149. }
  150. for c, cell := range row.Cells {
  151. XfId := colsXfIdList[c]
  152. // generate NumFmtId and add new NumFmt
  153. xNumFmt := styles.newNumFmt(cell.NumFmt)
  154. style := cell.style
  155. if style != nil {
  156. XfId = handleStyleForXLSX(style, xNumFmt.NumFmtId, styles)
  157. } else if len(cell.NumFmt) > 0 && s.Cols[c].numFmt != cell.NumFmt {
  158. XfId = handleNumFmtIdForXLSX(xNumFmt.NumFmtId, styles)
  159. }
  160. if c > maxCell {
  161. maxCell = c
  162. }
  163. xC := xlsxC{}
  164. xC.R = fmt.Sprintf("%s%d", numericToLetters(c), r+1)
  165. switch cell.cellType {
  166. case CellTypeString:
  167. if len(cell.Value) > 0 {
  168. xC.V = strconv.Itoa(refTable.AddString(cell.Value))
  169. }
  170. xC.T = "s"
  171. xC.S = XfId
  172. case CellTypeBool:
  173. xC.V = cell.Value
  174. xC.T = "b"
  175. xC.S = XfId
  176. case CellTypeNumeric:
  177. xC.V = cell.Value
  178. xC.S = XfId
  179. case CellTypeDate:
  180. xC.V = cell.Value
  181. xC.S = XfId
  182. case CellTypeFormula:
  183. xC.V = cell.Value
  184. xC.F = &xlsxF{Content: cell.formula}
  185. xC.S = XfId
  186. case CellTypeError:
  187. xC.V = cell.Value
  188. xC.F = &xlsxF{Content: cell.formula}
  189. xC.T = "e"
  190. xC.S = XfId
  191. case CellTypeGeneral:
  192. xC.V = cell.Value
  193. xC.S = XfId
  194. }
  195. xRow.C = append(xRow.C, xC)
  196. if cell.HMerge > 0 || cell.VMerge > 0 {
  197. // r == rownum, c == colnum
  198. mc := xlsxMergeCell{}
  199. start := fmt.Sprintf("%s%d", numericToLetters(c), r+1)
  200. endcol := c + cell.HMerge
  201. endrow := r + cell.VMerge + 1
  202. end := fmt.Sprintf("%s%d", numericToLetters(endcol), endrow)
  203. mc.Ref = start + ":" + end
  204. if worksheet.MergeCells == nil {
  205. worksheet.MergeCells = &xlsxMergeCells{}
  206. }
  207. worksheet.MergeCells.Cells = append(worksheet.MergeCells.Cells, mc)
  208. }
  209. }
  210. xSheet.Row = append(xSheet.Row, xRow)
  211. }
  212. if worksheet.MergeCells != nil {
  213. worksheet.MergeCells.Count = len(worksheet.MergeCells.Cells)
  214. }
  215. worksheet.SheetData = xSheet
  216. dimension := xlsxDimension{}
  217. dimension.Ref = fmt.Sprintf("A1:%s%d",
  218. numericToLetters(maxCell), maxRow+1)
  219. if dimension.Ref == "A1:A1" {
  220. dimension.Ref = "A1"
  221. }
  222. worksheet.Dimension = dimension
  223. return worksheet
  224. }
  225. func handleStyleForXLSX(style *Style, NumFmtId int, styles *xlsxStyleSheet) (XfId int) {
  226. xFont, xFill, xBorder, xCellStyleXf, xCellXf := style.makeXLSXStyleElements()
  227. fontId := styles.addFont(xFont)
  228. fillId := styles.addFill(xFill)
  229. // HACK - adding light grey fill, as in OO and Google
  230. greyfill := xlsxFill{}
  231. greyfill.PatternFill.PatternType = "lightGray"
  232. styles.addFill(greyfill)
  233. borderId := styles.addBorder(xBorder)
  234. xCellStyleXf.FontId = fontId
  235. xCellStyleXf.FillId = fillId
  236. xCellStyleXf.BorderId = borderId
  237. xCellStyleXf.NumFmtId = builtInNumFmtIndex_GENERAL
  238. xCellXf.FontId = fontId
  239. xCellXf.FillId = fillId
  240. xCellXf.BorderId = borderId
  241. xCellXf.NumFmtId = NumFmtId
  242. // apply the numFmtId when it is not the default cellxf
  243. if xCellXf.NumFmtId > 0 {
  244. xCellXf.ApplyNumberFormat = true
  245. }
  246. xCellStyleXf.Alignment.Horizontal = style.Alignment.Horizontal
  247. xCellStyleXf.Alignment.Indent = style.Alignment.Indent
  248. xCellStyleXf.Alignment.ShrinkToFit = style.Alignment.ShrinkToFit
  249. xCellStyleXf.Alignment.TextRotation = style.Alignment.TextRotation
  250. xCellStyleXf.Alignment.Vertical = style.Alignment.Vertical
  251. xCellStyleXf.Alignment.WrapText = style.Alignment.WrapText
  252. xCellXf.Alignment.Horizontal = style.Alignment.Horizontal
  253. xCellXf.Alignment.Indent = style.Alignment.Indent
  254. xCellXf.Alignment.ShrinkToFit = style.Alignment.ShrinkToFit
  255. xCellXf.Alignment.TextRotation = style.Alignment.TextRotation
  256. xCellXf.Alignment.Vertical = style.Alignment.Vertical
  257. xCellXf.Alignment.WrapText = style.Alignment.WrapText
  258. styles.addCellStyleXf(xCellStyleXf)
  259. XfId = styles.addCellXf(xCellXf)
  260. return
  261. }
  262. func handleNumFmtIdForXLSX(NumFmtId int, styles *xlsxStyleSheet) (XfId int) {
  263. xCellXf := makeXLSXCellElement()
  264. xCellXf.NumFmtId = NumFmtId
  265. if xCellXf.NumFmtId > 0 {
  266. xCellXf.ApplyNumberFormat = true
  267. }
  268. XfId = styles.addCellXf(xCellXf)
  269. return
  270. }