From f6f14f507ee1adf4883cb1b12f27932a63afb286 Mon Sep 17 00:00:00 2001 From: three Date: Fri, 13 Aug 2021 01:32:44 +0800 Subject: [PATCH] Speed up merge cells --- adjust.go | 8 +- adjust_test.go | 4 + calc.go | 12 +-- cell.go | 31 +++++-- col.go | 6 +- lib.go | 5 +- merge.go | 217 ++++++++++++++++++++++++++++++-------------- merge_test.go | 34 ++++--- picture.go | 2 + pivotTable.go | 2 +- rows.go | 4 +- sheet.go | 3 + xmlSharedStrings.go | 22 +---- xmlWorksheet.go | 7 +- 14 files changed, 227 insertions(+), 130 deletions(-) diff --git a/adjust.go b/adjust.go index 1fe6663..9f2176f 100644 --- a/adjust.go +++ b/adjust.go @@ -145,7 +145,7 @@ func (f *File) adjustAutoFilter(ws *xlsxWorksheet, dir adjustDirection, num, off return nil } - coordinates, err := f.areaRefToCoordinates(ws.AutoFilter.Ref) + coordinates, err := areaRefToCoordinates(ws.AutoFilter.Ref) if err != nil { return err } @@ -199,7 +199,7 @@ func (f *File) adjustMergeCells(ws *xlsxWorksheet, dir adjustDirection, num, off for i := 0; i < len(ws.MergeCells.Cells); i++ { areaData := ws.MergeCells.Cells[i] - coordinates, err := f.areaRefToCoordinates(areaData.Ref) + coordinates, err := areaRefToCoordinates(areaData.Ref) if err != nil { return err } @@ -219,7 +219,7 @@ func (f *File) adjustMergeCells(ws *xlsxWorksheet, dir adjustDirection, num, off x1 = f.adjustMergeCellsHelper(x1, num, offset) x2 = f.adjustMergeCellsHelper(x2, num, offset) } - if x1 == x2 && y1 == y2 { + if x1 == x2 && y1 == y2 && i >= 0 { f.deleteMergeCell(ws, i) i-- } @@ -234,7 +234,7 @@ func (f *File) adjustMergeCells(ws *xlsxWorksheet, dir adjustDirection, num, off // compare and calculate cell axis by the given pivot, operation axis and // offset. func (f *File) adjustMergeCellsHelper(pivot, num, offset int) int { - if pivot >= num { + if pivot > num { pivot += offset if pivot < 1 { return 1 diff --git a/adjust_test.go b/adjust_test.go index ced091d..f56f763 100644 --- a/adjust_test.go +++ b/adjust_test.go @@ -84,6 +84,10 @@ func TestAdjustHelper(t *testing.T) { assert.EqualError(t, f.adjustHelper("SheetN", rows, 0, 0), "sheet SheetN is not exist") } +func TestAdjustMergeCellsHelper(t *testing.T) { + assert.Equal(t, 1, NewFile().adjustMergeCellsHelper(1, 0, -2)) +} + func TestAdjustCalcChain(t *testing.T) { f := NewFile() f.CalcChain = &xlsxCalcChain{ diff --git a/calc.go b/calc.go index cd7fa97..a03520b 100644 --- a/calc.go +++ b/calc.go @@ -5103,11 +5103,11 @@ func (fn *formulaFuncs) kth(name string, argsList *list.List) formulaArg { return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 2 arguments", name)) } array := argsList.Front().Value.(formulaArg).ToList() - kArg := argsList.Back().Value.(formulaArg).ToNumber() - if kArg.Type != ArgNumber { - return kArg + argK := argsList.Back().Value.(formulaArg).ToNumber() + if argK.Type != ArgNumber { + return argK } - k := int(kArg.Number) + k := int(argK.Number) if k < 1 { return newErrorFormulaArg(formulaErrorNUM, "k should be > 0") } @@ -7177,7 +7177,7 @@ func (fn *formulaFuncs) VLOOKUP(argsList *list.List) formulaArg { func vlookupBinarySearch(tableArray, lookupValue formulaArg) (matchIdx int, wasExact bool) { var low, high, lastMatchIdx int = 0, len(tableArray.Matrix) - 1, -1 for low <= high { - var mid int = low + (high-low)/2 + mid := low + (high-low)/2 mtx := tableArray.Matrix[mid] lhs := mtx[0] switch lookupValue.Type { @@ -7216,7 +7216,7 @@ func vlookupBinarySearch(tableArray, lookupValue formulaArg) (matchIdx int, wasE func hlookupBinarySearch(row []formulaArg, lookupValue formulaArg) (matchIdx int, wasExact bool) { var low, high, lastMatchIdx int = 0, len(row) - 1, -1 for low <= high { - var mid int = low + (high-low)/2 + mid := low + (high-low)/2 mtx := row[mid] result := compareFormulaArg(mtx, lookupValue, false, false) if result == criteriaEq { diff --git a/cell.go b/cell.go index 9200d13..6ad5f44 100644 --- a/cell.go +++ b/cell.go @@ -101,6 +101,28 @@ func (f *File) SetCellValue(sheet, axis string, value interface{}) error { return err } +// String extracts characters from a string item. +func (x xlsxSI) String() string { + if len(x.R) > 0 { + var rows strings.Builder + for _, s := range x.R { + if s.T != nil { + rows.WriteString(s.T.Val) + } + } + return bstrUnmarshal(rows.String()) + } + if x.T != nil { + return bstrUnmarshal(x.T.Val) + } + return "" +} + +// hasValue determine if cell non-blank value. +func (c *xlsxC) hasValue() bool { + return c.S != 0 || c.V != "" || c.F != nil || c.T != "" +} + // setCellIntFunc is a wrapper of SetCellInt. func (f *File) setCellIntFunc(sheet, axis string, value interface{}) error { var err error @@ -431,13 +453,11 @@ func (f *File) GetCellHyperLink(sheet, axis string) (bool, string, error) { if _, _, err := SplitCellName(axis); err != nil { return false, "", err } - ws, err := f.workSheetReader(sheet) if err != nil { return false, "", err } - axis, err = f.mergeCellsParser(ws, axis) - if err != nil { + if axis, err = f.mergeCellsParser(ws, axis); err != nil { return false, "", err } if ws.Hyperlinks != nil { @@ -485,8 +505,7 @@ func (f *File) SetCellHyperLink(sheet, axis, link, linkType string, opts ...Hype if err != nil { return err } - axis, err = f.mergeCellsParser(ws, axis) - if err != nil { + if axis, err = f.mergeCellsParser(ws, axis); err != nil { return err } @@ -932,7 +951,7 @@ func (f *File) checkCellInArea(cell, area string) (bool, error) { if len(rng) != 2 { return false, err } - coordinates, err := f.areaRefToCoordinates(area) + coordinates, err := areaRefToCoordinates(area) if err != nil { return false, err } diff --git a/col.go b/col.go index 088fac9..7fbeeba 100644 --- a/col.go +++ b/col.go @@ -616,10 +616,10 @@ func (f *File) positionObjectPixels(sheet string, col, row, x1, y1, width, heigh // getColWidth provides a function to get column width in pixels by given // sheet name and column number. func (f *File) getColWidth(sheet string, col int) int { - xlsx, _ := f.workSheetReader(sheet) - if xlsx.Cols != nil { + ws, _ := f.workSheetReader(sheet) + if ws.Cols != nil { var width float64 - for _, v := range xlsx.Cols.Col { + for _, v := range ws.Cols.Col { if v.Min <= col && col <= v.Max { width = v.Width } diff --git a/lib.go b/lib.go index 7db14c4..912f738 100644 --- a/lib.go +++ b/lib.go @@ -221,12 +221,11 @@ func CoordinatesToCellName(col, row int, abs ...bool) (string, error) { // areaRefToCoordinates provides a function to convert area reference to a // pair of coordinates. -func (f *File) areaRefToCoordinates(ref string) ([]int, error) { +func areaRefToCoordinates(ref string) ([]int, error) { rng := strings.Split(strings.Replace(ref, "$", "", -1), ":") if len(rng) < 2 { return nil, ErrParameterInvalid } - return areaRangeToCoordinates(rng[0], rng[1]) } @@ -290,7 +289,7 @@ func (f *File) flatSqref(sqref string) (cells map[int][][]int, err error) { } cells[col] = append(cells[col], []int{col, row}) case 2: - if coordinates, err = f.areaRefToCoordinates(ref); err != nil { + if coordinates, err = areaRefToCoordinates(ref); err != nil { return } _ = sortCoordinates(coordinates) diff --git a/merge.go b/merge.go index 7769b89..1cd8acd 100644 --- a/merge.go +++ b/merge.go @@ -11,10 +11,16 @@ package excelize -import ( - "fmt" - "strings" -) +import "strings" + +// Rect gets merged cell rectangle coordinates sequence. +func (mc *xlsxMergeCell) Rect() ([]int, error) { + var err error + if mc.rect == nil { + mc.rect, err = areaRefToCoordinates(mc.Ref) + } + return mc.rect, err +} // MergeCell provides a function to merge cells by given coordinate area and // sheet name. Merging cells only keeps the upper-left cell value, and @@ -24,7 +30,9 @@ import ( // err := f.MergeCell("Sheet1", "D3", "E9") // // If you create a merged cell that overlaps with another existing merged cell, -// those merged cells that already exist will be removed. +// those merged cells that already exist will be removed. The cell coordinates +// tuple after merging in the following range will be: A1(x3,y1) D1(x2,y1) +// A8(x3,y4) D8(x2,y4) // // B1(x1,y1) D1(x2,y1) // +------------------------+ @@ -39,15 +47,15 @@ import ( // +------------------------+ // func (f *File) MergeCell(sheet, hcell, vcell string) error { - rect1, err := f.areaRefToCoordinates(hcell + ":" + vcell) + rect, err := areaRefToCoordinates(hcell + ":" + vcell) if err != nil { return err } // Correct the coordinate area, such correct C1:B3 to B1:C3. - _ = sortCoordinates(rect1) + _ = sortCoordinates(rect) - hcell, _ = CoordinatesToCellName(rect1[0], rect1[1]) - vcell, _ = CoordinatesToCellName(rect1[2], rect1[3]) + hcell, _ = CoordinatesToCellName(rect[0], rect[1]) + vcell, _ = CoordinatesToCellName(rect[2], rect[3]) ws, err := f.workSheetReader(sheet) if err != nil { @@ -55,49 +63,9 @@ func (f *File) MergeCell(sheet, hcell, vcell string) error { } ref := hcell + ":" + vcell if ws.MergeCells != nil { - for i := 0; i < len(ws.MergeCells.Cells); i++ { - cellData := ws.MergeCells.Cells[i] - if cellData == nil { - continue - } - cc := strings.Split(cellData.Ref, ":") - if len(cc) != 2 { - return fmt.Errorf("invalid area %q", cellData.Ref) - } - - rect2, err := f.areaRefToCoordinates(cellData.Ref) - if err != nil { - return err - } - - // Delete the merged cells of the overlapping area. - if isOverlap(rect1, rect2) { - ws.MergeCells.Cells = append(ws.MergeCells.Cells[:i], ws.MergeCells.Cells[i+1:]...) - i-- - - if rect1[0] > rect2[0] { - rect1[0], rect2[0] = rect2[0], rect1[0] - } - - if rect1[2] < rect2[2] { - rect1[2], rect2[2] = rect2[2], rect1[2] - } - - if rect1[1] > rect2[1] { - rect1[1], rect2[1] = rect2[1], rect1[1] - } - - if rect1[3] < rect2[3] { - rect1[3], rect2[3] = rect2[3], rect1[3] - } - hcell, _ = CoordinatesToCellName(rect1[0], rect1[1]) - vcell, _ = CoordinatesToCellName(rect1[2], rect1[3]) - ref = hcell + ":" + vcell - } - } - ws.MergeCells.Cells = append(ws.MergeCells.Cells, &xlsxMergeCell{Ref: ref}) + ws.MergeCells.Cells = append(ws.MergeCells.Cells, &xlsxMergeCell{Ref: ref, rect: rect}) } else { - ws.MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: ref}}} + ws.MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: ref, rect: rect}}} } ws.MergeCells.Count = len(ws.MergeCells.Cells) return err @@ -114,7 +82,7 @@ func (f *File) UnmergeCell(sheet string, hcell, vcell string) error { if err != nil { return err } - rect1, err := f.areaRefToCoordinates(hcell + ":" + vcell) + rect1, err := areaRefToCoordinates(hcell + ":" + vcell) if err != nil { return err } @@ -126,26 +94,19 @@ func (f *File) UnmergeCell(sheet string, hcell, vcell string) error { if ws.MergeCells == nil { return nil } - + if err = f.mergeOverlapCells(ws); err != nil { + return err + } i := 0 - for _, cellData := range ws.MergeCells.Cells { - if cellData == nil { + for _, mergeCell := range ws.MergeCells.Cells { + if mergeCell == nil { continue } - cc := strings.Split(cellData.Ref, ":") - if len(cc) != 2 { - return fmt.Errorf("invalid area %q", cellData.Ref) - } - - rect2, err := f.areaRefToCoordinates(cellData.Ref) - if err != nil { - return err - } - + rect2, _ := areaRefToCoordinates(mergeCell.Ref) if isOverlap(rect1, rect2) { continue } - ws.MergeCells.Cells[i] = cellData + ws.MergeCells.Cells[i] = mergeCell i++ } ws.MergeCells.Cells = ws.MergeCells.Cells[:i] @@ -165,8 +126,10 @@ func (f *File) GetMergeCells(sheet string) ([]MergeCell, error) { return mergeCells, err } if ws.MergeCells != nil { + if err = f.mergeOverlapCells(ws); err != nil { + return mergeCells, err + } mergeCells = make([]MergeCell, 0, len(ws.MergeCells.Cells)) - for i := range ws.MergeCells.Cells { ref := ws.MergeCells.Cells[i].Ref axis := strings.Split(ref, ":")[0] @@ -174,10 +137,128 @@ func (f *File) GetMergeCells(sheet string) ([]MergeCell, error) { mergeCells = append(mergeCells, []string{ref, val}) } } - return mergeCells, err } +// overlapRange calculate overlap range of merged cells, and returns max +// column and rows of the range. +func overlapRange(ws *xlsxWorksheet) (row, col int, err error) { + var rect []int + for _, mergeCell := range ws.MergeCells.Cells { + if mergeCell == nil { + continue + } + if rect, err = mergeCell.Rect(); err != nil { + return + } + x1, y1, x2, y2 := rect[0], rect[1], rect[2], rect[3] + if x1 > col { + col = x1 + } + if x2 > col { + col = x2 + } + if y1 > row { + row = y1 + } + if y2 > row { + row = y2 + } + } + return +} + +// flatMergedCells convert merged cells range reference to cell-matrix. +func flatMergedCells(ws *xlsxWorksheet, matrix [][]*xlsxMergeCell) error { + for i, cell := range ws.MergeCells.Cells { + rect, err := cell.Rect() + if err != nil { + return err + } + x1, y1, x2, y2 := rect[0]-1, rect[1]-1, rect[2]-1, rect[3]-1 + var overlapCells []*xlsxMergeCell + for x := x1; x <= x2; x++ { + for y := y1; y <= y2; y++ { + if matrix[x][y] != nil { + overlapCells = append(overlapCells, matrix[x][y]) + } + matrix[x][y] = cell + } + } + if len(overlapCells) != 0 { + newCell := cell + for _, overlapCell := range overlapCells { + newCell = mergeCell(cell, overlapCell) + } + newRect, _ := newCell.Rect() + x1, y1, x2, y2 := newRect[0]-1, newRect[1]-1, newRect[2]-1, newRect[3]-1 + for x := x1; x <= x2; x++ { + for y := y1; y <= y2; y++ { + matrix[x][y] = newCell + } + } + ws.MergeCells.Cells[i] = newCell + } + } + return nil +} + +// mergeOverlapCells merge overlap cells. +func (f *File) mergeOverlapCells(ws *xlsxWorksheet) error { + rows, cols, err := overlapRange(ws) + if err != nil { + return err + } + if rows == 0 || cols == 0 { + return nil + } + matrix := make([][]*xlsxMergeCell, cols) + for i := range matrix { + matrix[i] = make([]*xlsxMergeCell, rows) + } + _ = flatMergedCells(ws, matrix) + mergeCells := ws.MergeCells.Cells[:0] + for _, cell := range ws.MergeCells.Cells { + rect, _ := cell.Rect() + x1, y1, x2, y2 := rect[0]-1, rect[1]-1, rect[2]-1, rect[3]-1 + if matrix[x1][y1] == cell { + mergeCells = append(mergeCells, cell) + for x := x1; x <= x2; x++ { + for y := y1; y <= y2; y++ { + matrix[x][y] = nil + } + } + } + } + ws.MergeCells.Count, ws.MergeCells.Cells = len(mergeCells), mergeCells + return nil +} + +// mergeCell merge two cells. +func mergeCell(cell1, cell2 *xlsxMergeCell) *xlsxMergeCell { + rect1, _ := cell1.Rect() + rect2, _ := cell2.Rect() + + if rect1[0] > rect2[0] { + rect1[0], rect2[0] = rect2[0], rect1[0] + } + + if rect1[2] < rect2[2] { + rect1[2], rect2[2] = rect2[2], rect1[2] + } + + if rect1[1] > rect2[1] { + rect1[1], rect2[1] = rect2[1], rect1[1] + } + + if rect1[3] < rect2[3] { + rect1[3], rect2[3] = rect2[3], rect1[3] + } + hcell, _ := CoordinatesToCellName(rect1[0], rect1[1]) + vcell, _ := CoordinatesToCellName(rect1[2], rect1[3]) + return &xlsxMergeCell{rect: rect1, Ref: hcell + ":" + vcell} +} + // MergeCell define a merged cell data. // It consists of the following structure. // example: []string{"D4:E10", "cell value"} diff --git a/merge_test.go b/merge_test.go index 41c122c..a370126 100644 --- a/merge_test.go +++ b/merge_test.go @@ -27,7 +27,7 @@ func TestMergeCell(t *testing.T) { assert.NoError(t, f.SetCellHyperLink("Sheet1", "J11", "https://github.com/xuri/excelize", "External")) assert.NoError(t, f.SetCellFormula("Sheet1", "G12", "SUM(Sheet1!B19,Sheet1!C19)")) value, err := f.GetCellValue("Sheet1", "H11") - assert.Equal(t, "0.5", value) + assert.Equal(t, "100", value) assert.NoError(t, err) value, err = f.GetCellValue("Sheet2", "A6") // Merged cell ref is single coordinate. assert.Equal(t, "", value) @@ -75,16 +75,24 @@ func TestMergeCell(t *testing.T) { assert.True(t, ok) ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{nil, nil}} assert.NoError(t, f.MergeCell("Sheet1", "A2", "B3")) +} - ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml") - assert.True(t, ok) - ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A1"}}} - assert.EqualError(t, f.MergeCell("Sheet1", "A2", "B3"), `invalid area "A1"`) +func TestMergeCellOverlap(t *testing.T) { + f := NewFile() + assert.NoError(t, f.MergeCell("Sheet1", "A1", "C2")) + assert.NoError(t, f.MergeCell("Sheet1", "B2", "D3")) + assert.NoError(t, f.SaveAs(filepath.Join("test", "TestMergeCellOverlap.xlsx"))) - ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml") - assert.True(t, ok) - ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}} - assert.EqualError(t, f.MergeCell("Sheet1", "A2", "B3"), `cannot convert cell "A" to coordinates: invalid cell name "A"`) + f, err := OpenFile(filepath.Join("test", "TestMergeCellOverlap.xlsx")) + if !assert.NoError(t, err) { + t.FailNow() + } + mc, err := f.GetMergeCells("Sheet1") + assert.NoError(t, err) + assert.Equal(t, 1, len(mc)) + assert.Equal(t, "A1", mc[0].GetStartAxis()) + assert.Equal(t, "D3", mc[0].GetEndAxis()) + assert.Equal(t, "", mc[0].GetCellValue()) } func TestGetMergeCells(t *testing.T) { @@ -173,11 +181,15 @@ func TestUnmergeCell(t *testing.T) { ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml") assert.True(t, ok) ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A1"}}} - assert.EqualError(t, f.UnmergeCell("Sheet1", "A2", "B3"), `invalid area "A1"`) + assert.EqualError(t, f.UnmergeCell("Sheet1", "A2", "B3"), "parameter is invalid") ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml") assert.True(t, ok) ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}} assert.EqualError(t, f.UnmergeCell("Sheet1", "A2", "B3"), `cannot convert cell "A" to coordinates: invalid cell name "A"`) - +} + +func TestFlatMergedCells(t *testing.T) { + ws := &xlsxWorksheet{MergeCells: &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A1"}}}} + assert.EqualError(t, flatMergedCells(ws, [][]*xlsxMergeCell{}), "parameter is invalid") } diff --git a/picture.go b/picture.go index d22a708..c37899e 100644 --- a/picture.go +++ b/picture.go @@ -148,6 +148,7 @@ func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string, if err != nil { return err } + ws.Lock() // Add first picture for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder. drawingID := f.countDrawings() + 1 drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml" @@ -162,6 +163,7 @@ func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string, } drawingHyperlinkRID = f.addRels(drawingRels, SourceRelationshipHyperLink, formatSet.Hyperlink, hyperlinkType) } + ws.Unlock() err = f.addDrawingPicture(sheet, drawingXML, cell, name, img.Width, img.Height, drawingRID, drawingHyperlinkRID, formatSet) if err != nil { return err diff --git a/pivotTable.go b/pivotTable.go index 07cf84c..0b0e5af 100644 --- a/pivotTable.go +++ b/pivotTable.go @@ -198,7 +198,7 @@ func (f *File) adjustRange(rangeStr string) (string, []int, error) { return "", []int{}, ErrParameterInvalid } trimRng := strings.Replace(rng[1], "$", "", -1) - coordinates, err := f.areaRefToCoordinates(trimRng) + coordinates, err := areaRefToCoordinates(trimRng) if err != nil { return rng[0], []int{}, err } diff --git a/rows.go b/rows.go index fcd3c1a..5fa2cb4 100644 --- a/rows.go +++ b/rows.go @@ -614,7 +614,7 @@ func (f *File) duplicateMergeCells(sheet string, ws *xlsxWorksheet, row, row2 in row++ } for _, rng := range ws.MergeCells.Cells { - coordinates, err := f.areaRefToCoordinates(rng.Ref) + coordinates, err := areaRefToCoordinates(rng.Ref) if err != nil { return err } @@ -624,7 +624,7 @@ func (f *File) duplicateMergeCells(sheet string, ws *xlsxWorksheet, row, row2 in } for i := 0; i < len(ws.MergeCells.Cells); i++ { areaData := ws.MergeCells.Cells[i] - coordinates, _ := f.areaRefToCoordinates(areaData.Ref) + coordinates, _ := areaRefToCoordinates(areaData.Ref) x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3] if y1 == y2 && y1 == row { from, _ := CoordinatesToCellName(x1, row2) diff --git a/sheet.go b/sheet.go index 756eb81..1c4b355 100644 --- a/sheet.go +++ b/sheet.go @@ -158,6 +158,9 @@ func (f *File) workSheetWriter() { f.Sheet.Range(func(p, ws interface{}) bool { if ws != nil { sheet := ws.(*xlsxWorksheet) + if sheet.MergeCells != nil && len(sheet.MergeCells.Cells) > 0 { + _ = f.mergeOverlapCells(sheet) + } for k, v := range sheet.SheetData.Row { sheet.SheetData.Row[k].C = trimCell(v.C) } diff --git a/xmlSharedStrings.go b/xmlSharedStrings.go index 816c931..e505d26 100644 --- a/xmlSharedStrings.go +++ b/xmlSharedStrings.go @@ -11,10 +11,7 @@ package excelize -import ( - "encoding/xml" - "strings" -) +import "encoding/xml" // xlsxSST directly maps the sst element from the namespace // http://schemas.openxmlformats.org/spreadsheetml/2006/main. String values may @@ -44,23 +41,6 @@ type xlsxSI struct { PhoneticPr *xlsxPhoneticPr `xml:"phoneticPr"` } -// String extracts characters from a string item. -func (x xlsxSI) String() string { - if len(x.R) > 0 { - var rows strings.Builder - for _, s := range x.R { - if s.T != nil { - rows.WriteString(s.T.Val) - } - } - return bstrUnmarshal(rows.String()) - } - if x.T != nil { - return bstrUnmarshal(x.T.Val) - } - return "" -} - // xlsxR represents a run of rich text. A rich text run is a region of text // that share a common set of properties, such as formatting properties. The // properties are defined in the rPr element, and the text displayed to the diff --git a/xmlWorksheet.go b/xmlWorksheet.go index 4499546..697504e 100644 --- a/xmlWorksheet.go +++ b/xmlWorksheet.go @@ -399,7 +399,8 @@ type xlsxCustomSheetView struct { // xlsxMergeCell directly maps the mergeCell element. A single merged cell. type xlsxMergeCell struct { - Ref string `xml:"ref,attr,omitempty"` + Ref string `xml:"ref,attr,omitempty"` + rect []int } // xlsxMergeCells directly maps the mergeCells element. This collection @@ -468,10 +469,6 @@ type xlsxC struct { IS *xlsxSI `xml:"is"` } -func (c *xlsxC) hasValue() bool { - return c.S != 0 || c.V != "" || c.F != nil || c.T != "" -} - // xlsxF represents a formula for the cell. The formula expression is // contained in the character node of this element. type xlsxF struct {