rows.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. // Copyright 2016 - 2023 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 and
  6. // read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and
  7. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
  8. // Supports complex components by high compatibility, and provided streaming
  9. // API for generating or reading data from a worksheet with huge amounts of
  10. // data. This library needs Go version 1.16 or later.
  11. package excelize
  12. import (
  13. "bytes"
  14. "encoding/xml"
  15. "fmt"
  16. "io"
  17. "math"
  18. "os"
  19. "strconv"
  20. "github.com/mohae/deepcopy"
  21. )
  22. // GetRows return all the rows in a sheet by given worksheet name, returned as
  23. // a two-dimensional array, where the value of the cell is converted to the
  24. // string type. If the cell format can be applied to the value of the cell,
  25. // the applied value will be used, otherwise the original value will be used.
  26. // GetRows fetched the rows with value or formula cells, the continually blank
  27. // cells in the tail of each row will be skipped, so the length of each row
  28. // may be inconsistent.
  29. //
  30. // For example, get and traverse the value of all cells by rows on a worksheet
  31. // named 'Sheet1':
  32. //
  33. // rows, err := f.GetRows("Sheet1")
  34. // if err != nil {
  35. // fmt.Println(err)
  36. // return
  37. // }
  38. // for _, row := range rows {
  39. // for _, colCell := range row {
  40. // fmt.Print(colCell, "\t")
  41. // }
  42. // fmt.Println()
  43. // }
  44. func (f *File) GetRows(sheet string, opts ...Options) ([][]string, error) {
  45. rows, err := f.Rows(sheet)
  46. if err != nil {
  47. return nil, err
  48. }
  49. results, cur, max := make([][]string, 0, 64), 0, 0
  50. for rows.Next() {
  51. cur++
  52. row, err := rows.Columns(opts...)
  53. if err != nil {
  54. break
  55. }
  56. results = append(results, row)
  57. if len(row) > 0 {
  58. max = cur
  59. }
  60. }
  61. return results[:max], rows.Close()
  62. }
  63. // Rows defines an iterator to a sheet.
  64. type Rows struct {
  65. err error
  66. curRow, seekRow int
  67. needClose, rawCellValue bool
  68. sheet string
  69. f *File
  70. tempFile *os.File
  71. sst *xlsxSST
  72. decoder *xml.Decoder
  73. token xml.Token
  74. curRowOpts, seekRowOpts RowOpts
  75. }
  76. // Next will return true if find the next row element.
  77. func (rows *Rows) Next() bool {
  78. rows.seekRow++
  79. if rows.curRow >= rows.seekRow {
  80. rows.curRowOpts = rows.seekRowOpts
  81. return true
  82. }
  83. for {
  84. token, _ := rows.decoder.Token()
  85. if token == nil {
  86. return false
  87. }
  88. switch xmlElement := token.(type) {
  89. case xml.StartElement:
  90. if xmlElement.Name.Local == "row" {
  91. rows.curRow++
  92. if rowNum, _ := attrValToInt("r", xmlElement.Attr); rowNum != 0 {
  93. rows.curRow = rowNum
  94. }
  95. rows.token = token
  96. rows.curRowOpts = extractRowOpts(xmlElement.Attr)
  97. return true
  98. }
  99. case xml.EndElement:
  100. if xmlElement.Name.Local == "sheetData" {
  101. return false
  102. }
  103. }
  104. }
  105. }
  106. // GetRowOpts will return the RowOpts of the current row.
  107. func (rows *Rows) GetRowOpts() RowOpts {
  108. return rows.curRowOpts
  109. }
  110. // Error will return the error when the error occurs.
  111. func (rows *Rows) Error() error {
  112. return rows.err
  113. }
  114. // Close closes the open worksheet XML file in the system temporary
  115. // directory.
  116. func (rows *Rows) Close() error {
  117. if rows.tempFile != nil {
  118. return rows.tempFile.Close()
  119. }
  120. return nil
  121. }
  122. // Columns return the current row's column values. This fetches the worksheet
  123. // data as a stream, returns each cell in a row as is, and will not skip empty
  124. // rows in the tail of the worksheet.
  125. func (rows *Rows) Columns(opts ...Options) ([]string, error) {
  126. if rows.curRow > rows.seekRow {
  127. return nil, nil
  128. }
  129. var rowIterator rowXMLIterator
  130. var token xml.Token
  131. rows.rawCellValue = getOptions(opts...).RawCellValue
  132. if rows.sst, rowIterator.err = rows.f.sharedStringsReader(); rowIterator.err != nil {
  133. return rowIterator.cells, rowIterator.err
  134. }
  135. for {
  136. if rows.token != nil {
  137. token = rows.token
  138. } else if token, _ = rows.decoder.Token(); token == nil {
  139. break
  140. }
  141. switch xmlElement := token.(type) {
  142. case xml.StartElement:
  143. rowIterator.inElement = xmlElement.Name.Local
  144. if rowIterator.inElement == "row" {
  145. rowNum := 0
  146. if rowNum, rowIterator.err = attrValToInt("r", xmlElement.Attr); rowNum != 0 {
  147. rows.curRow = rowNum
  148. } else if rows.token == nil {
  149. rows.curRow++
  150. }
  151. rows.token = token
  152. rows.seekRowOpts = extractRowOpts(xmlElement.Attr)
  153. if rows.curRow > rows.seekRow {
  154. rows.token = nil
  155. return rowIterator.cells, rowIterator.err
  156. }
  157. }
  158. if rows.rowXMLHandler(&rowIterator, &xmlElement, rows.rawCellValue); rowIterator.err != nil {
  159. rows.token = nil
  160. return rowIterator.cells, rowIterator.err
  161. }
  162. rows.token = nil
  163. case xml.EndElement:
  164. if xmlElement.Name.Local == "sheetData" {
  165. return rowIterator.cells, rowIterator.err
  166. }
  167. }
  168. }
  169. return rowIterator.cells, rowIterator.err
  170. }
  171. // extractRowOpts extract row element attributes.
  172. func extractRowOpts(attrs []xml.Attr) RowOpts {
  173. rowOpts := RowOpts{Height: defaultRowHeight}
  174. if styleID, err := attrValToInt("s", attrs); err == nil && styleID > 0 && styleID < MaxCellStyles {
  175. rowOpts.StyleID = styleID
  176. }
  177. if hidden, err := attrValToBool("hidden", attrs); err == nil {
  178. rowOpts.Hidden = hidden
  179. }
  180. if height, err := attrValToFloat("ht", attrs); err == nil {
  181. rowOpts.Height = height
  182. }
  183. return rowOpts
  184. }
  185. // appendSpace append blank characters to slice by given length and source slice.
  186. func appendSpace(l int, s []string) []string {
  187. for i := 1; i < l; i++ {
  188. s = append(s, "")
  189. }
  190. return s
  191. }
  192. // ErrSheetNotExist defines an error of sheet that does not exist
  193. type ErrSheetNotExist struct {
  194. SheetName string
  195. }
  196. func (err ErrSheetNotExist) Error() string {
  197. return fmt.Sprintf("sheet %s does not exist", err.SheetName)
  198. }
  199. // rowXMLIterator defined runtime use field for the worksheet row SAX parser.
  200. type rowXMLIterator struct {
  201. err error
  202. inElement string
  203. cellCol, cellRow int
  204. cells []string
  205. }
  206. // rowXMLHandler parse the row XML element of the worksheet.
  207. func (rows *Rows) rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.StartElement, raw bool) {
  208. if rowIterator.inElement == "c" {
  209. rowIterator.cellCol++
  210. colCell := xlsxC{}
  211. _ = rows.decoder.DecodeElement(&colCell, xmlElement)
  212. if colCell.R != "" {
  213. if rowIterator.cellCol, _, rowIterator.err = CellNameToCoordinates(colCell.R); rowIterator.err != nil {
  214. return
  215. }
  216. }
  217. blank := rowIterator.cellCol - len(rowIterator.cells)
  218. if val, _ := colCell.getValueFrom(rows.f, rows.sst, raw); val != "" || colCell.F != nil {
  219. rowIterator.cells = append(appendSpace(blank, rowIterator.cells), val)
  220. }
  221. }
  222. }
  223. // Rows returns a rows iterator, used for streaming reading data for a
  224. // worksheet with a large data. This function is concurrency safe. For
  225. // example:
  226. //
  227. // rows, err := f.Rows("Sheet1")
  228. // if err != nil {
  229. // fmt.Println(err)
  230. // return
  231. // }
  232. // for rows.Next() {
  233. // row, err := rows.Columns()
  234. // if err != nil {
  235. // fmt.Println(err)
  236. // }
  237. // for _, colCell := range row {
  238. // fmt.Print(colCell, "\t")
  239. // }
  240. // fmt.Println()
  241. // }
  242. // if err = rows.Close(); err != nil {
  243. // fmt.Println(err)
  244. // }
  245. func (f *File) Rows(sheet string) (*Rows, error) {
  246. if err := checkSheetName(sheet); err != nil {
  247. return nil, err
  248. }
  249. name, ok := f.getSheetXMLPath(sheet)
  250. if !ok {
  251. return nil, ErrSheetNotExist{sheet}
  252. }
  253. if ws, ok := f.Sheet.Load(name); ok && ws != nil {
  254. worksheet := ws.(*xlsxWorksheet)
  255. worksheet.Lock()
  256. defer worksheet.Unlock()
  257. // Flush data
  258. output, _ := xml.Marshal(worksheet)
  259. f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
  260. }
  261. var err error
  262. rows := Rows{f: f, sheet: name}
  263. rows.needClose, rows.decoder, rows.tempFile, err = f.xmlDecoder(name)
  264. return &rows, err
  265. }
  266. // getFromStringItem build shared string item offset list from system temporary
  267. // file at one time, and return value by given to string index.
  268. func (f *File) getFromStringItem(index int) string {
  269. if f.sharedStringTemp != nil {
  270. if len(f.sharedStringItem) <= index {
  271. return strconv.Itoa(index)
  272. }
  273. offsetRange := f.sharedStringItem[index]
  274. buf := make([]byte, offsetRange[1]-offsetRange[0])
  275. if _, err := f.sharedStringTemp.ReadAt(buf, int64(offsetRange[0])); err != nil {
  276. return strconv.Itoa(index)
  277. }
  278. return string(buf)
  279. }
  280. needClose, decoder, tempFile, err := f.xmlDecoder(defaultXMLPathSharedStrings)
  281. if needClose && err == nil {
  282. defer tempFile.Close()
  283. }
  284. f.sharedStringItem = [][]uint{}
  285. f.sharedStringTemp, _ = os.CreateTemp(os.TempDir(), "excelize-")
  286. f.tempFiles.Store(defaultTempFileSST, f.sharedStringTemp.Name())
  287. var (
  288. inElement string
  289. i, offset uint
  290. )
  291. for {
  292. token, _ := decoder.Token()
  293. if token == nil {
  294. break
  295. }
  296. switch xmlElement := token.(type) {
  297. case xml.StartElement:
  298. inElement = xmlElement.Name.Local
  299. if inElement == "si" {
  300. si := xlsxSI{}
  301. _ = decoder.DecodeElement(&si, &xmlElement)
  302. startIdx := offset
  303. n, _ := f.sharedStringTemp.WriteString(si.String())
  304. offset += uint(n)
  305. f.sharedStringItem = append(f.sharedStringItem, []uint{startIdx, offset})
  306. i++
  307. }
  308. }
  309. }
  310. return f.getFromStringItem(index)
  311. }
  312. // xmlDecoder creates XML decoder by given path in the zip from memory data
  313. // or system temporary file.
  314. func (f *File) xmlDecoder(name string) (bool, *xml.Decoder, *os.File, error) {
  315. var (
  316. content []byte
  317. err error
  318. tempFile *os.File
  319. )
  320. if content = f.readXML(name); len(content) > 0 {
  321. return false, f.xmlNewDecoder(bytes.NewReader(content)), tempFile, err
  322. }
  323. tempFile, err = f.readTemp(name)
  324. return true, f.xmlNewDecoder(tempFile), tempFile, err
  325. }
  326. // SetRowHeight provides a function to set the height of a single row. For
  327. // example, set the height of the first row in Sheet1:
  328. //
  329. // err := f.SetRowHeight("Sheet1", 1, 50)
  330. func (f *File) SetRowHeight(sheet string, row int, height float64) error {
  331. if row < 1 {
  332. return newInvalidRowNumberError(row)
  333. }
  334. if height > MaxRowHeight {
  335. return ErrMaxRowHeight
  336. }
  337. ws, err := f.workSheetReader(sheet)
  338. if err != nil {
  339. return err
  340. }
  341. prepareSheetXML(ws, 0, row)
  342. rowIdx := row - 1
  343. ws.SheetData.Row[rowIdx].Ht = float64Ptr(height)
  344. ws.SheetData.Row[rowIdx].CustomHeight = true
  345. return nil
  346. }
  347. // getRowHeight provides a function to get row height in pixels by given sheet
  348. // name and row number.
  349. func (f *File) getRowHeight(sheet string, row int) int {
  350. ws, _ := f.workSheetReader(sheet)
  351. ws.Lock()
  352. defer ws.Unlock()
  353. for i := range ws.SheetData.Row {
  354. v := &ws.SheetData.Row[i]
  355. if v.R == row && v.Ht != nil {
  356. return int(convertRowHeightToPixels(*v.Ht))
  357. }
  358. }
  359. // Optimization for when the row heights haven't changed.
  360. return int(defaultRowHeightPixels)
  361. }
  362. // GetRowHeight provides a function to get row height by given worksheet name
  363. // and row number. For example, get the height of the first row in Sheet1:
  364. //
  365. // height, err := f.GetRowHeight("Sheet1", 1)
  366. func (f *File) GetRowHeight(sheet string, row int) (float64, error) {
  367. if row < 1 {
  368. return defaultRowHeightPixels, newInvalidRowNumberError(row)
  369. }
  370. ht := defaultRowHeight
  371. ws, err := f.workSheetReader(sheet)
  372. if err != nil {
  373. return ht, err
  374. }
  375. if ws.SheetFormatPr != nil && ws.SheetFormatPr.CustomHeight {
  376. ht = ws.SheetFormatPr.DefaultRowHeight
  377. }
  378. if row > len(ws.SheetData.Row) {
  379. return ht, nil // it will be better to use 0, but we take care with BC
  380. }
  381. for _, v := range ws.SheetData.Row {
  382. if v.R == row && v.Ht != nil {
  383. return *v.Ht, nil
  384. }
  385. }
  386. // Optimization for when the row heights haven't changed.
  387. return ht, nil
  388. }
  389. // sharedStringsReader provides a function to get the pointer to the structure
  390. // after deserialization of xl/sharedStrings.xml.
  391. func (f *File) sharedStringsReader() (*xlsxSST, error) {
  392. var err error
  393. f.Lock()
  394. defer f.Unlock()
  395. relPath := f.getWorkbookRelsPath()
  396. if f.SharedStrings == nil {
  397. var sharedStrings xlsxSST
  398. ss := f.readXML(defaultXMLPathSharedStrings)
  399. if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))).
  400. Decode(&sharedStrings); err != nil && err != io.EOF {
  401. return f.SharedStrings, err
  402. }
  403. if sharedStrings.Count == 0 {
  404. sharedStrings.Count = len(sharedStrings.SI)
  405. }
  406. if sharedStrings.UniqueCount == 0 {
  407. sharedStrings.UniqueCount = sharedStrings.Count
  408. }
  409. f.SharedStrings = &sharedStrings
  410. for i := range sharedStrings.SI {
  411. if sharedStrings.SI[i].T != nil {
  412. f.sharedStringsMap[sharedStrings.SI[i].T.Val] = i
  413. }
  414. }
  415. if err = f.addContentTypePart(0, "sharedStrings"); err != nil {
  416. return f.SharedStrings, err
  417. }
  418. rels, err := f.relsReader(relPath)
  419. if err != nil {
  420. return f.SharedStrings, err
  421. }
  422. for _, rel := range rels.Relationships {
  423. if rel.Target == "/xl/sharedStrings.xml" {
  424. return f.SharedStrings, nil
  425. }
  426. }
  427. // Update workbook.xml.rels
  428. f.addRels(relPath, SourceRelationshipSharedStrings, "/xl/sharedStrings.xml", "")
  429. }
  430. return f.SharedStrings, nil
  431. }
  432. // SetRowVisible provides a function to set visible of a single row by given
  433. // worksheet name and Excel row number. For example, hide row 2 in Sheet1:
  434. //
  435. // err := f.SetRowVisible("Sheet1", 2, false)
  436. func (f *File) SetRowVisible(sheet string, row int, visible bool) error {
  437. if row < 1 {
  438. return newInvalidRowNumberError(row)
  439. }
  440. ws, err := f.workSheetReader(sheet)
  441. if err != nil {
  442. return err
  443. }
  444. prepareSheetXML(ws, 0, row)
  445. ws.SheetData.Row[row-1].Hidden = !visible
  446. return nil
  447. }
  448. // GetRowVisible provides a function to get visible of a single row by given
  449. // worksheet name and Excel row number. For example, get visible state of row
  450. // 2 in Sheet1:
  451. //
  452. // visible, err := f.GetRowVisible("Sheet1", 2)
  453. func (f *File) GetRowVisible(sheet string, row int) (bool, error) {
  454. if row < 1 {
  455. return false, newInvalidRowNumberError(row)
  456. }
  457. ws, err := f.workSheetReader(sheet)
  458. if err != nil {
  459. return false, err
  460. }
  461. if row > len(ws.SheetData.Row) {
  462. return false, nil
  463. }
  464. return !ws.SheetData.Row[row-1].Hidden, nil
  465. }
  466. // SetRowOutlineLevel provides a function to set outline level number of a
  467. // single row by given worksheet name and Excel row number. The value of
  468. // parameter 'level' is 1-7. For example, outline row 2 in Sheet1 to level 1:
  469. //
  470. // err := f.SetRowOutlineLevel("Sheet1", 2, 1)
  471. func (f *File) SetRowOutlineLevel(sheet string, row int, level uint8) error {
  472. if row < 1 {
  473. return newInvalidRowNumberError(row)
  474. }
  475. if level > 7 || level < 1 {
  476. return ErrOutlineLevel
  477. }
  478. ws, err := f.workSheetReader(sheet)
  479. if err != nil {
  480. return err
  481. }
  482. prepareSheetXML(ws, 0, row)
  483. ws.SheetData.Row[row-1].OutlineLevel = level
  484. return nil
  485. }
  486. // GetRowOutlineLevel provides a function to get outline level number of a
  487. // single row by given worksheet name and Excel row number. For example, get
  488. // outline number of row 2 in Sheet1:
  489. //
  490. // level, err := f.GetRowOutlineLevel("Sheet1", 2)
  491. func (f *File) GetRowOutlineLevel(sheet string, row int) (uint8, error) {
  492. if row < 1 {
  493. return 0, newInvalidRowNumberError(row)
  494. }
  495. ws, err := f.workSheetReader(sheet)
  496. if err != nil {
  497. return 0, err
  498. }
  499. if row > len(ws.SheetData.Row) {
  500. return 0, nil
  501. }
  502. return ws.SheetData.Row[row-1].OutlineLevel, nil
  503. }
  504. // RemoveRow provides a function to remove single row by given worksheet name
  505. // and Excel row number. For example, remove row 3 in Sheet1:
  506. //
  507. // err := f.RemoveRow("Sheet1", 3)
  508. //
  509. // Use this method with caution, which will affect changes in references such
  510. // as formulas, charts, and so on. If there is any referenced value of the
  511. // worksheet, it will cause a file error when you open it. The excelize only
  512. // partially updates these references currently.
  513. func (f *File) RemoveRow(sheet string, row int) error {
  514. if row < 1 {
  515. return newInvalidRowNumberError(row)
  516. }
  517. ws, err := f.workSheetReader(sheet)
  518. if err != nil {
  519. return err
  520. }
  521. if row > len(ws.SheetData.Row) {
  522. return f.adjustHelper(sheet, rows, row, -1)
  523. }
  524. keep := 0
  525. for rowIdx := 0; rowIdx < len(ws.SheetData.Row); rowIdx++ {
  526. v := &ws.SheetData.Row[rowIdx]
  527. if v.R != row {
  528. ws.SheetData.Row[keep] = *v
  529. keep++
  530. }
  531. }
  532. ws.SheetData.Row = ws.SheetData.Row[:keep]
  533. return f.adjustHelper(sheet, rows, row, -1)
  534. }
  535. // InsertRows provides a function to insert new rows after the given Excel row
  536. // number starting from 1 and number of rows. For example, create two rows
  537. // before row 3 in Sheet1:
  538. //
  539. // err := f.InsertRows("Sheet1", 3, 2)
  540. //
  541. // Use this method with caution, which will affect changes in references such
  542. // as formulas, charts, and so on. If there is any referenced value of the
  543. // worksheet, it will cause a file error when you open it. The excelize only
  544. // partially updates these references currently.
  545. func (f *File) InsertRows(sheet string, row, n int) error {
  546. if row < 1 {
  547. return newInvalidRowNumberError(row)
  548. }
  549. if row >= TotalRows || n >= TotalRows {
  550. return ErrMaxRows
  551. }
  552. if n < 1 {
  553. return ErrParameterInvalid
  554. }
  555. return f.adjustHelper(sheet, rows, row, n)
  556. }
  557. // DuplicateRow inserts a copy of specified row (by its Excel row number) below
  558. //
  559. // err := f.DuplicateRow("Sheet1", 2)
  560. //
  561. // Use this method with caution, which will affect changes in references such
  562. // as formulas, charts, and so on. If there is any referenced value of the
  563. // worksheet, it will cause a file error when you open it. The excelize only
  564. // partially updates these references currently.
  565. func (f *File) DuplicateRow(sheet string, row int) error {
  566. return f.DuplicateRowTo(sheet, row, row+1)
  567. }
  568. // DuplicateRowTo inserts a copy of specified row by it Excel number
  569. // to specified row position moving down exists rows after target position
  570. //
  571. // err := f.DuplicateRowTo("Sheet1", 2, 7)
  572. //
  573. // Use this method with caution, which will affect changes in references such
  574. // as formulas, charts, and so on. If there is any referenced value of the
  575. // worksheet, it will cause a file error when you open it. The excelize only
  576. // partially updates these references currently.
  577. func (f *File) DuplicateRowTo(sheet string, row, row2 int) error {
  578. if row < 1 {
  579. return newInvalidRowNumberError(row)
  580. }
  581. ws, err := f.workSheetReader(sheet)
  582. if err != nil {
  583. return err
  584. }
  585. if row2 < 1 || row == row2 {
  586. return nil
  587. }
  588. var ok bool
  589. var rowCopy xlsxRow
  590. for i, r := range ws.SheetData.Row {
  591. if r.R == row {
  592. rowCopy = deepcopy.Copy(ws.SheetData.Row[i]).(xlsxRow)
  593. ok = true
  594. break
  595. }
  596. }
  597. if err := f.adjustHelper(sheet, rows, row2, 1); err != nil {
  598. return err
  599. }
  600. if !ok {
  601. return nil
  602. }
  603. idx2 := -1
  604. for i, r := range ws.SheetData.Row {
  605. if r.R == row2 {
  606. idx2 = i
  607. break
  608. }
  609. }
  610. if idx2 == -1 && len(ws.SheetData.Row) >= row2 {
  611. return nil
  612. }
  613. rowCopy.C = append(make([]xlsxC, 0, len(rowCopy.C)), rowCopy.C...)
  614. f.adjustSingleRowDimensions(&rowCopy, row2)
  615. if idx2 != -1 {
  616. ws.SheetData.Row[idx2] = rowCopy
  617. } else {
  618. ws.SheetData.Row = append(ws.SheetData.Row, rowCopy)
  619. }
  620. return f.duplicateMergeCells(sheet, ws, row, row2)
  621. }
  622. // duplicateMergeCells merge cells in the destination row if there are single
  623. // row merged cells in the copied row.
  624. func (f *File) duplicateMergeCells(sheet string, ws *xlsxWorksheet, row, row2 int) error {
  625. if ws.MergeCells == nil {
  626. return nil
  627. }
  628. if row > row2 {
  629. row++
  630. }
  631. for _, rng := range ws.MergeCells.Cells {
  632. coordinates, err := rangeRefToCoordinates(rng.Ref)
  633. if err != nil {
  634. return err
  635. }
  636. if coordinates[1] < row2 && row2 < coordinates[3] {
  637. return nil
  638. }
  639. }
  640. for i := 0; i < len(ws.MergeCells.Cells); i++ {
  641. mergedCells := ws.MergeCells.Cells[i]
  642. coordinates, _ := rangeRefToCoordinates(mergedCells.Ref)
  643. x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
  644. if y1 == y2 && y1 == row {
  645. from, _ := CoordinatesToCellName(x1, row2)
  646. to, _ := CoordinatesToCellName(x2, row2)
  647. if err := f.MergeCell(sheet, from, to); err != nil {
  648. return err
  649. }
  650. }
  651. }
  652. return nil
  653. }
  654. // checkRow provides a function to check and fill each column element for all
  655. // rows and make that is continuous in a worksheet of XML. For example:
  656. //
  657. // <row r="15" spans="1:22" x14ac:dyDescent="0.2">
  658. // <c r="A15" s="2" />
  659. // <c r="B15" s="2" />
  660. // <c r="F15" s="1" />
  661. // <c r="G15" s="1" />
  662. // </row>
  663. //
  664. // in this case, we should to change it to
  665. //
  666. // <row r="15" spans="1:22" x14ac:dyDescent="0.2">
  667. // <c r="A15" s="2" />
  668. // <c r="B15" s="2" />
  669. // <c r="C15" s="2" />
  670. // <c r="D15" s="2" />
  671. // <c r="E15" s="2" />
  672. // <c r="F15" s="1" />
  673. // <c r="G15" s="1" />
  674. // </row>
  675. //
  676. // Notice: this method could be very slow for large spreadsheets (more than
  677. // 3000 rows one sheet).
  678. func checkRow(ws *xlsxWorksheet) error {
  679. for rowIdx := range ws.SheetData.Row {
  680. rowData := &ws.SheetData.Row[rowIdx]
  681. colCount := len(rowData.C)
  682. if colCount == 0 {
  683. continue
  684. }
  685. // check and fill the cell without r attribute in a row element
  686. rCount := 0
  687. for idx, cell := range rowData.C {
  688. rCount++
  689. if cell.R != "" {
  690. lastR, _, err := CellNameToCoordinates(cell.R)
  691. if err != nil {
  692. return err
  693. }
  694. if lastR > rCount {
  695. rCount = lastR
  696. }
  697. continue
  698. }
  699. rowData.C[idx].R, _ = CoordinatesToCellName(rCount, rowIdx+1)
  700. }
  701. lastCol, _, err := CellNameToCoordinates(rowData.C[colCount-1].R)
  702. if err != nil {
  703. return err
  704. }
  705. if colCount < lastCol {
  706. sourceList := rowData.C
  707. targetList := make([]xlsxC, 0, lastCol)
  708. rowData.C = ws.SheetData.Row[rowIdx].C[:0]
  709. for colIdx := 0; colIdx < lastCol; colIdx++ {
  710. cellName, err := CoordinatesToCellName(colIdx+1, rowIdx+1)
  711. if err != nil {
  712. return err
  713. }
  714. targetList = append(targetList, xlsxC{R: cellName})
  715. }
  716. rowData.C = targetList
  717. for colIdx := range sourceList {
  718. colData := &sourceList[colIdx]
  719. colNum, _, err := CellNameToCoordinates(colData.R)
  720. if err != nil {
  721. return err
  722. }
  723. ws.SheetData.Row[rowIdx].C[colNum-1] = *colData
  724. }
  725. }
  726. }
  727. return nil
  728. }
  729. // hasAttr determine if row non-default attributes.
  730. func (r *xlsxRow) hasAttr() bool {
  731. return r.Spans != "" || r.S != 0 || r.CustomFormat || r.Ht != nil ||
  732. r.Hidden || r.CustomHeight || r.OutlineLevel != 0 || r.Collapsed ||
  733. r.ThickTop || r.ThickBot || r.Ph
  734. }
  735. // SetRowStyle provides a function to set the style of rows by given worksheet
  736. // name, row range, and style ID. Note that this will overwrite the existing
  737. // styles for the rows, it won't append or merge style with existing styles.
  738. //
  739. // For example set style of row 1 on Sheet1:
  740. //
  741. // err := f.SetRowStyle("Sheet1", 1, 1, styleID)
  742. //
  743. // Set style of rows 1 to 10 on Sheet1:
  744. //
  745. // err := f.SetRowStyle("Sheet1", 1, 10, styleID)
  746. func (f *File) SetRowStyle(sheet string, start, end, styleID int) error {
  747. if end < start {
  748. start, end = end, start
  749. }
  750. if start < 1 {
  751. return newInvalidRowNumberError(start)
  752. }
  753. if end > TotalRows {
  754. return ErrMaxRows
  755. }
  756. s, err := f.stylesReader()
  757. if err != nil {
  758. return err
  759. }
  760. s.Lock()
  761. defer s.Unlock()
  762. if styleID < 0 || s.CellXfs == nil || len(s.CellXfs.Xf) <= styleID {
  763. return newInvalidStyleID(styleID)
  764. }
  765. ws, err := f.workSheetReader(sheet)
  766. if err != nil {
  767. return err
  768. }
  769. prepareSheetXML(ws, 0, end)
  770. for row := start - 1; row < end; row++ {
  771. ws.SheetData.Row[row].S = styleID
  772. ws.SheetData.Row[row].CustomFormat = true
  773. for i := range ws.SheetData.Row[row].C {
  774. if _, rowNum, err := CellNameToCoordinates(ws.SheetData.Row[row].C[i].R); err == nil && rowNum-1 == row {
  775. ws.SheetData.Row[row].C[i].S = styleID
  776. }
  777. }
  778. }
  779. return nil
  780. }
  781. // convertRowHeightToPixels provides a function to convert the height of a
  782. // cell from user's units to pixels. If the height hasn't been set by the user
  783. // we use the default value. If the row is hidden it has a value of zero.
  784. func convertRowHeightToPixels(height float64) float64 {
  785. var pixels float64
  786. if height == 0 {
  787. return pixels
  788. }
  789. pixels = math.Ceil(4.0 / 3.0 * height)
  790. return pixels
  791. }