merge.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  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 "strings"
  13. // Rect gets merged cell rectangle coordinates sequence.
  14. func (mc *xlsxMergeCell) Rect() ([]int, error) {
  15. var err error
  16. if mc.rect == nil {
  17. mergedCellsRef := mc.Ref
  18. if !strings.Contains(mergedCellsRef, ":") {
  19. mergedCellsRef += ":" + mergedCellsRef
  20. }
  21. mc.rect, err = rangeRefToCoordinates(mergedCellsRef)
  22. }
  23. return mc.rect, err
  24. }
  25. // MergeCell provides a function to merge cells by given range reference and
  26. // sheet name. Merging cells only keeps the upper-left cell value, and
  27. // discards the other values. For example create a merged cell of D3:E9 on
  28. // Sheet1:
  29. //
  30. // err := f.MergeCell("Sheet1", "D3", "E9")
  31. //
  32. // If you create a merged cell that overlaps with another existing merged cell,
  33. // those merged cells that already exist will be removed. The cell references
  34. // tuple after merging in the following range will be: A1(x3,y1) D1(x2,y1)
  35. // A8(x3,y4) D8(x2,y4)
  36. //
  37. // B1(x1,y1) D1(x2,y1)
  38. // +------------------------+
  39. // | |
  40. // A4(x3,y3) | C4(x4,y3) |
  41. // +------------------------+ |
  42. // | | | |
  43. // | |B5(x1,y2) | D5(x2,y2)|
  44. // | +------------------------+
  45. // | |
  46. // |A8(x3,y4) C8(x4,y4)|
  47. // +------------------------+
  48. func (f *File) MergeCell(sheet, hCell, vCell string) error {
  49. rect, err := rangeRefToCoordinates(hCell + ":" + vCell)
  50. if err != nil {
  51. return err
  52. }
  53. // Correct the range reference, such correct C1:B3 to B1:C3.
  54. _ = sortCoordinates(rect)
  55. hCell, _ = CoordinatesToCellName(rect[0], rect[1])
  56. vCell, _ = CoordinatesToCellName(rect[2], rect[3])
  57. ws, err := f.workSheetReader(sheet)
  58. if err != nil {
  59. return err
  60. }
  61. ws.Lock()
  62. defer ws.Unlock()
  63. ref := hCell + ":" + vCell
  64. if ws.MergeCells != nil {
  65. ws.MergeCells.Cells = append(ws.MergeCells.Cells, &xlsxMergeCell{Ref: ref, rect: rect})
  66. } else {
  67. ws.MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: ref, rect: rect}}}
  68. }
  69. ws.MergeCells.Count = len(ws.MergeCells.Cells)
  70. return err
  71. }
  72. // UnmergeCell provides a function to unmerge a given range reference.
  73. // For example unmerge range reference D3:E9 on Sheet1:
  74. //
  75. // err := f.UnmergeCell("Sheet1", "D3", "E9")
  76. //
  77. // Attention: overlapped range will also be unmerged.
  78. func (f *File) UnmergeCell(sheet, hCell, vCell string) error {
  79. ws, err := f.workSheetReader(sheet)
  80. if err != nil {
  81. return err
  82. }
  83. ws.Lock()
  84. defer ws.Unlock()
  85. rect1, err := rangeRefToCoordinates(hCell + ":" + vCell)
  86. if err != nil {
  87. return err
  88. }
  89. // Correct the range reference, such correct C1:B3 to B1:C3.
  90. _ = sortCoordinates(rect1)
  91. // return nil since no MergeCells in the sheet
  92. if ws.MergeCells == nil {
  93. return nil
  94. }
  95. if err = f.mergeOverlapCells(ws); err != nil {
  96. return err
  97. }
  98. i := 0
  99. for _, mergeCell := range ws.MergeCells.Cells {
  100. if mergeCell == nil {
  101. continue
  102. }
  103. mergedCellsRef := mergeCell.Ref
  104. if !strings.Contains(mergedCellsRef, ":") {
  105. mergedCellsRef += ":" + mergedCellsRef
  106. }
  107. rect2, _ := rangeRefToCoordinates(mergedCellsRef)
  108. if isOverlap(rect1, rect2) {
  109. continue
  110. }
  111. ws.MergeCells.Cells[i] = mergeCell
  112. i++
  113. }
  114. ws.MergeCells.Cells = ws.MergeCells.Cells[:i]
  115. ws.MergeCells.Count = len(ws.MergeCells.Cells)
  116. if ws.MergeCells.Count == 0 {
  117. ws.MergeCells = nil
  118. }
  119. return nil
  120. }
  121. // GetMergeCells provides a function to get all merged cells from a worksheet
  122. // currently.
  123. func (f *File) GetMergeCells(sheet string) ([]MergeCell, error) {
  124. var mergeCells []MergeCell
  125. ws, err := f.workSheetReader(sheet)
  126. if err != nil {
  127. return mergeCells, err
  128. }
  129. if ws.MergeCells != nil {
  130. if err = f.mergeOverlapCells(ws); err != nil {
  131. return mergeCells, err
  132. }
  133. mergeCells = make([]MergeCell, 0, len(ws.MergeCells.Cells))
  134. for i := range ws.MergeCells.Cells {
  135. ref := ws.MergeCells.Cells[i].Ref
  136. cell := strings.Split(ref, ":")[0]
  137. val, _ := f.GetCellValue(sheet, cell)
  138. mergeCells = append(mergeCells, []string{ref, val})
  139. }
  140. }
  141. return mergeCells, err
  142. }
  143. // overlapRange calculate overlap range of merged cells, and returns max
  144. // column and rows of the range.
  145. func overlapRange(ws *xlsxWorksheet) (row, col int, err error) {
  146. var rect []int
  147. for _, mergeCell := range ws.MergeCells.Cells {
  148. if mergeCell == nil {
  149. continue
  150. }
  151. if rect, err = mergeCell.Rect(); err != nil {
  152. return
  153. }
  154. x1, y1, x2, y2 := rect[0], rect[1], rect[2], rect[3]
  155. if x1 > col {
  156. col = x1
  157. }
  158. if x2 > col {
  159. col = x2
  160. }
  161. if y1 > row {
  162. row = y1
  163. }
  164. if y2 > row {
  165. row = y2
  166. }
  167. }
  168. return
  169. }
  170. // flatMergedCells convert merged cells range reference to cell-matrix.
  171. func flatMergedCells(ws *xlsxWorksheet, matrix [][]*xlsxMergeCell) error {
  172. for i, cell := range ws.MergeCells.Cells {
  173. rect, err := cell.Rect()
  174. if err != nil {
  175. return err
  176. }
  177. x1, y1, x2, y2 := rect[0]-1, rect[1]-1, rect[2]-1, rect[3]-1
  178. var overlapCells []*xlsxMergeCell
  179. for x := x1; x <= x2; x++ {
  180. for y := y1; y <= y2; y++ {
  181. if matrix[x][y] != nil {
  182. overlapCells = append(overlapCells, matrix[x][y])
  183. }
  184. matrix[x][y] = cell
  185. }
  186. }
  187. if len(overlapCells) != 0 {
  188. newCell := cell
  189. for _, overlapCell := range overlapCells {
  190. newCell = mergeCell(cell, overlapCell)
  191. }
  192. newRect, _ := newCell.Rect()
  193. x1, y1, x2, y2 := newRect[0]-1, newRect[1]-1, newRect[2]-1, newRect[3]-1
  194. for x := x1; x <= x2; x++ {
  195. for y := y1; y <= y2; y++ {
  196. matrix[x][y] = newCell
  197. }
  198. }
  199. ws.MergeCells.Cells[i] = newCell
  200. }
  201. }
  202. return nil
  203. }
  204. // mergeOverlapCells merge overlap cells.
  205. func (f *File) mergeOverlapCells(ws *xlsxWorksheet) error {
  206. rows, cols, err := overlapRange(ws)
  207. if err != nil {
  208. return err
  209. }
  210. if rows == 0 || cols == 0 {
  211. return nil
  212. }
  213. matrix := make([][]*xlsxMergeCell, cols)
  214. for i := range matrix {
  215. matrix[i] = make([]*xlsxMergeCell, rows)
  216. }
  217. _ = flatMergedCells(ws, matrix)
  218. mergeCells := ws.MergeCells.Cells[:0]
  219. for _, cell := range ws.MergeCells.Cells {
  220. rect, _ := cell.Rect()
  221. x1, y1, x2, y2 := rect[0]-1, rect[1]-1, rect[2]-1, rect[3]-1
  222. if matrix[x1][y1] == cell {
  223. mergeCells = append(mergeCells, cell)
  224. for x := x1; x <= x2; x++ {
  225. for y := y1; y <= y2; y++ {
  226. matrix[x][y] = nil
  227. }
  228. }
  229. }
  230. }
  231. ws.MergeCells.Count, ws.MergeCells.Cells = len(mergeCells), mergeCells
  232. return nil
  233. }
  234. // mergeCell merge two cells.
  235. func mergeCell(cell1, cell2 *xlsxMergeCell) *xlsxMergeCell {
  236. rect1, _ := cell1.Rect()
  237. rect2, _ := cell2.Rect()
  238. if rect1[0] > rect2[0] {
  239. rect1[0], rect2[0] = rect2[0], rect1[0]
  240. }
  241. if rect1[2] < rect2[2] {
  242. rect1[2], rect2[2] = rect2[2], rect1[2]
  243. }
  244. if rect1[1] > rect2[1] {
  245. rect1[1], rect2[1] = rect2[1], rect1[1]
  246. }
  247. if rect1[3] < rect2[3] {
  248. rect1[3], rect2[3] = rect2[3], rect1[3]
  249. }
  250. hCell, _ := CoordinatesToCellName(rect1[0], rect1[1])
  251. vCell, _ := CoordinatesToCellName(rect1[2], rect1[3])
  252. return &xlsxMergeCell{rect: rect1, Ref: hCell + ":" + vCell}
  253. }
  254. // MergeCell define a merged cell data.
  255. // It consists of the following structure.
  256. // example: []string{"D4:E10", "cell value"}
  257. type MergeCell []string
  258. // GetCellValue returns merged cell value.
  259. func (m *MergeCell) GetCellValue() string {
  260. return (*m)[1]
  261. }
  262. // GetStartAxis returns the top left cell reference of merged range, for
  263. // example: "C2".
  264. func (m *MergeCell) GetStartAxis() string {
  265. return strings.Split((*m)[0], ":")[0]
  266. }
  267. // GetEndAxis returns the bottom right cell reference of merged range, for
  268. // example: "D4".
  269. func (m *MergeCell) GetEndAxis() string {
  270. return strings.Split((*m)[0], ":")[1]
  271. }