cell.go 7.7 KB

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