adjust.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. // Copyright 2016 - 2019 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 files. Support reads and writes XLSX file generated by
  7. // Microsoft Excel™ 2007 and later. Support save file without losing original
  8. // charts of XLSX. This library needs Go version 1.8 or later.
  9. package excelize
  10. import "strings"
  11. type adjustDirection bool
  12. const (
  13. columns adjustDirection = false
  14. rows adjustDirection = true
  15. )
  16. // adjustHelper provides a function to adjust rows and columns dimensions,
  17. // hyperlinks, merged cells and auto filter when inserting or deleting rows or
  18. // columns.
  19. //
  20. // sheet: Worksheet name that we're editing
  21. // column: Index number of the column we're inserting/deleting before
  22. // row: Index number of the row we're inserting/deleting before
  23. // offset: Number of rows/column to insert/delete negative values indicate deletion
  24. //
  25. // TODO: adjustCalcChain, adjustPageBreaks, adjustComments,
  26. // adjustDataValidations, adjustProtectedCells
  27. //
  28. func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int) error {
  29. xlsx, err := f.workSheetReader(sheet)
  30. if err != nil {
  31. return err
  32. }
  33. if dir == rows {
  34. f.adjustRowDimensions(xlsx, num, offset)
  35. } else {
  36. f.adjustColDimensions(xlsx, num, offset)
  37. }
  38. f.adjustHyperlinks(xlsx, sheet, dir, num, offset)
  39. if err = f.adjustMergeCells(xlsx, dir, num, offset); err != nil {
  40. return err
  41. }
  42. if err = f.adjustAutoFilter(xlsx, dir, num, offset); err != nil {
  43. return err
  44. }
  45. checkSheet(xlsx)
  46. checkRow(xlsx)
  47. return nil
  48. }
  49. // adjustColDimensions provides a function to update column dimensions when
  50. // inserting or deleting rows or columns.
  51. func (f *File) adjustColDimensions(xlsx *xlsxWorksheet, col, offset int) {
  52. for rowIdx := range xlsx.SheetData.Row {
  53. for colIdx, v := range xlsx.SheetData.Row[rowIdx].C {
  54. cellCol, cellRow, _ := CellNameToCoordinates(v.R)
  55. if col <= cellCol {
  56. if newCol := cellCol + offset; newCol > 0 {
  57. xlsx.SheetData.Row[rowIdx].C[colIdx].R, _ = CoordinatesToCellName(newCol, cellRow)
  58. }
  59. }
  60. }
  61. }
  62. }
  63. // adjustRowDimensions provides a function to update row dimensions when
  64. // inserting or deleting rows or columns.
  65. func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, row, offset int) {
  66. for i, r := range xlsx.SheetData.Row {
  67. if newRow := r.R + offset; r.R >= row && newRow > 0 {
  68. f.ajustSingleRowDimensions(&xlsx.SheetData.Row[i], newRow)
  69. }
  70. }
  71. }
  72. // ajustSingleRowDimensions provides a function to ajust single row dimensions.
  73. func (f *File) ajustSingleRowDimensions(r *xlsxRow, num int) {
  74. r.R = num
  75. for i, col := range r.C {
  76. colName, _, _ := SplitCellName(col.R)
  77. r.C[i].R, _ = JoinCellName(colName, num)
  78. }
  79. }
  80. // adjustHyperlinks provides a function to update hyperlinks when inserting or
  81. // deleting rows or columns.
  82. func (f *File) adjustHyperlinks(xlsx *xlsxWorksheet, sheet string, dir adjustDirection, num, offset int) {
  83. // short path
  84. if xlsx.Hyperlinks == nil || len(xlsx.Hyperlinks.Hyperlink) == 0 {
  85. return
  86. }
  87. // order is important
  88. if offset < 0 {
  89. for rowIdx, linkData := range xlsx.Hyperlinks.Hyperlink {
  90. colNum, rowNum, _ := CellNameToCoordinates(linkData.Ref)
  91. if (dir == rows && num == rowNum) || (dir == columns && num == colNum) {
  92. f.deleteSheetRelationships(sheet, linkData.RID)
  93. if len(xlsx.Hyperlinks.Hyperlink) > 1 {
  94. xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink[:rowIdx],
  95. xlsx.Hyperlinks.Hyperlink[rowIdx+1:]...)
  96. } else {
  97. xlsx.Hyperlinks = nil
  98. }
  99. }
  100. }
  101. }
  102. if xlsx.Hyperlinks == nil {
  103. return
  104. }
  105. for i := range xlsx.Hyperlinks.Hyperlink {
  106. link := &xlsx.Hyperlinks.Hyperlink[i] // get reference
  107. colNum, rowNum, _ := CellNameToCoordinates(link.Ref)
  108. if dir == rows {
  109. if rowNum >= num {
  110. link.Ref, _ = CoordinatesToCellName(colNum, rowNum+offset)
  111. }
  112. } else {
  113. if colNum >= num {
  114. link.Ref, _ = CoordinatesToCellName(colNum+offset, rowNum)
  115. }
  116. }
  117. }
  118. }
  119. // adjustAutoFilter provides a function to update the auto filter when
  120. // inserting or deleting rows or columns.
  121. func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, dir adjustDirection, num, offset int) error {
  122. if xlsx.AutoFilter == nil {
  123. return nil
  124. }
  125. rng := strings.Split(xlsx.AutoFilter.Ref, ":")
  126. firstCell := rng[0]
  127. lastCell := rng[1]
  128. firstCol, firstRow, err := CellNameToCoordinates(firstCell)
  129. if err != nil {
  130. return err
  131. }
  132. lastCol, lastRow, err := CellNameToCoordinates(lastCell)
  133. if err != nil {
  134. return err
  135. }
  136. if (dir == rows && firstRow == num && offset < 0) || (dir == columns && firstCol == num && lastCol == num) {
  137. xlsx.AutoFilter = nil
  138. for rowIdx := range xlsx.SheetData.Row {
  139. rowData := &xlsx.SheetData.Row[rowIdx]
  140. if rowData.R > firstRow && rowData.R <= lastRow {
  141. rowData.Hidden = false
  142. }
  143. }
  144. return nil
  145. }
  146. if dir == rows {
  147. if firstRow >= num {
  148. firstCell, _ = CoordinatesToCellName(firstCol, firstRow+offset)
  149. }
  150. if lastRow >= num {
  151. lastCell, _ = CoordinatesToCellName(lastCol, lastRow+offset)
  152. }
  153. } else {
  154. if lastCol >= num {
  155. lastCell, _ = CoordinatesToCellName(lastCol+offset, lastRow)
  156. }
  157. }
  158. xlsx.AutoFilter.Ref = firstCell + ":" + lastCell
  159. return nil
  160. }
  161. // adjustMergeCells provides a function to update merged cells when inserting
  162. // or deleting rows or columns.
  163. func (f *File) adjustMergeCells(xlsx *xlsxWorksheet, dir adjustDirection, num, offset int) error {
  164. if xlsx.MergeCells == nil {
  165. return nil
  166. }
  167. for i, areaData := range xlsx.MergeCells.Cells {
  168. rng := strings.Split(areaData.Ref, ":")
  169. firstCell := rng[0]
  170. lastCell := rng[1]
  171. firstCol, firstRow, err := CellNameToCoordinates(firstCell)
  172. if err != nil {
  173. return err
  174. }
  175. lastCol, lastRow, err := CellNameToCoordinates(lastCell)
  176. if err != nil {
  177. return err
  178. }
  179. adjust := func(v int) int {
  180. if v >= num {
  181. v += offset
  182. if v < 1 {
  183. return 1
  184. }
  185. return v
  186. }
  187. return v
  188. }
  189. if dir == rows {
  190. firstRow = adjust(firstRow)
  191. lastRow = adjust(lastRow)
  192. } else {
  193. firstCol = adjust(firstCol)
  194. lastCol = adjust(lastCol)
  195. }
  196. if firstCol == lastCol && firstRow == lastRow {
  197. if len(xlsx.MergeCells.Cells) > 1 {
  198. xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:i], xlsx.MergeCells.Cells[i+1:]...)
  199. xlsx.MergeCells.Count = len(xlsx.MergeCells.Cells)
  200. } else {
  201. xlsx.MergeCells = nil
  202. }
  203. }
  204. if firstCell, err = CoordinatesToCellName(firstCol, firstRow); err != nil {
  205. return err
  206. }
  207. if lastCell, err = CoordinatesToCellName(lastCol, lastRow); err != nil {
  208. return err
  209. }
  210. areaData.Ref = firstCell + ":" + lastCell
  211. }
  212. return nil
  213. }