cell.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. package excelize
  2. import (
  3. "encoding/xml"
  4. "strconv"
  5. "strings"
  6. )
  7. // GetCellValue provides function to get formatted value from cell by given
  8. // sheet index and axis in XLSX file. If it is possible to apply a format to the
  9. // cell value, it will do so, if not then an error will be returned, along with
  10. // the raw value of the cell.
  11. func (f *File) GetCellValue(sheet, axis string) string {
  12. xlsx := f.workSheetReader(sheet)
  13. axis = strings.ToUpper(axis)
  14. if xlsx.MergeCells != nil {
  15. for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
  16. if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
  17. axis = strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0]
  18. }
  19. }
  20. }
  21. row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
  22. xAxis := row - 1
  23. rows := len(xlsx.SheetData.Row)
  24. if rows > 1 {
  25. lastRow := xlsx.SheetData.Row[rows-1].R
  26. if lastRow >= rows {
  27. rows = lastRow
  28. }
  29. }
  30. if rows < xAxis {
  31. return ""
  32. }
  33. for _, v := range xlsx.SheetData.Row {
  34. if v.R != row {
  35. continue
  36. }
  37. for _, r := range v.C {
  38. if axis != r.R {
  39. continue
  40. }
  41. switch r.T {
  42. case "s":
  43. shardStrings := xlsxSST{}
  44. xlsxSI := 0
  45. xlsxSI, _ = strconv.Atoi(r.V)
  46. xml.Unmarshal([]byte(f.readXML("xl/sharedStrings.xml")), &shardStrings)
  47. return f.formattedValue(r.S, shardStrings.SI[xlsxSI].T)
  48. case "str":
  49. return f.formattedValue(r.S, r.V)
  50. default:
  51. return f.formattedValue(r.S, r.V)
  52. }
  53. }
  54. }
  55. return ""
  56. }
  57. // formattedValue provides function to returns a value after formatted. If it is
  58. // possible to apply a format to the cell value, it will do so, if not then an
  59. // error will be returned, along with the raw value of the cell.
  60. func (f *File) formattedValue(s int, v string) string {
  61. if s == 0 {
  62. return v
  63. }
  64. var styleSheet xlsxStyleSheet
  65. xml.Unmarshal([]byte(f.readXML("xl/styles.xml")), &styleSheet)
  66. ok := builtInNumFmtFunc[styleSheet.CellXfs.Xf[s].NumFmtID]
  67. if ok != nil {
  68. return ok(styleSheet.CellXfs.Xf[s].NumFmtID, v)
  69. }
  70. return v
  71. }
  72. // GetCellFormula provides function to get formula from cell by given sheet
  73. // index and axis in XLSX file.
  74. func (f *File) GetCellFormula(sheet, axis string) string {
  75. xlsx := f.workSheetReader(sheet)
  76. axis = strings.ToUpper(axis)
  77. if xlsx.MergeCells != nil {
  78. for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
  79. if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
  80. axis = strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0]
  81. }
  82. }
  83. }
  84. row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
  85. xAxis := row - 1
  86. rows := len(xlsx.SheetData.Row)
  87. if rows > 1 {
  88. lastRow := xlsx.SheetData.Row[rows-1].R
  89. if lastRow >= rows {
  90. rows = lastRow
  91. }
  92. }
  93. if rows < xAxis {
  94. return ""
  95. }
  96. for _, v := range xlsx.SheetData.Row {
  97. if v.R != row {
  98. continue
  99. }
  100. for _, f := range v.C {
  101. if axis != f.R {
  102. continue
  103. }
  104. if f.F != nil {
  105. return f.F.Content
  106. }
  107. }
  108. }
  109. return ""
  110. }
  111. // SetCellFormula provides function to set cell formula by given string and
  112. // sheet index.
  113. func (f *File) SetCellFormula(sheet, axis, formula string) {
  114. xlsx := f.workSheetReader(sheet)
  115. axis = strings.ToUpper(axis)
  116. if xlsx.MergeCells != nil {
  117. for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
  118. if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
  119. axis = strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0]
  120. }
  121. }
  122. }
  123. col := string(strings.Map(letterOnlyMapF, axis))
  124. row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
  125. xAxis := row - 1
  126. yAxis := titleToNumber(col)
  127. rows := xAxis + 1
  128. cell := yAxis + 1
  129. completeRow(xlsx, rows, cell)
  130. completeCol(xlsx, rows, cell)
  131. if xlsx.SheetData.Row[xAxis].C[yAxis].F != nil {
  132. xlsx.SheetData.Row[xAxis].C[yAxis].F.Content = formula
  133. } else {
  134. f := xlsxF{
  135. Content: formula,
  136. }
  137. xlsx.SheetData.Row[xAxis].C[yAxis].F = &f
  138. }
  139. }
  140. // SetCellHyperLink provides function to set cell hyperlink by given sheet index
  141. // and link URL address. Only support external link currently.
  142. func (f *File) SetCellHyperLink(sheet, axis, link string) {
  143. xlsx := f.workSheetReader(sheet)
  144. axis = strings.ToUpper(axis)
  145. if xlsx.MergeCells != nil {
  146. for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
  147. if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
  148. axis = strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0]
  149. }
  150. }
  151. }
  152. rID := f.addSheetRelationships(sheet, SourceRelationshipHyperLink, link, "External")
  153. hyperlink := xlsxHyperlink{
  154. Ref: axis,
  155. RID: "rId" + strconv.Itoa(rID),
  156. }
  157. if xlsx.Hyperlinks != nil {
  158. xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink, hyperlink)
  159. } else {
  160. hyperlinks := xlsxHyperlinks{}
  161. hyperlinks.Hyperlink = append(hyperlinks.Hyperlink, hyperlink)
  162. xlsx.Hyperlinks = &hyperlinks
  163. }
  164. }
  165. // MergeCell provides function to merge cells by given coordinate area and sheet
  166. // name. For example create a merged cell of D3:E9 on Sheet1:
  167. //
  168. // xlsx.MergeCell("sheet1", "D3", "E9")
  169. //
  170. // If you create a merged cell that overlaps with another existing merged cell,
  171. // those merged cells that already exist will be removed.
  172. func (f *File) MergeCell(sheet, hcell, vcell string) {
  173. if hcell == vcell {
  174. return
  175. }
  176. hcell = strings.ToUpper(hcell)
  177. vcell = strings.ToUpper(vcell)
  178. // Coordinate conversion, convert C1:B3 to 2,0,1,2.
  179. hcol := string(strings.Map(letterOnlyMapF, hcell))
  180. hrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, hcell))
  181. hyAxis := hrow - 1
  182. hxAxis := titleToNumber(hcol)
  183. vcol := string(strings.Map(letterOnlyMapF, vcell))
  184. vrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, vcell))
  185. vyAxis := vrow - 1
  186. vxAxis := titleToNumber(vcol)
  187. if vxAxis < hxAxis {
  188. hcell, vcell = vcell, hcell
  189. vxAxis, hxAxis = hxAxis, vxAxis
  190. }
  191. if vyAxis < hyAxis {
  192. hcell, vcell = vcell, hcell
  193. vyAxis, hyAxis = hyAxis, vyAxis
  194. }
  195. xlsx := f.workSheetReader(sheet)
  196. if xlsx.MergeCells != nil {
  197. mergeCell := xlsxMergeCell{}
  198. // Correct the coordinate area, such correct C1:B3 to B1:C3.
  199. mergeCell.Ref = toAlphaString(hxAxis+1) + strconv.Itoa(hyAxis+1) + ":" + toAlphaString(vxAxis+1) + strconv.Itoa(vyAxis+1)
  200. // Delete the merged cells of the overlapping area.
  201. for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
  202. if checkCellInArea(hcell, xlsx.MergeCells.Cells[i].Ref) || checkCellInArea(strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0], mergeCell.Ref) {
  203. xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:i], xlsx.MergeCells.Cells[i+1:]...)
  204. } else if checkCellInArea(vcell, xlsx.MergeCells.Cells[i].Ref) || checkCellInArea(strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[1], mergeCell.Ref) {
  205. xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:i], xlsx.MergeCells.Cells[i+1:]...)
  206. }
  207. }
  208. xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells, &mergeCell)
  209. } else {
  210. mergeCell := xlsxMergeCell{}
  211. // Correct the coordinate area, such correct C1:B3 to B1:C3.
  212. mergeCell.Ref = toAlphaString(hxAxis+1) + strconv.Itoa(hyAxis+1) + ":" + toAlphaString(vxAxis+1) + strconv.Itoa(vyAxis+1)
  213. mergeCells := xlsxMergeCells{}
  214. mergeCells.Cells = append(mergeCells.Cells, &mergeCell)
  215. xlsx.MergeCells = &mergeCells
  216. }
  217. }
  218. // checkCellInArea provides function to determine if a given coordinate is
  219. // within an area.
  220. func checkCellInArea(cell, area string) bool {
  221. result := false
  222. cell = strings.ToUpper(cell)
  223. col := string(strings.Map(letterOnlyMapF, cell))
  224. row, _ := strconv.Atoi(strings.Map(intOnlyMapF, cell))
  225. xAxis := row - 1
  226. yAxis := titleToNumber(col)
  227. ref := strings.Split(area, ":")
  228. hCol := string(strings.Map(letterOnlyMapF, ref[0]))
  229. hRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, ref[0]))
  230. hyAxis := hRow - 1
  231. hxAxis := titleToNumber(hCol)
  232. vCol := string(strings.Map(letterOnlyMapF, ref[1]))
  233. vRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, ref[1]))
  234. vyAxis := vRow - 1
  235. vxAxis := titleToNumber(vCol)
  236. if hxAxis <= yAxis && yAxis <= vxAxis && hyAxis <= xAxis && xAxis <= vyAxis {
  237. result = true
  238. }
  239. return result
  240. }