cell.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. package excelize
  2. import (
  3. "encoding/xml"
  4. "fmt"
  5. "strconv"
  6. "strings"
  7. "time"
  8. )
  9. // mergeCellsParser provides function to check merged cells in worksheet by
  10. // given axis.
  11. func (f *File) mergeCellsParser(xlsx *xlsxWorksheet, axis string) string {
  12. axis = strings.ToUpper(axis)
  13. if xlsx.MergeCells != nil {
  14. for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
  15. if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
  16. axis = strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0]
  17. }
  18. }
  19. }
  20. return axis
  21. }
  22. // SetCellValue provides function to set value of a cell. The following shows
  23. // the supported data types:
  24. //
  25. // int
  26. // int8
  27. // int16
  28. // int32
  29. // int64
  30. // uint
  31. // uint8
  32. // uint16
  33. // uint32
  34. // uint64
  35. // float32
  36. // float64
  37. // string
  38. // []byte
  39. // time.Time
  40. // nil
  41. //
  42. // Note that default date format is m/d/yy h:mm of time.Time type value. You can
  43. // set numbers format by SetCellStyle() method.
  44. func (f *File) SetCellValue(sheet, axis string, value interface{}) {
  45. switch t := value.(type) {
  46. case int:
  47. f.SetCellInt(sheet, axis, value.(int))
  48. case int8:
  49. f.SetCellInt(sheet, axis, int(value.(int8)))
  50. case int16:
  51. f.SetCellInt(sheet, axis, int(value.(int16)))
  52. case int32:
  53. f.SetCellInt(sheet, axis, int(value.(int32)))
  54. case int64:
  55. f.SetCellInt(sheet, axis, int(value.(int64)))
  56. case uint:
  57. f.SetCellInt(sheet, axis, int(value.(uint)))
  58. case uint8:
  59. f.SetCellInt(sheet, axis, int(value.(uint8)))
  60. case uint16:
  61. f.SetCellInt(sheet, axis, int(value.(uint16)))
  62. case uint32:
  63. f.SetCellInt(sheet, axis, int(value.(uint32)))
  64. case uint64:
  65. f.SetCellInt(sheet, axis, int(value.(uint64)))
  66. case float32:
  67. f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(value.(float32)), 'f', -1, 32))
  68. case float64:
  69. f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(value.(float64)), 'f', -1, 64))
  70. case string:
  71. f.SetCellStr(sheet, axis, t)
  72. case []byte:
  73. f.SetCellStr(sheet, axis, string(t))
  74. case time.Time:
  75. f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(timeToExcelTime(timeToUTCTime(value.(time.Time)))), 'f', -1, 64))
  76. f.setDefaultTimeStyle(sheet, axis)
  77. case nil:
  78. f.SetCellStr(sheet, axis, "")
  79. default:
  80. f.SetCellStr(sheet, axis, fmt.Sprintf("%v", value))
  81. }
  82. }
  83. // GetCellValue provides function to get formatted value from cell by given
  84. // worksheet name and axis in XLSX file. If it is possible to apply a format to
  85. // the cell value, it will do so, if not then an error will be returned, along
  86. // with the raw value of the cell.
  87. func (f *File) GetCellValue(sheet, axis string) string {
  88. xlsx := f.workSheetReader(sheet)
  89. axis = f.mergeCellsParser(xlsx, axis)
  90. row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
  91. if err != nil {
  92. return ""
  93. }
  94. xAxis := row - 1
  95. rows := len(xlsx.SheetData.Row)
  96. if rows > 1 {
  97. lastRow := xlsx.SheetData.Row[rows-1].R
  98. if lastRow >= rows {
  99. rows = lastRow
  100. }
  101. }
  102. if rows < xAxis {
  103. return ""
  104. }
  105. for k := range xlsx.SheetData.Row {
  106. if xlsx.SheetData.Row[k].R == row {
  107. for i := range xlsx.SheetData.Row[k].C {
  108. if axis == xlsx.SheetData.Row[k].C[i].R {
  109. val, _ := xlsx.SheetData.Row[k].C[i].getValueFrom(f, f.sharedStringsReader())
  110. return val
  111. }
  112. }
  113. }
  114. }
  115. return ""
  116. }
  117. // formattedValue provides function to returns a value after formatted. If it is
  118. // possible to apply a format to the cell value, it will do so, if not then an
  119. // error will be returned, along with the raw value of the cell.
  120. func (f *File) formattedValue(s int, v string) string {
  121. if s == 0 {
  122. return v
  123. }
  124. styleSheet := f.stylesReader()
  125. ok := builtInNumFmtFunc[styleSheet.CellXfs.Xf[s].NumFmtID]
  126. if ok != nil {
  127. return ok(styleSheet.CellXfs.Xf[s].NumFmtID, v)
  128. }
  129. return v
  130. }
  131. // GetCellStyle provides function to get cell style index by given worksheet
  132. // name and cell coordinates.
  133. func (f *File) GetCellStyle(sheet, axis string) int {
  134. xlsx := f.workSheetReader(sheet)
  135. axis = f.mergeCellsParser(xlsx, axis)
  136. col := string(strings.Map(letterOnlyMapF, axis))
  137. row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
  138. if err != nil {
  139. return 0
  140. }
  141. xAxis := row - 1
  142. yAxis := TitleToNumber(col)
  143. rows := xAxis + 1
  144. cell := yAxis + 1
  145. completeRow(xlsx, rows, cell)
  146. completeCol(xlsx, rows, cell)
  147. return f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
  148. }
  149. // GetCellFormula provides function to get formula from cell by given worksheet
  150. // name and axis in XLSX file.
  151. func (f *File) GetCellFormula(sheet, axis string) string {
  152. xlsx := f.workSheetReader(sheet)
  153. axis = f.mergeCellsParser(xlsx, axis)
  154. row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
  155. if err != nil {
  156. return ""
  157. }
  158. xAxis := row - 1
  159. rows := len(xlsx.SheetData.Row)
  160. if rows > 1 {
  161. lastRow := xlsx.SheetData.Row[rows-1].R
  162. if lastRow >= rows {
  163. rows = lastRow
  164. }
  165. }
  166. if rows < xAxis {
  167. return ""
  168. }
  169. for k := range xlsx.SheetData.Row {
  170. if xlsx.SheetData.Row[k].R == row {
  171. for i := range xlsx.SheetData.Row[k].C {
  172. if axis == xlsx.SheetData.Row[k].C[i].R {
  173. if xlsx.SheetData.Row[k].C[i].F != nil {
  174. return xlsx.SheetData.Row[k].C[i].F.Content
  175. }
  176. }
  177. }
  178. }
  179. }
  180. return ""
  181. }
  182. // SetCellFormula provides function to set cell formula by given string and
  183. // worksheet name.
  184. func (f *File) SetCellFormula(sheet, axis, formula string) {
  185. xlsx := f.workSheetReader(sheet)
  186. axis = f.mergeCellsParser(xlsx, axis)
  187. col := string(strings.Map(letterOnlyMapF, axis))
  188. row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
  189. if err != nil {
  190. return
  191. }
  192. xAxis := row - 1
  193. yAxis := TitleToNumber(col)
  194. rows := xAxis + 1
  195. cell := yAxis + 1
  196. completeRow(xlsx, rows, cell)
  197. completeCol(xlsx, rows, cell)
  198. if xlsx.SheetData.Row[xAxis].C[yAxis].F != nil {
  199. xlsx.SheetData.Row[xAxis].C[yAxis].F.Content = formula
  200. } else {
  201. f := xlsxF{
  202. Content: formula,
  203. }
  204. xlsx.SheetData.Row[xAxis].C[yAxis].F = &f
  205. }
  206. }
  207. // SetCellHyperLink provides function to set cell hyperlink by given worksheet
  208. // name and link URL address. LinkType defines two types of hyperlink "External"
  209. // for web site or "Location" for moving to one of cell in this workbook. The
  210. // below is example for external link.
  211. //
  212. // xlsx.SetCellHyperLink("Sheet1", "A3", "https://github.com/xuri/excelize", "External")
  213. // // Set underline and font color style for the cell.
  214. // style, _ := xlsx.NewStyle(`{"font":{"color":"#1265BE","underline":"single"}}`)
  215. // xlsx.SetCellStyle("Sheet1", "A3", "A3", style)
  216. //
  217. // A this is another example for "Location":
  218. //
  219. // xlsx.SetCellHyperLink("Sheet1", "A3", "Sheet1!A40", "Location")
  220. //
  221. func (f *File) SetCellHyperLink(sheet, axis, link, linkType string) {
  222. xlsx := f.workSheetReader(sheet)
  223. axis = f.mergeCellsParser(xlsx, axis)
  224. linkTypes := map[string]xlsxHyperlink{
  225. "External": {},
  226. "Location": {Location: link},
  227. }
  228. hyperlink, ok := linkTypes[linkType]
  229. if !ok || axis == "" {
  230. return
  231. }
  232. hyperlink.Ref = axis
  233. if linkType == "External" {
  234. rID := f.addSheetRelationships(sheet, SourceRelationshipHyperLink, link, linkType)
  235. hyperlink.RID = "rId" + strconv.Itoa(rID)
  236. }
  237. if xlsx.Hyperlinks == nil {
  238. xlsx.Hyperlinks = &xlsxHyperlinks{}
  239. }
  240. xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink, hyperlink)
  241. }
  242. // GetCellHyperLink provides function to get cell hyperlink by given worksheet
  243. // name and axis. Boolean type value link will be ture if the cell has a
  244. // hyperlink and the target is the address of the hyperlink. Otherwise, the
  245. // value of link will be false and the value of the target will be a blank
  246. // string. For example get hyperlink of Sheet1!H6:
  247. //
  248. // link, target := xlsx.GetCellHyperLink("Sheet1", "H6")
  249. //
  250. func (f *File) GetCellHyperLink(sheet, axis string) (bool, string) {
  251. var link bool
  252. var target string
  253. xlsx := f.workSheetReader(sheet)
  254. axis = f.mergeCellsParser(xlsx, axis)
  255. if xlsx.Hyperlinks == nil || axis == "" {
  256. return link, target
  257. }
  258. for h := range xlsx.Hyperlinks.Hyperlink {
  259. if xlsx.Hyperlinks.Hyperlink[h].Ref == axis {
  260. link = true
  261. target = xlsx.Hyperlinks.Hyperlink[h].Location
  262. if xlsx.Hyperlinks.Hyperlink[h].RID != "" {
  263. target = f.getSheetRelationshipsTargetByID(sheet, xlsx.Hyperlinks.Hyperlink[h].RID)
  264. }
  265. }
  266. }
  267. return link, target
  268. }
  269. // MergeCell provides function to merge cells by given coordinate area and sheet
  270. // name. For example create a merged cell of D3:E9 on Sheet1:
  271. //
  272. // xlsx.MergeCell("Sheet1", "D3", "E9")
  273. //
  274. // If you create a merged cell that overlaps with another existing merged cell,
  275. // those merged cells that already exist will be removed.
  276. func (f *File) MergeCell(sheet, hcell, vcell string) {
  277. if hcell == vcell {
  278. return
  279. }
  280. hcell = strings.ToUpper(hcell)
  281. vcell = strings.ToUpper(vcell)
  282. // Coordinate conversion, convert C1:B3 to 2,0,1,2.
  283. hcol := string(strings.Map(letterOnlyMapF, hcell))
  284. hrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, hcell))
  285. hyAxis := hrow - 1
  286. hxAxis := TitleToNumber(hcol)
  287. vcol := string(strings.Map(letterOnlyMapF, vcell))
  288. vrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, vcell))
  289. vyAxis := vrow - 1
  290. vxAxis := TitleToNumber(vcol)
  291. if vxAxis < hxAxis {
  292. hcell, vcell = vcell, hcell
  293. vxAxis, hxAxis = hxAxis, vxAxis
  294. }
  295. if vyAxis < hyAxis {
  296. hcell, vcell = vcell, hcell
  297. vyAxis, hyAxis = hyAxis, vyAxis
  298. }
  299. xlsx := f.workSheetReader(sheet)
  300. if xlsx.MergeCells != nil {
  301. mergeCell := xlsxMergeCell{}
  302. // Correct the coordinate area, such correct C1:B3 to B1:C3.
  303. mergeCell.Ref = ToAlphaString(hxAxis) + strconv.Itoa(hyAxis+1) + ":" + ToAlphaString(vxAxis) + strconv.Itoa(vyAxis+1)
  304. // Delete the merged cells of the overlapping area.
  305. for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
  306. if checkCellInArea(hcell, xlsx.MergeCells.Cells[i].Ref) || checkCellInArea(strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0], mergeCell.Ref) {
  307. xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:i], xlsx.MergeCells.Cells[i+1:]...)
  308. } else if checkCellInArea(vcell, xlsx.MergeCells.Cells[i].Ref) || checkCellInArea(strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[1], mergeCell.Ref) {
  309. xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:i], xlsx.MergeCells.Cells[i+1:]...)
  310. }
  311. }
  312. xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells, &mergeCell)
  313. } else {
  314. mergeCell := xlsxMergeCell{}
  315. // Correct the coordinate area, such correct C1:B3 to B1:C3.
  316. mergeCell.Ref = ToAlphaString(hxAxis) + strconv.Itoa(hyAxis+1) + ":" + ToAlphaString(vxAxis) + strconv.Itoa(vyAxis+1)
  317. mergeCells := xlsxMergeCells{}
  318. mergeCells.Cells = append(mergeCells.Cells, &mergeCell)
  319. xlsx.MergeCells = &mergeCells
  320. }
  321. }
  322. // SetCellInt provides function to set int type value of a cell by given
  323. // worksheet name, cell coordinates and cell value.
  324. func (f *File) SetCellInt(sheet, axis string, value int) {
  325. xlsx := f.workSheetReader(sheet)
  326. axis = f.mergeCellsParser(xlsx, axis)
  327. col := string(strings.Map(letterOnlyMapF, axis))
  328. row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
  329. if err != nil {
  330. return
  331. }
  332. xAxis := row - 1
  333. yAxis := TitleToNumber(col)
  334. rows := xAxis + 1
  335. cell := yAxis + 1
  336. completeRow(xlsx, rows, cell)
  337. completeCol(xlsx, rows, cell)
  338. xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
  339. xlsx.SheetData.Row[xAxis].C[yAxis].T = ""
  340. xlsx.SheetData.Row[xAxis].C[yAxis].V = strconv.Itoa(value)
  341. }
  342. // prepareCellStyle provides function to prepare style index of cell in
  343. // worksheet by given column index and style index.
  344. func (f *File) prepareCellStyle(xlsx *xlsxWorksheet, col, style int) int {
  345. if xlsx.Cols != nil && style == 0 {
  346. for _, v := range xlsx.Cols.Col {
  347. if v.Min <= col && col <= v.Max {
  348. style = v.Style
  349. }
  350. }
  351. }
  352. return style
  353. }
  354. // SetCellStr provides function to set string type value of a cell. Total number
  355. // of characters that a cell can contain 32767 characters.
  356. func (f *File) SetCellStr(sheet, axis, value string) {
  357. xlsx := f.workSheetReader(sheet)
  358. axis = f.mergeCellsParser(xlsx, axis)
  359. if len(value) > 32767 {
  360. value = value[0:32767]
  361. }
  362. col := string(strings.Map(letterOnlyMapF, axis))
  363. row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
  364. if err != nil {
  365. return
  366. }
  367. xAxis := row - 1
  368. yAxis := TitleToNumber(col)
  369. rows := xAxis + 1
  370. cell := yAxis + 1
  371. completeRow(xlsx, rows, cell)
  372. completeCol(xlsx, rows, cell)
  373. // Leading space(s) character detection.
  374. if len(value) > 0 {
  375. if value[0] == 32 {
  376. xlsx.SheetData.Row[xAxis].C[yAxis].XMLSpace = xml.Attr{
  377. Name: xml.Name{Space: NameSpaceXML, Local: "space"},
  378. Value: "preserve",
  379. }
  380. }
  381. }
  382. xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
  383. xlsx.SheetData.Row[xAxis].C[yAxis].T = "str"
  384. xlsx.SheetData.Row[xAxis].C[yAxis].V = value
  385. }
  386. // SetCellDefault provides function to set string type value of a cell as
  387. // default format without escaping the cell.
  388. func (f *File) SetCellDefault(sheet, axis, value string) {
  389. xlsx := f.workSheetReader(sheet)
  390. axis = f.mergeCellsParser(xlsx, axis)
  391. col := string(strings.Map(letterOnlyMapF, axis))
  392. row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
  393. if err != nil {
  394. return
  395. }
  396. xAxis := row - 1
  397. yAxis := TitleToNumber(col)
  398. rows := xAxis + 1
  399. cell := yAxis + 1
  400. completeRow(xlsx, rows, cell)
  401. completeCol(xlsx, rows, cell)
  402. xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
  403. xlsx.SheetData.Row[xAxis].C[yAxis].T = ""
  404. xlsx.SheetData.Row[xAxis].C[yAxis].V = value
  405. }
  406. // checkCellInArea provides function to determine if a given coordinate is
  407. // within an area.
  408. func checkCellInArea(cell, area string) bool {
  409. result := false
  410. cell = strings.ToUpper(cell)
  411. col := string(strings.Map(letterOnlyMapF, cell))
  412. row, _ := strconv.Atoi(strings.Map(intOnlyMapF, cell))
  413. xAxis := row - 1
  414. yAxis := TitleToNumber(col)
  415. ref := strings.Split(area, ":")
  416. hCol := string(strings.Map(letterOnlyMapF, ref[0]))
  417. hRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, ref[0]))
  418. hyAxis := hRow - 1
  419. hxAxis := TitleToNumber(hCol)
  420. vCol := string(strings.Map(letterOnlyMapF, ref[1]))
  421. vRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, ref[1]))
  422. vyAxis := vRow - 1
  423. vxAxis := TitleToNumber(vCol)
  424. if hxAxis <= yAxis && yAxis <= vxAxis && hyAxis <= xAxis && xAxis <= vyAxis {
  425. result = true
  426. }
  427. return result
  428. }