cell.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  1. // Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
  2. // this source code is governed by a BSD-style license that can be found in
  3. // the LICENSE file.
  4. //
  5. // Package excelize providing a set of functions that allow you to write to
  6. // and read from XLSX / XLSM / XLTM files. Supports reading and writing
  7. // spreadsheet documents generated by Microsoft Exce™ 2007 and later. Supports
  8. // complex components by high compatibility, and provided streaming API for
  9. // generating or reading data from a worksheet with huge amounts of data. This
  10. // library needs Go version 1.10 or later.
  11. package excelize
  12. import (
  13. "encoding/xml"
  14. "errors"
  15. "fmt"
  16. "reflect"
  17. "strconv"
  18. "strings"
  19. "time"
  20. )
  21. const (
  22. // STCellFormulaTypeArray defined the formula is an array formula.
  23. STCellFormulaTypeArray = "array"
  24. // STCellFormulaTypeDataTable defined the formula is a data table formula.
  25. STCellFormulaTypeDataTable = "dataTable"
  26. // STCellFormulaTypeNormal defined the formula is a regular cell formula.
  27. STCellFormulaTypeNormal = "normal"
  28. // STCellFormulaTypeShared defined the formula is part of a shared formula.
  29. STCellFormulaTypeShared = "shared"
  30. )
  31. // GetCellValue provides a function to get formatted value from cell by given
  32. // worksheet name and axis in XLSX file. If it is possible to apply a format
  33. // to the cell value, it will do so, if not then an error will be returned,
  34. // along with the raw value of the cell.
  35. func (f *File) GetCellValue(sheet, axis string) (string, error) {
  36. return f.getCellStringFunc(sheet, axis, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {
  37. val, err := c.getValueFrom(f, f.sharedStringsReader())
  38. return val, true, err
  39. })
  40. }
  41. // SetCellValue provides a function to set value of a cell. The specified
  42. // coordinates should not be in the first row of the table. The following
  43. // shows the supported data types:
  44. //
  45. // int
  46. // int8
  47. // int16
  48. // int32
  49. // int64
  50. // uint
  51. // uint8
  52. // uint16
  53. // uint32
  54. // uint64
  55. // float32
  56. // float64
  57. // string
  58. // []byte
  59. // time.Duration
  60. // time.Time
  61. // bool
  62. // nil
  63. //
  64. // Note that default date format is m/d/yy h:mm of time.Time type value. You can
  65. // set numbers format by SetCellStyle() method.
  66. func (f *File) SetCellValue(sheet, axis string, value interface{}) error {
  67. var err error
  68. switch v := value.(type) {
  69. case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
  70. err = f.setCellIntFunc(sheet, axis, v)
  71. case float32:
  72. err = f.SetCellFloat(sheet, axis, float64(v), -1, 32)
  73. case float64:
  74. err = f.SetCellFloat(sheet, axis, v, -1, 64)
  75. case string:
  76. err = f.SetCellStr(sheet, axis, v)
  77. case []byte:
  78. err = f.SetCellStr(sheet, axis, string(v))
  79. case time.Duration:
  80. _, d := setCellDuration(v)
  81. err = f.SetCellDefault(sheet, axis, d)
  82. if err != nil {
  83. return err
  84. }
  85. err = f.setDefaultTimeStyle(sheet, axis, 21)
  86. case time.Time:
  87. err = f.setCellTimeFunc(sheet, axis, v)
  88. case bool:
  89. err = f.SetCellBool(sheet, axis, v)
  90. case nil:
  91. err = f.SetCellStr(sheet, axis, "")
  92. default:
  93. err = f.SetCellStr(sheet, axis, fmt.Sprint(value))
  94. }
  95. return err
  96. }
  97. // setCellIntFunc is a wrapper of SetCellInt.
  98. func (f *File) setCellIntFunc(sheet, axis string, value interface{}) error {
  99. var err error
  100. switch v := value.(type) {
  101. case int:
  102. err = f.SetCellInt(sheet, axis, v)
  103. case int8:
  104. err = f.SetCellInt(sheet, axis, int(v))
  105. case int16:
  106. err = f.SetCellInt(sheet, axis, int(v))
  107. case int32:
  108. err = f.SetCellInt(sheet, axis, int(v))
  109. case int64:
  110. err = f.SetCellInt(sheet, axis, int(v))
  111. case uint:
  112. err = f.SetCellInt(sheet, axis, int(v))
  113. case uint8:
  114. err = f.SetCellInt(sheet, axis, int(v))
  115. case uint16:
  116. err = f.SetCellInt(sheet, axis, int(v))
  117. case uint32:
  118. err = f.SetCellInt(sheet, axis, int(v))
  119. case uint64:
  120. err = f.SetCellInt(sheet, axis, int(v))
  121. }
  122. return err
  123. }
  124. // setCellTimeFunc provides a method to process time type of value for
  125. // SetCellValue.
  126. func (f *File) setCellTimeFunc(sheet, axis string, value time.Time) error {
  127. xlsx, err := f.workSheetReader(sheet)
  128. if err != nil {
  129. return err
  130. }
  131. cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
  132. if err != nil {
  133. return err
  134. }
  135. cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
  136. var isNum bool
  137. cellData.T, cellData.V, isNum, err = setCellTime(value)
  138. if err != nil {
  139. return err
  140. }
  141. if isNum {
  142. err = f.setDefaultTimeStyle(sheet, axis, 22)
  143. if err != nil {
  144. return err
  145. }
  146. }
  147. return err
  148. }
  149. func setCellTime(value time.Time) (t string, b string, isNum bool, err error) {
  150. var excelTime float64
  151. excelTime, err = timeToExcelTime(value)
  152. if err != nil {
  153. return
  154. }
  155. isNum = excelTime > 0
  156. if isNum {
  157. t, b = setCellDefault(strconv.FormatFloat(excelTime, 'f', -1, 64))
  158. } else {
  159. t, b = setCellDefault(value.Format(time.RFC3339Nano))
  160. }
  161. return
  162. }
  163. func setCellDuration(value time.Duration) (t string, v string) {
  164. v = strconv.FormatFloat(value.Seconds()/86400.0, 'f', -1, 32)
  165. return
  166. }
  167. // SetCellInt provides a function to set int type value of a cell by given
  168. // worksheet name, cell coordinates and cell value.
  169. func (f *File) SetCellInt(sheet, axis string, value int) error {
  170. xlsx, err := f.workSheetReader(sheet)
  171. if err != nil {
  172. return err
  173. }
  174. cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
  175. if err != nil {
  176. return err
  177. }
  178. cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
  179. cellData.T, cellData.V = setCellInt(value)
  180. return err
  181. }
  182. func setCellInt(value int) (t string, v string) {
  183. v = strconv.Itoa(value)
  184. return
  185. }
  186. // SetCellBool provides a function to set bool type value of a cell by given
  187. // worksheet name, cell name and cell value.
  188. func (f *File) SetCellBool(sheet, axis string, value bool) error {
  189. xlsx, err := f.workSheetReader(sheet)
  190. if err != nil {
  191. return err
  192. }
  193. cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
  194. if err != nil {
  195. return err
  196. }
  197. cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
  198. cellData.T, cellData.V = setCellBool(value)
  199. return err
  200. }
  201. func setCellBool(value bool) (t string, v string) {
  202. t = "b"
  203. if value {
  204. v = "1"
  205. } else {
  206. v = "0"
  207. }
  208. return
  209. }
  210. // SetCellFloat sets a floating point value into a cell. The prec parameter
  211. // specifies how many places after the decimal will be shown while -1 is a
  212. // special value that will use as many decimal places as necessary to
  213. // represent the number. bitSize is 32 or 64 depending on if a float32 or
  214. // float64 was originally used for the value. For Example:
  215. //
  216. // var x float32 = 1.325
  217. // f.SetCellFloat("Sheet1", "A1", float64(x), 2, 32)
  218. //
  219. func (f *File) SetCellFloat(sheet, axis string, value float64, prec, bitSize int) error {
  220. xlsx, err := f.workSheetReader(sheet)
  221. if err != nil {
  222. return err
  223. }
  224. cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
  225. if err != nil {
  226. return err
  227. }
  228. cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
  229. cellData.T, cellData.V = setCellFloat(value, prec, bitSize)
  230. return err
  231. }
  232. func setCellFloat(value float64, prec, bitSize int) (t string, v string) {
  233. v = strconv.FormatFloat(value, 'f', prec, bitSize)
  234. return
  235. }
  236. // SetCellStr provides a function to set string type value of a cell. Total
  237. // number of characters that a cell can contain 32767 characters.
  238. func (f *File) SetCellStr(sheet, axis, value string) error {
  239. xlsx, err := f.workSheetReader(sheet)
  240. if err != nil {
  241. return err
  242. }
  243. cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
  244. if err != nil {
  245. return err
  246. }
  247. cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
  248. cellData.T, cellData.V = f.setCellString(value)
  249. return err
  250. }
  251. // setCellString provides a function to set string type to shared string
  252. // table.
  253. func (f *File) setCellString(value string) (t string, v string) {
  254. if len(value) > TotalCellChars {
  255. value = value[0:TotalCellChars]
  256. }
  257. t = "s"
  258. v = strconv.Itoa(f.setSharedString(value))
  259. return
  260. }
  261. // setSharedString provides a function to add string to the share string table.
  262. func (f *File) setSharedString(val string) int {
  263. sst := f.sharedStringsReader()
  264. f.Lock()
  265. defer f.Unlock()
  266. if i, ok := f.sharedStringsMap[val]; ok {
  267. return i
  268. }
  269. sst.Count++
  270. sst.UniqueCount++
  271. t := xlsxT{Val: val}
  272. // Leading and ending space(s) character detection.
  273. if len(val) > 0 && (val[0] == 32 || val[len(val)-1] == 32) {
  274. ns := xml.Attr{
  275. Name: xml.Name{Space: NameSpaceXML, Local: "space"},
  276. Value: "preserve",
  277. }
  278. t.Space = ns
  279. }
  280. sst.SI = append(sst.SI, xlsxSI{T: &t})
  281. f.sharedStringsMap[val] = sst.UniqueCount - 1
  282. return sst.UniqueCount - 1
  283. }
  284. // setCellStr provides a function to set string type to cell.
  285. func setCellStr(value string) (t string, v string, ns xml.Attr) {
  286. if len(value) > TotalCellChars {
  287. value = value[0:TotalCellChars]
  288. }
  289. // Leading and ending space(s) character detection.
  290. if len(value) > 0 && (value[0] == 32 || value[len(value)-1] == 32) {
  291. ns = xml.Attr{
  292. Name: xml.Name{Space: NameSpaceXML, Local: "space"},
  293. Value: "preserve",
  294. }
  295. }
  296. t = "str"
  297. v = value
  298. return
  299. }
  300. // SetCellDefault provides a function to set string type value of a cell as
  301. // default format without escaping the cell.
  302. func (f *File) SetCellDefault(sheet, axis, value string) error {
  303. xlsx, err := f.workSheetReader(sheet)
  304. if err != nil {
  305. return err
  306. }
  307. cellData, col, _, err := f.prepareCell(xlsx, sheet, axis)
  308. if err != nil {
  309. return err
  310. }
  311. cellData.S = f.prepareCellStyle(xlsx, col, cellData.S)
  312. cellData.T, cellData.V = setCellDefault(value)
  313. return err
  314. }
  315. func setCellDefault(value string) (t string, v string) {
  316. v = value
  317. return
  318. }
  319. // GetCellFormula provides a function to get formula from cell by given
  320. // worksheet name and axis in XLSX file.
  321. func (f *File) GetCellFormula(sheet, axis string) (string, error) {
  322. return f.getCellStringFunc(sheet, axis, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {
  323. if c.F == nil {
  324. return "", false, nil
  325. }
  326. if c.F.T == STCellFormulaTypeShared {
  327. return getSharedForumula(x, c.F.Si), true, nil
  328. }
  329. return c.F.Content, true, nil
  330. })
  331. }
  332. // FormulaOpts can be passed to SetCellFormula to use other formula types.
  333. type FormulaOpts struct {
  334. Type *string // Formula type
  335. Ref *string // Shared formula ref
  336. }
  337. // SetCellFormula provides a function to set cell formula by given string and
  338. // worksheet name.
  339. func (f *File) SetCellFormula(sheet, axis, formula string, opts ...FormulaOpts) error {
  340. xlsx, err := f.workSheetReader(sheet)
  341. if err != nil {
  342. return err
  343. }
  344. cellData, _, _, err := f.prepareCell(xlsx, sheet, axis)
  345. if err != nil {
  346. return err
  347. }
  348. if formula == "" {
  349. cellData.F = nil
  350. f.deleteCalcChain(f.getSheetID(sheet), axis)
  351. return err
  352. }
  353. if cellData.F != nil {
  354. cellData.F.Content = formula
  355. } else {
  356. cellData.F = &xlsxF{Content: formula}
  357. }
  358. for _, o := range opts {
  359. if o.Type != nil {
  360. cellData.F.T = *o.Type
  361. }
  362. if o.Ref != nil {
  363. cellData.F.Ref = *o.Ref
  364. }
  365. }
  366. return err
  367. }
  368. // GetCellHyperLink provides a function to get cell hyperlink by given
  369. // worksheet name and axis. Boolean type value link will be ture if the cell
  370. // has a hyperlink and the target is the address of the hyperlink. Otherwise,
  371. // the value of link will be false and the value of the target will be a blank
  372. // string. For example get hyperlink of Sheet1!H6:
  373. //
  374. // link, target, err := f.GetCellHyperLink("Sheet1", "H6")
  375. //
  376. func (f *File) GetCellHyperLink(sheet, axis string) (bool, string, error) {
  377. // Check for correct cell name
  378. if _, _, err := SplitCellName(axis); err != nil {
  379. return false, "", err
  380. }
  381. xlsx, err := f.workSheetReader(sheet)
  382. if err != nil {
  383. return false, "", err
  384. }
  385. axis, err = f.mergeCellsParser(xlsx, axis)
  386. if err != nil {
  387. return false, "", err
  388. }
  389. if xlsx.Hyperlinks != nil {
  390. for _, link := range xlsx.Hyperlinks.Hyperlink {
  391. if link.Ref == axis {
  392. if link.RID != "" {
  393. return true, f.getSheetRelationshipsTargetByID(sheet, link.RID), err
  394. }
  395. return true, link.Location, err
  396. }
  397. }
  398. }
  399. return false, "", err
  400. }
  401. // SetCellHyperLink provides a function to set cell hyperlink by given
  402. // worksheet name and link URL address. LinkType defines two types of
  403. // hyperlink "External" for web site or "Location" for moving to one of cell
  404. // in this workbook. Maximum limit hyperlinks in a worksheet is 65530. The
  405. // below is example for external link.
  406. //
  407. // err := f.SetCellHyperLink("Sheet1", "A3", "https://github.com/360EntSecGroup-Skylar/excelize", "External")
  408. // // Set underline and font color style for the cell.
  409. // style, err := f.NewStyle(`{"font":{"color":"#1265BE","underline":"single"}}`)
  410. // err = f.SetCellStyle("Sheet1", "A3", "A3", style)
  411. //
  412. // A this is another example for "Location":
  413. //
  414. // err := f.SetCellHyperLink("Sheet1", "A3", "Sheet1!A40", "Location")
  415. //
  416. func (f *File) SetCellHyperLink(sheet, axis, link, linkType string) error {
  417. // Check for correct cell name
  418. if _, _, err := SplitCellName(axis); err != nil {
  419. return err
  420. }
  421. xlsx, err := f.workSheetReader(sheet)
  422. if err != nil {
  423. return err
  424. }
  425. axis, err = f.mergeCellsParser(xlsx, axis)
  426. if err != nil {
  427. return err
  428. }
  429. var linkData xlsxHyperlink
  430. if xlsx.Hyperlinks == nil {
  431. xlsx.Hyperlinks = new(xlsxHyperlinks)
  432. }
  433. if len(xlsx.Hyperlinks.Hyperlink) > TotalSheetHyperlinks {
  434. return errors.New("over maximum limit hyperlinks in a worksheet")
  435. }
  436. switch linkType {
  437. case "External":
  438. linkData = xlsxHyperlink{
  439. Ref: axis,
  440. }
  441. sheetPath := f.sheetMap[trimSheetName(sheet)]
  442. sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels"
  443. rID := f.addRels(sheetRels, SourceRelationshipHyperLink, link, linkType)
  444. linkData.RID = "rId" + strconv.Itoa(rID)
  445. f.addSheetNameSpace(sheet, SourceRelationship)
  446. case "Location":
  447. linkData = xlsxHyperlink{
  448. Ref: axis,
  449. Location: link,
  450. }
  451. default:
  452. return fmt.Errorf("invalid link type %q", linkType)
  453. }
  454. xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink, linkData)
  455. return nil
  456. }
  457. // SetCellRichText provides a function to set cell with rich text by given
  458. // worksheet. For example, set rich text on the A1 cell of the worksheet named
  459. // Sheet1:
  460. //
  461. // package main
  462. //
  463. // import (
  464. // "fmt"
  465. //
  466. // "github.com/360EntSecGroup-Skylar/excelize"
  467. // )
  468. //
  469. // func main() {
  470. // f := excelize.NewFile()
  471. // if err := f.SetRowHeight("Sheet1", 1, 35); err != nil {
  472. // fmt.Println(err)
  473. // return
  474. // }
  475. // if err := f.SetColWidth("Sheet1", "A", "A", 44); err != nil {
  476. // fmt.Println(err)
  477. // return
  478. // }
  479. // if err := f.SetCellRichText("Sheet1", "A1", []excelize.RichTextRun{
  480. // {
  481. // Text: "bold",
  482. // Font: &excelize.Font{
  483. // Bold: true,
  484. // Color: "2354e8",
  485. // Family: "Times New Roman",
  486. // },
  487. // },
  488. // {
  489. // Text: " and ",
  490. // Font: &excelize.Font{
  491. // Family: "Times New Roman",
  492. // },
  493. // },
  494. // {
  495. // Text: " italic",
  496. // Font: &excelize.Font{
  497. // Bold: true,
  498. // Color: "e83723",
  499. // Italic: true,
  500. // Family: "Times New Roman",
  501. // },
  502. // },
  503. // {
  504. // Text: "text with color and font-family,",
  505. // Font: &excelize.Font{
  506. // Bold: true,
  507. // Color: "2354e8",
  508. // Family: "Times New Roman",
  509. // },
  510. // },
  511. // {
  512. // Text: "\r\nlarge text with ",
  513. // Font: &excelize.Font{
  514. // Size: 14,
  515. // Color: "ad23e8",
  516. // },
  517. // },
  518. // {
  519. // Text: "strike",
  520. // Font: &excelize.Font{
  521. // Color: "e89923",
  522. // Strike: true,
  523. // },
  524. // },
  525. // {
  526. // Text: " and ",
  527. // Font: &excelize.Font{
  528. // Size: 14,
  529. // Color: "ad23e8",
  530. // },
  531. // },
  532. // {
  533. // Text: "underline.",
  534. // Font: &excelize.Font{
  535. // Color: "23e833",
  536. // Underline: "single",
  537. // },
  538. // },
  539. // }); err != nil {
  540. // fmt.Println(err)
  541. // return
  542. // }
  543. // style, err := f.NewStyle(&excelize.Style{
  544. // Alignment: &excelize.Alignment{
  545. // WrapText: true,
  546. // },
  547. // })
  548. // if err != nil {
  549. // fmt.Println(err)
  550. // return
  551. // }
  552. // if err := f.SetCellStyle("Sheet1", "A1", "A1", style); err != nil {
  553. // fmt.Println(err)
  554. // return
  555. // }
  556. // if err := f.SaveAs("Book1.xlsx"); err != nil {
  557. // fmt.Println(err)
  558. // }
  559. // }
  560. //
  561. func (f *File) SetCellRichText(sheet, cell string, runs []RichTextRun) error {
  562. ws, err := f.workSheetReader(sheet)
  563. if err != nil {
  564. return err
  565. }
  566. cellData, col, _, err := f.prepareCell(ws, sheet, cell)
  567. if err != nil {
  568. return err
  569. }
  570. cellData.S = f.prepareCellStyle(ws, col, cellData.S)
  571. si := xlsxSI{}
  572. sst := f.sharedStringsReader()
  573. textRuns := []xlsxR{}
  574. for _, textRun := range runs {
  575. run := xlsxR{T: &xlsxT{Val: textRun.Text}}
  576. if strings.ContainsAny(textRun.Text, "\r\n ") {
  577. run.T.Space = xml.Attr{Name: xml.Name{Space: NameSpaceXML, Local: "space"}, Value: "preserve"}
  578. }
  579. fnt := textRun.Font
  580. if fnt != nil {
  581. rpr := xlsxRPr{}
  582. if fnt.Bold {
  583. rpr.B = " "
  584. }
  585. if fnt.Italic {
  586. rpr.I = " "
  587. }
  588. if fnt.Strike {
  589. rpr.Strike = " "
  590. }
  591. if fnt.Underline != "" {
  592. rpr.U = &attrValString{Val: &fnt.Underline}
  593. }
  594. if fnt.Family != "" {
  595. rpr.RFont = &attrValString{Val: &fnt.Family}
  596. }
  597. if fnt.Size > 0.0 {
  598. rpr.Sz = &attrValFloat{Val: &fnt.Size}
  599. }
  600. if fnt.Color != "" {
  601. rpr.Color = &xlsxColor{RGB: getPaletteColor(fnt.Color)}
  602. }
  603. run.RPr = &rpr
  604. }
  605. textRuns = append(textRuns, run)
  606. }
  607. si.R = textRuns
  608. sst.SI = append(sst.SI, si)
  609. sst.Count++
  610. sst.UniqueCount++
  611. cellData.T, cellData.V = "s", strconv.Itoa(len(sst.SI)-1)
  612. return err
  613. }
  614. // SetSheetRow writes an array to row by given worksheet name, starting
  615. // coordinate and a pointer to array type 'slice'. For example, writes an
  616. // array to row 6 start with the cell B6 on Sheet1:
  617. //
  618. // err := f.SetSheetRow("Sheet1", "B6", &[]interface{}{"1", nil, 2})
  619. //
  620. func (f *File) SetSheetRow(sheet, axis string, slice interface{}) error {
  621. col, row, err := CellNameToCoordinates(axis)
  622. if err != nil {
  623. return err
  624. }
  625. // Make sure 'slice' is a Ptr to Slice
  626. v := reflect.ValueOf(slice)
  627. if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Slice {
  628. return errors.New("pointer to slice expected")
  629. }
  630. v = v.Elem()
  631. for i := 0; i < v.Len(); i++ {
  632. cell, err := CoordinatesToCellName(col+i, row)
  633. // Error should never happens here. But keep checking to early detect regresions
  634. // if it will be introduced in future.
  635. if err != nil {
  636. return err
  637. }
  638. if err := f.SetCellValue(sheet, cell, v.Index(i).Interface()); err != nil {
  639. return err
  640. }
  641. }
  642. return err
  643. }
  644. // getCellInfo does common preparation for all SetCell* methods.
  645. func (f *File) prepareCell(xlsx *xlsxWorksheet, sheet, cell string) (*xlsxC, int, int, error) {
  646. xlsx.Lock()
  647. defer xlsx.Unlock()
  648. var err error
  649. cell, err = f.mergeCellsParser(xlsx, cell)
  650. if err != nil {
  651. return nil, 0, 0, err
  652. }
  653. col, row, err := CellNameToCoordinates(cell)
  654. if err != nil {
  655. return nil, 0, 0, err
  656. }
  657. prepareSheetXML(xlsx, col, row)
  658. return &xlsx.SheetData.Row[row-1].C[col-1], col, row, err
  659. }
  660. // getCellStringFunc does common value extraction workflow for all GetCell*
  661. // methods. Passed function implements specific part of required logic.
  662. func (f *File) getCellStringFunc(sheet, axis string, fn func(x *xlsxWorksheet, c *xlsxC) (string, bool, error)) (string, error) {
  663. xlsx, err := f.workSheetReader(sheet)
  664. if err != nil {
  665. return "", err
  666. }
  667. axis, err = f.mergeCellsParser(xlsx, axis)
  668. if err != nil {
  669. return "", err
  670. }
  671. _, row, err := CellNameToCoordinates(axis)
  672. if err != nil {
  673. return "", err
  674. }
  675. xlsx.Lock()
  676. defer xlsx.Unlock()
  677. lastRowNum := 0
  678. if l := len(xlsx.SheetData.Row); l > 0 {
  679. lastRowNum = xlsx.SheetData.Row[l-1].R
  680. }
  681. // keep in mind: row starts from 1
  682. if row > lastRowNum {
  683. return "", nil
  684. }
  685. for rowIdx := range xlsx.SheetData.Row {
  686. rowData := &xlsx.SheetData.Row[rowIdx]
  687. if rowData.R != row {
  688. continue
  689. }
  690. for colIdx := range rowData.C {
  691. colData := &rowData.C[colIdx]
  692. if axis != colData.R {
  693. continue
  694. }
  695. val, ok, err := fn(xlsx, colData)
  696. if err != nil {
  697. return "", err
  698. }
  699. if ok {
  700. return val, nil
  701. }
  702. }
  703. }
  704. return "", nil
  705. }
  706. // formattedValue provides a function to returns a value after formatted. If
  707. // it is possible to apply a format to the cell value, it will do so, if not
  708. // then an error will be returned, along with the raw value of the cell.
  709. func (f *File) formattedValue(s int, v string) string {
  710. if s == 0 {
  711. return v
  712. }
  713. styleSheet := f.stylesReader()
  714. ok := builtInNumFmtFunc[*styleSheet.CellXfs.Xf[s].NumFmtID]
  715. if ok != nil {
  716. return ok(*styleSheet.CellXfs.Xf[s].NumFmtID, v)
  717. }
  718. return v
  719. }
  720. // prepareCellStyle provides a function to prepare style index of cell in
  721. // worksheet by given column index and style index.
  722. func (f *File) prepareCellStyle(xlsx *xlsxWorksheet, col, style int) int {
  723. if xlsx.Cols != nil && style == 0 {
  724. for _, c := range xlsx.Cols.Col {
  725. if c.Min <= col && col <= c.Max {
  726. style = c.Style
  727. }
  728. }
  729. }
  730. return style
  731. }
  732. // mergeCellsParser provides a function to check merged cells in worksheet by
  733. // given axis.
  734. func (f *File) mergeCellsParser(xlsx *xlsxWorksheet, axis string) (string, error) {
  735. axis = strings.ToUpper(axis)
  736. if xlsx.MergeCells != nil {
  737. for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
  738. ok, err := f.checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref)
  739. if err != nil {
  740. return axis, err
  741. }
  742. if ok {
  743. axis = strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0]
  744. }
  745. }
  746. }
  747. return axis, nil
  748. }
  749. // checkCellInArea provides a function to determine if a given coordinate is
  750. // within an area.
  751. func (f *File) checkCellInArea(cell, area string) (bool, error) {
  752. col, row, err := CellNameToCoordinates(cell)
  753. if err != nil {
  754. return false, err
  755. }
  756. rng := strings.Split(area, ":")
  757. if len(rng) != 2 {
  758. return false, err
  759. }
  760. coordinates, err := f.areaRefToCoordinates(area)
  761. if err != nil {
  762. return false, err
  763. }
  764. return cellInRef([]int{col, row}, coordinates), err
  765. }
  766. // cellInRef provides a function to determine if a given range is within an
  767. // range.
  768. func cellInRef(cell, ref []int) bool {
  769. return cell[0] >= ref[0] && cell[0] <= ref[2] && cell[1] >= ref[1] && cell[1] <= ref[3]
  770. }
  771. // isOverlap find if the given two rectangles overlap or not.
  772. func isOverlap(rect1, rect2 []int) bool {
  773. return cellInRef([]int{rect1[0], rect1[1]}, rect2) ||
  774. cellInRef([]int{rect1[2], rect1[1]}, rect2) ||
  775. cellInRef([]int{rect1[0], rect1[3]}, rect2) ||
  776. cellInRef([]int{rect1[2], rect1[3]}, rect2) ||
  777. cellInRef([]int{rect2[0], rect2[1]}, rect1) ||
  778. cellInRef([]int{rect2[2], rect2[1]}, rect1) ||
  779. cellInRef([]int{rect2[0], rect2[3]}, rect1) ||
  780. cellInRef([]int{rect2[2], rect2[3]}, rect1)
  781. }
  782. // getSharedForumula find a cell contains the same formula as another cell,
  783. // the "shared" value can be used for the t attribute and the si attribute can
  784. // be used to refer to the cell containing the formula. Two formulas are
  785. // considered to be the same when their respective representations in
  786. // R1C1-reference notation, are the same.
  787. //
  788. // Note that this function not validate ref tag to check the cell if or not in
  789. // allow area, and always return origin shared formula.
  790. func getSharedForumula(xlsx *xlsxWorksheet, si string) string {
  791. for _, r := range xlsx.SheetData.Row {
  792. for _, c := range r.C {
  793. if c.F != nil && c.F.Ref != "" && c.F.T == STCellFormulaTypeShared && c.F.Si == si {
  794. return c.F.Content
  795. }
  796. }
  797. }
  798. return ""
  799. }