Skip to content

Commit f0a56d6

Browse files
authored
This closes #2226, optimize overlap merged cell ranges check (#2228)
- Update unit tests - Remove empty row on saving, reduce generated workbook file size
1 parent 896a086 commit f0a56d6

File tree

3 files changed

+49
-130
lines changed

3 files changed

+49
-130
lines changed

merge.go

Lines changed: 43 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -112,20 +112,12 @@ func (f *File) UnmergeCell(sheet, topLeftCell, bottomRightCell string) error {
112112
if ws.MergeCells == nil {
113113
return nil
114114
}
115-
if err = f.mergeOverlapCells(ws); err != nil {
115+
if err = ws.mergeOverlapCells(); err != nil {
116116
return err
117117
}
118118
i := 0
119119
for _, mergeCell := range ws.MergeCells.Cells {
120-
if mergeCell == nil {
121-
continue
122-
}
123-
mergedCellsRef := mergeCell.Ref
124-
if !strings.Contains(mergedCellsRef, ":") {
125-
mergedCellsRef += ":" + mergedCellsRef
126-
}
127-
rect2, _ := rangeRefToCoordinates(mergedCellsRef)
128-
if isOverlap(rect1, rect2) {
120+
if rect2, _ := rangeRefToCoordinates(mergeCell.Ref); isOverlap(rect1, rect2) {
129121
continue
130122
}
131123
ws.MergeCells.Cells[i] = mergeCell
@@ -163,7 +155,7 @@ func (f *File) GetMergeCells(sheet string, withoutValues ...bool) ([]MergeCell,
163155
return mergeCells, err
164156
}
165157
if ws.MergeCells != nil {
166-
if err = f.mergeOverlapCells(ws); err != nil {
158+
if err = ws.mergeOverlapCells(); err != nil {
167159
return mergeCells, err
168160
}
169161
mergeCells = make([]MergeCell, 0, len(ws.MergeCells.Cells))
@@ -179,123 +171,56 @@ func (f *File) GetMergeCells(sheet string, withoutValues ...bool) ([]MergeCell,
179171
return mergeCells, err
180172
}
181173

182-
// overlapRange calculate overlap range of merged cells, and returns max
183-
// column and rows of the range.
184-
func overlapRange(ws *xlsxWorksheet) (row, col int, err error) {
185-
var rect []int
186-
for _, mergeCell := range ws.MergeCells.Cells {
187-
if mergeCell == nil {
174+
// mergeOverlapCells merge overlap cells.
175+
func (ws *xlsxWorksheet) mergeOverlapCells() error {
176+
var (
177+
err error
178+
rectList [][]int
179+
merged = true
180+
)
181+
for _, cell := range ws.MergeCells.Cells {
182+
if cell == nil {
188183
continue
189184
}
190-
if rect, err = mergeCell.Rect(); err != nil {
191-
return
192-
}
193-
x1, y1, x2, y2 := rect[0], rect[1], rect[2], rect[3]
194-
if x1 > col {
195-
col = x1
196-
}
197-
if x2 > col {
198-
col = x2
199-
}
200-
if y1 > row {
201-
row = y1
202-
}
203-
if y2 > row {
204-
row = y2
205-
}
206-
}
207-
return
208-
}
209-
210-
// flatMergedCells convert merged cells range reference to cell-matrix.
211-
func flatMergedCells(ws *xlsxWorksheet, matrix [][]*xlsxMergeCell) error {
212-
for i, cell := range ws.MergeCells.Cells {
213-
rect, err := cell.Rect()
214-
if err != nil {
185+
if cell.rect, err = cell.Rect(); err != nil {
215186
return err
216187
}
217-
x1, y1, x2, y2 := rect[0]-1, rect[1]-1, rect[2]-1, rect[3]-1
218-
var overlapCells []*xlsxMergeCell
219-
for x := x1; x <= x2; x++ {
220-
for y := y1; y <= y2; y++ {
221-
if matrix[x][y] != nil {
222-
overlapCells = append(overlapCells, matrix[x][y])
223-
}
224-
matrix[x][y] = cell
225-
}
226-
}
227-
if len(overlapCells) != 0 {
228-
newCell := cell
229-
for _, overlapCell := range overlapCells {
230-
newCell = mergeCell(cell, overlapCell)
188+
rectList = append(rectList, cell.rect)
189+
}
190+
for merged {
191+
merged = false
192+
var mergedRectList [][]int
193+
used := make([]bool, len(rectList))
194+
for i := 0; i < len(rectList); i++ {
195+
if used[i] {
196+
continue
231197
}
232-
newRect, _ := newCell.Rect()
233-
x1, y1, x2, y2 := newRect[0]-1, newRect[1]-1, newRect[2]-1, newRect[3]-1
234-
for x := x1; x <= x2; x++ {
235-
for y := y1; y <= y2; y++ {
236-
matrix[x][y] = newCell
198+
r1 := rectList[i]
199+
for j := i + 1; j < len(rectList); j++ {
200+
if used[j] {
201+
continue
237202
}
238-
}
239-
ws.MergeCells.Cells[i] = newCell
240-
}
241-
}
242-
return nil
243-
}
244-
245-
// mergeOverlapCells merge overlap cells.
246-
func (f *File) mergeOverlapCells(ws *xlsxWorksheet) error {
247-
rows, cols, err := overlapRange(ws)
248-
if err != nil {
249-
return err
250-
}
251-
if rows == 0 || cols == 0 {
252-
return nil
253-
}
254-
matrix := make([][]*xlsxMergeCell, cols)
255-
for i := range matrix {
256-
matrix[i] = make([]*xlsxMergeCell, rows)
257-
}
258-
_ = flatMergedCells(ws, matrix)
259-
mergeCells := ws.MergeCells.Cells[:0]
260-
for _, cell := range ws.MergeCells.Cells {
261-
rect, _ := cell.Rect()
262-
x1, y1, x2, y2 := rect[0]-1, rect[1]-1, rect[2]-1, rect[3]-1
263-
if matrix[x1][y1] == cell {
264-
mergeCells = append(mergeCells, cell)
265-
for x := x1; x <= x2; x++ {
266-
for y := y1; y <= y2; y++ {
267-
matrix[x][y] = nil
203+
if r2 := rectList[j]; isOverlap(r1, r2) {
204+
r1 = []int{
205+
min(r1[0], r2[0]),
206+
min(r1[1], r2[1]),
207+
max(r1[2], r2[2]),
208+
max(r1[3], r2[3]),
209+
}
210+
merged, used[j] = true, true
268211
}
269212
}
213+
mergedRectList = append(mergedRectList, r1)
270214
}
215+
rectList = mergedRectList
271216
}
272-
ws.MergeCells.Count, ws.MergeCells.Cells = len(mergeCells), mergeCells
273-
return nil
274-
}
275-
276-
// mergeCell merge two cells.
277-
func mergeCell(cell1, cell2 *xlsxMergeCell) *xlsxMergeCell {
278-
rect1, _ := cell1.Rect()
279-
rect2, _ := cell2.Rect()
280-
281-
if rect1[0] > rect2[0] {
282-
rect1[0], rect2[0] = rect2[0], rect1[0]
283-
}
284-
285-
if rect1[2] < rect2[2] {
286-
rect1[2], rect2[2] = rect2[2], rect1[2]
287-
}
288-
289-
if rect1[1] > rect2[1] {
290-
rect1[1], rect2[1] = rect2[1], rect1[1]
291-
}
292-
293-
if rect1[3] < rect2[3] {
294-
rect1[3], rect2[3] = rect2[3], rect1[3]
217+
ws.MergeCells.Cells = make([]*xlsxMergeCell, 0, len(rectList))
218+
for _, r := range rectList {
219+
ref, _ := coordinatesToRangeRef(r)
220+
ws.MergeCells.Cells = append(ws.MergeCells.Cells, &xlsxMergeCell{Ref: ref, rect: r})
295221
}
296-
topLeftCell, _ := CoordinatesToCellName(rect1[0], rect1[1])
297-
bottomRightCell, _ := CoordinatesToCellName(rect1[2], rect1[3])
298-
return &xlsxMergeCell{rect: rect1, Ref: topLeftCell + ":" + bottomRightCell}
222+
ws.MergeCells.Count = len(ws.MergeCells.Cells)
223+
return err
299224
}
300225

301226
// MergeCell define a merged cell data.
@@ -317,9 +242,5 @@ func (m *MergeCell) GetStartAxis() string {
317242
// GetEndAxis returns the bottom right cell reference of merged range, for
318243
// example: "D4".
319244
func (m *MergeCell) GetEndAxis() string {
320-
coordinates := strings.Split((*m)[0], ":")
321-
if len(coordinates) == 2 {
322-
return coordinates[1]
323-
}
324-
return coordinates[0]
245+
return strings.Split((*m)[0], ":")[1]
325246
}

merge_test.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -217,11 +217,6 @@ func TestUnmergeCell(t *testing.T) {
217217
assert.EqualError(t, f.UnmergeCell("Sheet1", "A2", "B3"), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
218218
}
219219

220-
func TestFlatMergedCells(t *testing.T) {
221-
ws := &xlsxWorksheet{MergeCells: &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: ""}}}}
222-
assert.EqualError(t, flatMergedCells(ws, [][]*xlsxMergeCell{}), "cannot convert cell \"\" to coordinates: invalid cell name \"\"")
223-
}
224-
225220
func TestMergeCellsParser(t *testing.T) {
226221
ws := &xlsxWorksheet{MergeCells: &xlsxMergeCells{Cells: []*xlsxMergeCell{nil}}}
227222
_, err := ws.mergeCellsParser("A1")

sheet.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ func (f *File) workSheetWriter() {
160160
if ws != nil {
161161
sheet := ws.(*xlsxWorksheet)
162162
if sheet.MergeCells != nil && len(sheet.MergeCells.Cells) > 0 {
163-
_ = f.mergeOverlapCells(sheet)
163+
_ = sheet.mergeOverlapCells()
164164
}
165165
if sheet.Cols != nil && len(sheet.Cols.Col) > 0 {
166166
f.mergeExpandedCols(sheet)
@@ -197,12 +197,15 @@ func trimRow(sheetData *xlsxSheetData) []xlsxRow {
197197
i int
198198
)
199199

200-
for k := range sheetData.Row {
200+
for k := 0; k < len(sheetData.Row); k++ {
201201
row = sheetData.Row[k]
202202
if row = trimCell(row); len(row.C) != 0 || row.hasAttr() {
203203
sheetData.Row[i] = row
204+
i++
205+
continue
204206
}
205-
i++
207+
sheetData.Row = append(sheetData.Row[:k], sheetData.Row[k+1:]...)
208+
k--
206209
}
207210
return sheetData.Row[:i]
208211
}

0 commit comments

Comments
 (0)