From 85e1f17788411be5a562549334f877e94e6dacfe Mon Sep 17 00:00:00 2001 From: xuri Date: Thu, 27 Jan 2022 22:37:32 +0800 Subject: [PATCH] This closes #1129, make cell support inheritance columns/rows style Correct cells style in merge range Fix incorrect style ID returned on getting cell style in some cases Unit test updated and simplified code --- cell.go | 52 +++++++++++++++++++++++++++--------------------- col.go | 11 +++++----- col_test.go | 20 ++++++++++++------- excelize_test.go | 1 + merge.go | 7 +++++-- rows_test.go | 3 +++ styles.go | 4 ++-- 7 files changed, 58 insertions(+), 40 deletions(-) diff --git a/cell.go b/cell.go index ff9a131f80..35060ab4e4 100644 --- a/cell.go +++ b/cell.go @@ -200,12 +200,12 @@ func (f *File) setCellTimeFunc(sheet, axis string, value time.Time) error { if err != nil { return err } - cellData, col, _, err := f.prepareCell(ws, sheet, axis) + cellData, col, row, err := f.prepareCell(ws, sheet, axis) if err != nil { return err } ws.Lock() - cellData.S = f.prepareCellStyle(ws, col, cellData.S) + cellData.S = f.prepareCellStyle(ws, col, row, cellData.S) ws.Unlock() var isNum bool @@ -214,10 +214,7 @@ func (f *File) setCellTimeFunc(sheet, axis string, value time.Time) error { return err } if isNum { - err = f.setDefaultTimeStyle(sheet, axis, 22) - if err != nil { - return err - } + _ = f.setDefaultTimeStyle(sheet, axis, 22) } return err } @@ -254,13 +251,13 @@ func (f *File) SetCellInt(sheet, axis string, value int) error { if err != nil { return err } - cellData, col, _, err := f.prepareCell(ws, sheet, axis) + cellData, col, row, err := f.prepareCell(ws, sheet, axis) if err != nil { return err } ws.Lock() defer ws.Unlock() - cellData.S = f.prepareCellStyle(ws, col, cellData.S) + cellData.S = f.prepareCellStyle(ws, col, row, cellData.S) cellData.T, cellData.V = setCellInt(value) return err } @@ -279,13 +276,13 @@ func (f *File) SetCellBool(sheet, axis string, value bool) error { if err != nil { return err } - cellData, col, _, err := f.prepareCell(ws, sheet, axis) + cellData, col, row, err := f.prepareCell(ws, sheet, axis) if err != nil { return err } ws.Lock() defer ws.Unlock() - cellData.S = f.prepareCellStyle(ws, col, cellData.S) + cellData.S = f.prepareCellStyle(ws, col, row, cellData.S) cellData.T, cellData.V = setCellBool(value) return err } @@ -316,13 +313,13 @@ func (f *File) SetCellFloat(sheet, axis string, value float64, prec, bitSize int if err != nil { return err } - cellData, col, _, err := f.prepareCell(ws, sheet, axis) + cellData, col, row, err := f.prepareCell(ws, sheet, axis) if err != nil { return err } ws.Lock() defer ws.Unlock() - cellData.S = f.prepareCellStyle(ws, col, cellData.S) + cellData.S = f.prepareCellStyle(ws, col, row, cellData.S) cellData.T, cellData.V = setCellFloat(value, prec, bitSize) return err } @@ -341,13 +338,13 @@ func (f *File) SetCellStr(sheet, axis, value string) error { if err != nil { return err } - cellData, col, _, err := f.prepareCell(ws, sheet, axis) + cellData, col, row, err := f.prepareCell(ws, sheet, axis) if err != nil { return err } ws.Lock() defer ws.Unlock() - cellData.S = f.prepareCellStyle(ws, col, cellData.S) + cellData.S = f.prepareCellStyle(ws, col, row, cellData.S) cellData.T, cellData.V, err = f.setCellString(value) return err } @@ -439,13 +436,13 @@ func (f *File) SetCellDefault(sheet, axis, value string) error { if err != nil { return err } - cellData, col, _, err := f.prepareCell(ws, sheet, axis) + cellData, col, row, err := f.prepareCell(ws, sheet, axis) if err != nil { return err } ws.Lock() defer ws.Unlock() - cellData.S = f.prepareCellStyle(ws, col, cellData.S) + cellData.S = f.prepareCellStyle(ws, col, row, cellData.S) cellData.T, cellData.V = setCellDefault(value) return err } @@ -937,14 +934,14 @@ func (f *File) SetCellRichText(sheet, cell string, runs []RichTextRun) error { if err != nil { return err } - cellData, col, _, err := f.prepareCell(ws, sheet, cell) + cellData, col, row, err := f.prepareCell(ws, sheet, cell) if err != nil { return err } if err := f.sharedStringsLoader(); err != nil { return err } - cellData.S = f.prepareCellStyle(ws, col, cellData.S) + cellData.S = f.prepareCellStyle(ws, col, row, cellData.S) si := xlsxSI{} sst := f.sharedStringsReader() textRuns := []xlsxR{} @@ -1133,14 +1130,19 @@ func isTimeNumFmt(format string) bool { // prepareCellStyle provides a function to prepare style index of cell in // worksheet by given column index and style index. -func (f *File) prepareCellStyle(ws *xlsxWorksheet, col, style int) int { +func (f *File) prepareCellStyle(ws *xlsxWorksheet, col, row, style int) int { if ws.Cols != nil && style == 0 { for _, c := range ws.Cols.Col { - if c.Min <= col && col <= c.Max { - style = c.Style + if c.Min <= col && col <= c.Max && c.Style != 0 { + return c.Style } } } + for rowIdx := range ws.SheetData.Row { + if styleID := ws.SheetData.Row[rowIdx].S; style == 0 && styleID != 0 { + return styleID + } + } return style } @@ -1150,6 +1152,11 @@ func (f *File) mergeCellsParser(ws *xlsxWorksheet, axis string) (string, error) axis = strings.ToUpper(axis) if ws.MergeCells != nil { for i := 0; i < len(ws.MergeCells.Cells); i++ { + if ws.MergeCells.Cells[i] == nil { + ws.MergeCells.Cells = append(ws.MergeCells.Cells[:i], ws.MergeCells.Cells[i+1:]...) + i-- + continue + } ok, err := f.checkCellInArea(axis, ws.MergeCells.Cells[i].Ref) if err != nil { return axis, err @@ -1170,8 +1177,7 @@ func (f *File) checkCellInArea(cell, area string) (bool, error) { return false, err } - rng := strings.Split(area, ":") - if len(rng) != 2 { + if rng := strings.Split(area, ":"); len(rng) != 2 { return false, err } coordinates, err := areaRefToCoordinates(area) diff --git a/col.go b/col.go index a496e88f9c..16d1f93c12 100644 --- a/col.go +++ b/col.go @@ -592,9 +592,8 @@ func (f *File) positionObjectPixels(sheet string, col, row, x1, y1, width, heigh row++ } - // Initialise end cell to the same as the start cell. - colEnd := col - rowEnd := row + // Initialized end cell to the same as the start cell. + colEnd, rowEnd := col, row width += x1 height += y1 @@ -632,7 +631,7 @@ func (f *File) getColWidth(sheet string, col int) int { return int(convertColWidthToPixels(width)) } } - // Optimisation for when the column widths haven't changed. + // Optimization for when the column widths haven't changed. return int(defaultColWidthPixels) } @@ -658,7 +657,7 @@ func (f *File) GetColWidth(sheet, col string) (float64, error) { return width, err } } - // Optimisation for when the column widths haven't changed. + // Optimization for when the column widths haven't changed. return defaultColWidth, err } @@ -707,7 +706,7 @@ func (f *File) RemoveCol(sheet, col string) error { return f.adjustHelper(sheet, columns, num, -1) } -// convertColWidthToPixels provieds function to convert the width of a cell +// convertColWidthToPixels provides function to convert the width of a cell // from user's units to pixels. Excel rounds the column width to the nearest // pixel. If the width hasn't been set by the user we use the default value. // If the column is hidden it has a value of zero. diff --git a/col_test.go b/col_test.go index e325ed17ce..df74523a27 100644 --- a/col_test.go +++ b/col_test.go @@ -289,18 +289,24 @@ func TestOutlineLevel(t *testing.T) { func TestSetColStyle(t *testing.T) { f := NewFile() assert.NoError(t, f.SetCellValue("Sheet1", "B2", "Hello")) - style, err := f.NewStyle(`{"fill":{"type":"pattern","color":["#94d3a2"],"pattern":1}}`) + styleID, err := f.NewStyle(`{"fill":{"type":"pattern","color":["#94d3a2"],"pattern":1}}`) assert.NoError(t, err) // Test set column style on not exists worksheet. - assert.EqualError(t, f.SetColStyle("SheetN", "E", style), "sheet SheetN is not exist") + assert.EqualError(t, f.SetColStyle("SheetN", "E", styleID), "sheet SheetN is not exist") // Test set column style with illegal cell coordinates. - assert.EqualError(t, f.SetColStyle("Sheet1", "*", style), newInvalidColumnNameError("*").Error()) - assert.EqualError(t, f.SetColStyle("Sheet1", "A:*", style), newInvalidColumnNameError("*").Error()) + assert.EqualError(t, f.SetColStyle("Sheet1", "*", styleID), newInvalidColumnNameError("*").Error()) + assert.EqualError(t, f.SetColStyle("Sheet1", "A:*", styleID), newInvalidColumnNameError("*").Error()) - assert.NoError(t, f.SetColStyle("Sheet1", "B", style)) + assert.NoError(t, f.SetColStyle("Sheet1", "B", styleID)) // Test set column style with already exists column with style. - assert.NoError(t, f.SetColStyle("Sheet1", "B", style)) - assert.NoError(t, f.SetColStyle("Sheet1", "D:C", style)) + assert.NoError(t, f.SetColStyle("Sheet1", "B", styleID)) + assert.NoError(t, f.SetColStyle("Sheet1", "D:C", styleID)) + ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml") + assert.True(t, ok) + ws.(*xlsxWorksheet).SheetData.Row[1].C[2].S = 0 + cellStyleID, err := f.GetCellStyle("Sheet1", "C2") + assert.NoError(t, err) + assert.Equal(t, styleID, cellStyleID) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetColStyle.xlsx"))) } diff --git a/excelize_test.go b/excelize_test.go index 1548cc6f5a..bafd446247 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -147,6 +147,7 @@ func TestOpenFile(t *testing.T) { assert.NoError(t, f.SetCellValue("Sheet2", "G4", time.Now())) assert.NoError(t, f.SetCellValue("Sheet2", "G4", time.Now().UTC())) + assert.EqualError(t, f.SetCellValue("SheetN", "A1", time.Now()), "sheet SheetN is not exist") // 02:46:40 assert.NoError(t, f.SetCellValue("Sheet2", "G5", time.Duration(1e13))) // Test completion column. diff --git a/merge.go b/merge.go index 7119d28632..3ba7d6ad0d 100644 --- a/merge.go +++ b/merge.go @@ -11,7 +11,9 @@ package excelize -import "strings" +import ( + "strings" +) // Rect gets merged cell rectangle coordinates sequence. func (mc *xlsxMergeCell) Rect() ([]int, error) { @@ -68,7 +70,8 @@ func (f *File) MergeCell(sheet, hCell, vCell string) error { ws.MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: ref, rect: rect}}} } ws.MergeCells.Count = len(ws.MergeCells.Cells) - return err + styleID, _ := f.GetCellStyle(sheet, hCell) + return f.SetCellStyle(sheet, hCell, vCell, styleID) } // UnmergeCell provides a function to unmerge a given coordinate area. diff --git a/rows_test.go b/rows_test.go index 4d81e6698b..1286a3773d 100644 --- a/rows_test.go +++ b/rows_test.go @@ -921,6 +921,9 @@ func TestSetRowStyle(t *testing.T) { assert.EqualError(t, f.SetRowStyle("Sheet1", 1, 1, -1), newInvalidStyleID(-1).Error()) assert.EqualError(t, f.SetRowStyle("SheetN", 1, 1, styleID), "sheet SheetN is not exist") assert.NoError(t, f.SetRowStyle("Sheet1", 10, 1, styleID)) + cellStyleID, err := f.GetCellStyle("Sheet1", "B2") + assert.NoError(t, err) + assert.Equal(t, styleID, cellStyleID) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetRowStyle.xlsx"))) } diff --git a/styles.go b/styles.go index 5c887dbd75..5d373d38df 100644 --- a/styles.go +++ b/styles.go @@ -2613,11 +2613,11 @@ func (f *File) GetCellStyle(sheet, axis string) (int, error) { if err != nil { return 0, err } - cellData, col, _, err := f.prepareCell(ws, sheet, axis) + cellData, col, row, err := f.prepareCell(ws, sheet, axis) if err != nil { return 0, err } - return f.prepareCellStyle(ws, col, cellData.S), err + return f.prepareCellStyle(ws, col, row, cellData.S), err } // SetCellStyle provides a function to add style attribute for cells by given