From e51aff2d9562bbfb290ef76a948facb6d4660eff Mon Sep 17 00:00:00 2001 From: xuri Date: Fri, 7 Feb 2020 00:25:01 +0800 Subject: [PATCH] Resolve #570, flat columns for the column's operation --- .travis.yml | 2 +- CONTRIBUTING.md | 6 ++- col.go | 123 +++++++++++++++++++++++++++++++++--------------- col_test.go | 4 +- merge.go | 25 +++++----- picture.go | 4 +- sheet.go | 4 +- table.go | 6 ++- xmlWorksheet.go | 8 ++-- 9 files changed, 116 insertions(+), 66 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5012f86d..1cb1d496 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ os: - osx env: - matrix: + jobs: - GOARCH=amd64 - GOARCH=386 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index afb7d4ee..53c650e5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -234,7 +234,9 @@ By making a contribution to this project, I certify that: Then you just add a line to every git commit message: - Signed-off-by: Ri Xu https://xuri.me +```text +Signed-off-by: Ri Xu https://xuri.me +``` Use your real name (sorry, no pseudonyms or anonymous contributions.) @@ -460,4 +462,4 @@ Do not use package math/rand to generate keys, even throwaway ones. Unseeded, the generator is completely predictable. Seeded with time.Nanoseconds(), there are just a few bits of entropy. Instead, use crypto/rand's Reader, and if you need text, print to -hexadecimal or base64 +hexadecimal or base64. diff --git a/col.go b/col.go index ff771f16..6f768003 100644 --- a/col.go +++ b/col.go @@ -13,6 +13,8 @@ import ( "errors" "math" "strings" + + "github.com/mohae/deepcopy" ) // Define the default cell size and EMU unit of measurement. @@ -59,7 +61,7 @@ func (f *File) GetColVisible(sheet, col string) (bool, error) { // // err := f.SetColVisible("Sheet1", "D", false) // -// Hide the columns from D to F (included) +// Hide the columns from D to F (included): // // err := f.SetColVisible("Sheet1", "D:F", false) // @@ -87,23 +89,31 @@ func (f *File) SetColVisible(sheet, columns string, visible bool) error { return err } colData := xlsxCol{ - Min: min, - Max: max, - Width: 9, // default width - Hidden: !visible, + Min: min, + Max: max, + Width: 9, // default width + Hidden: !visible, CustomWidth: true, } - if xlsx.Cols != nil { - xlsx.Cols.Col = append(xlsx.Cols.Col, colData) - } else { + if xlsx.Cols == nil { cols := xlsxCols{} cols.Col = append(cols.Col, colData) xlsx.Cols = &cols + return nil } + xlsx.Cols.Col = flatCols(colData, xlsx.Cols.Col, func(fc, c xlsxCol) xlsxCol { + fc.BestFit = c.BestFit + fc.Collapsed = c.Collapsed + fc.CustomWidth = c.CustomWidth + fc.OutlineLevel = c.OutlineLevel + fc.Phonetic = c.Phonetic + fc.Style = c.Style + fc.Width = c.Width + return fc + }) return nil } - // GetColOutlineLevel provides a function to get outline level of a single // column by given worksheet name and column name. For example, get outline // level of column D in Sheet1: @@ -162,16 +172,16 @@ func (f *File) SetColOutlineLevel(sheet, col string, level uint8) error { xlsx.Cols = &cols return err } - for v := range xlsx.Cols.Col { - if xlsx.Cols.Col[v].Min <= colNum && colNum <= xlsx.Cols.Col[v].Max { - colData = xlsx.Cols.Col[v] - } - } - colData.Min = colNum - colData.Max = colNum - colData.OutlineLevel = level - colData.CustomWidth = true - xlsx.Cols.Col = append(xlsx.Cols.Col, colData) + xlsx.Cols.Col = flatCols(colData, xlsx.Cols.Col, func(fc, c xlsxCol) xlsxCol { + fc.BestFit = c.BestFit + fc.Collapsed = c.Collapsed + fc.CustomWidth = c.CustomWidth + fc.Hidden = c.Hidden + fc.Phonetic = c.Phonetic + fc.Style = c.Style + fc.Width = c.Width + return fc + }) return err } @@ -214,21 +224,21 @@ func (f *File) SetColStyle(sheet, columns string, styleID int) error { if xlsx.Cols == nil { xlsx.Cols = &xlsxCols{} } - var find bool - for idx, col := range xlsx.Cols.Col { - if col.Min == min && col.Max == max { - xlsx.Cols.Col[idx].Style = styleID - find = true - } - } - if !find { - xlsx.Cols.Col = append(xlsx.Cols.Col, xlsxCol{ - Min: min, - Max: max, - Width: 9, - Style: styleID, - }) - } + xlsx.Cols.Col = flatCols(xlsxCol{ + Min: min, + Max: max, + Width: 9, + Style: styleID, + }, xlsx.Cols.Col, func(fc, c xlsxCol) xlsxCol { + fc.BestFit = c.BestFit + fc.Collapsed = c.Collapsed + fc.CustomWidth = c.CustomWidth + fc.Hidden = c.Hidden + fc.OutlineLevel = c.OutlineLevel + fc.Phonetic = c.Phonetic + fc.Width = c.Width + return fc + }) return nil } @@ -261,16 +271,55 @@ func (f *File) SetColWidth(sheet, startcol, endcol string, width float64) error Width: width, CustomWidth: true, } - if xlsx.Cols != nil { - xlsx.Cols.Col = append(xlsx.Cols.Col, col) - } else { + if xlsx.Cols == nil { cols := xlsxCols{} cols.Col = append(cols.Col, col) xlsx.Cols = &cols + return err } + xlsx.Cols.Col = flatCols(col, xlsx.Cols.Col, func(fc, c xlsxCol) xlsxCol { + fc.BestFit = c.BestFit + fc.Collapsed = c.Collapsed + fc.Hidden = c.Hidden + fc.OutlineLevel = c.OutlineLevel + fc.Phonetic = c.Phonetic + fc.Style = c.Style + return fc + }) return err } +// flatCols provides a method for the column's operation functions to flatten +// and check the worksheet columns. +func flatCols(col xlsxCol, cols []xlsxCol, replacer func(fc, c xlsxCol) xlsxCol) []xlsxCol { + fc := []xlsxCol{} + for i := col.Min; i <= col.Max; i++ { + c := deepcopy.Copy(col).(xlsxCol) + c.Min, c.Max = i, i + fc = append(fc, c) + } + inFlat := func(colID int, cols []xlsxCol) (int, bool) { + for idx, c := range cols { + if c.Max == colID && c.Min == colID { + return idx, true + } + } + return -1, false + } + for _, column := range cols { + for i := column.Min; i <= column.Max; i++ { + if idx, ok := inFlat(i, fc); ok { + fc[idx] = replacer(fc[idx], column) + continue + } + c := deepcopy.Copy(column).(xlsxCol) + c.Min, c.Max = i, i + fc = append(fc, c) + } + } + return fc +} + // positionObjectPixels calculate the vertices that define the position of a // graphical object within the worksheet in pixels. // diff --git a/col_test.go b/col_test.go index 08fac1ce..050c998e 100644 --- a/col_test.go +++ b/col_test.go @@ -31,7 +31,7 @@ func TestColumnVisibility(t *testing.T) { assert.Equal(t, false, visible) assert.NoError(t, err) // ...and displaying them back SetColVisible(...true) - assert.NoError(t, f.SetColVisible("Sheet1", "F:V", true)) + assert.NoError(t, f.SetColVisible("Sheet1", "V:F", true)) visible, err = f.GetColVisible("Sheet1", "F") assert.Equal(t, true, visible) assert.NoError(t, err) @@ -53,7 +53,7 @@ func TestColumnVisibility(t *testing.T) { f.NewSheet("Sheet3") assert.NoError(t, f.SetColVisible("Sheet3", "E", false)) - + assert.EqualError(t, f.SetColVisible("Sheet1", "A:-1", true), "invalid column name \"-1\"") assert.EqualError(t, f.SetColVisible("SheetN", "E", false), "sheet SheetN is not exist") assert.NoError(t, f.SaveAs(filepath.Join("test", "TestColumnVisibility.xlsx"))) }) diff --git a/merge.go b/merge.go index b952a1ed..f29640dd 100644 --- a/merge.go +++ b/merge.go @@ -22,20 +22,17 @@ import ( // If you create a merged cell that overlaps with another existing merged cell, // those merged cells that already exist will be removed. // -// B1(x1,y1) D1(x2,y1) -// +--------------------------------+ -// | | -// | | -// A4(x3,y3) | C4(x4,y3) | -// +-----------------------------+ | -// | | | | -// | | | | -// | |B5(x1,y2) | D5(x2,y2)| -// | +--------------------------------+ -// | | -// | | -// |A8(x3,y4) C8(x4,y4)| -// +-----------------------------+ +// B1(x1,y1) D1(x2,y1) +// +------------------------+ +// | | +// A4(x3,y3) | C4(x4,y3) | +// +------------------------+ | +// | | | | +// | |B5(x1,y2) | D5(x2,y2)| +// | +------------------------+ +// | | +// |A8(x3,y4) C8(x4,y4)| +// +------------------------+ // func (f *File) MergeCell(sheet, hcell, vcell string) error { rect1, err := f.areaRefToCoordinates(hcell + ":" + vcell) diff --git a/picture.go b/picture.go index 213bae9f..0b91b91f 100644 --- a/picture.go +++ b/picture.go @@ -462,8 +462,8 @@ func (f *File) GetPicture(sheet, cell string) (string, []byte, error) { return f.getPicture(row, col, drawingXML, drawingRelationships) } -// DeletePicture provides a function to delete chart in XLSX by given -// worksheet and cell name. Note that the image file won't deleted from the +// DeletePicture provides a function to delete charts in XLSX by given +// worksheet and cell name. Note that the image file won't be deleted from the // document currently. func (f *File) DeletePicture(sheet, cell string) (err error) { col, row, err := CellNameToCoordinates(cell) diff --git a/sheet.go b/sheet.go index 19b90c63..a6ff2a10 100644 --- a/sheet.go +++ b/sheet.go @@ -287,8 +287,8 @@ func (f *File) GetActiveSheetIndex() int { return 0 } -// SetSheetName provides a function to set the worksheet name be given old and -// new worksheet name. Maximum 31 characters are allowed in sheet title and +// SetSheetName provides a function to set the worksheet name by given old and +// new worksheet names. Maximum 31 characters are allowed in sheet title and // this function only changes the name of the sheet and will not update the // sheet name in the formula or reference associated with the cell. So there // may be problem formula error or reference missing. diff --git a/table.go b/table.go index 566238c6..55901cd9 100644 --- a/table.go +++ b/table.go @@ -39,8 +39,10 @@ func parseFormatTableSet(formatSet string) (*formatTable, error) { // // err := f.AddTable("Sheet2", "F2", "H6", `{"table_name":"table","table_style":"TableStyleMedium2", "show_first_column":true,"show_last_column":true,"show_row_stripes":false,"show_column_stripes":true}`) // -// Note that the table at least two lines include string type header. Multiple -// tables coordinate areas can't have an intersection. +// Note that the table must be at least two lines including the header. The +// header cells must contain strings and must be unique, and must set the +// header row data of the table before calling the AddTable function. Multiple +// tables coordinate areas that can't have an intersection. // // table_name: The name of the table, in the same worksheet name of the table should be unique // diff --git a/xmlWorksheet.go b/xmlWorksheet.go index ed304ccb..46253e61 100644 --- a/xmlWorksheet.go +++ b/xmlWorksheet.go @@ -278,15 +278,15 @@ type xlsxCols struct { // width and column formatting for one or more columns of the worksheet. type xlsxCol struct { BestFit bool `xml:"bestFit,attr,omitempty"` - Collapsed bool `xml:"collapsed,attr"` + Collapsed bool `xml:"collapsed,attr,omitempty"` CustomWidth bool `xml:"customWidth,attr,omitempty"` - Hidden bool `xml:"hidden,attr"` + Hidden bool `xml:"hidden,attr,omitempty"` Max int `xml:"max,attr"` Min int `xml:"min,attr"` OutlineLevel uint8 `xml:"outlineLevel,attr,omitempty"` Phonetic bool `xml:"phonetic,attr,omitempty"` - Style int `xml:"style,attr"` - Width float64 `xml:"width,attr"` + Style int `xml:"style,attr,omitempty"` + Width float64 `xml:"width,attr,omitempty"` } // xlsxDimension directly maps the dimension element in the namespace