From 25e1459bb5f5cc1a74110c7a1e15d11c738e3436 Mon Sep 17 00:00:00 2001 From: xuri Date: Sun, 1 May 2022 12:28:36 +0800 Subject: [PATCH] This closes #1217, support update cell hyperlink Ref #1129, make `SetRowStyle` overwrite style of the cells --- cell.go | 24 +++++++++++++++++------- excelize.go | 22 ++++++++++++++++++++++ excelize_test.go | 16 +++++++++++----- rows.go | 5 +++++ rows_test.go | 15 +++++++++------ 5 files changed, 64 insertions(+), 18 deletions(-) diff --git a/cell.go b/cell.go index 1d6ed847f6..70832cef83 100644 --- a/cell.go +++ b/cell.go @@ -715,10 +715,17 @@ func (f *File) SetCellHyperLink(sheet, axis, link, linkType string, opts ...Hype } var linkData xlsxHyperlink - + idx := -1 if ws.Hyperlinks == nil { ws.Hyperlinks = new(xlsxHyperlinks) } + for i, hyperlink := range ws.Hyperlinks.Hyperlink { + if hyperlink.Ref == axis { + idx = i + linkData = hyperlink + break + } + } if len(ws.Hyperlinks.Hyperlink) > TotalSheetHyperlinks { return ErrTotalSheetHyperlinks @@ -726,12 +733,12 @@ func (f *File) SetCellHyperLink(sheet, axis, link, linkType string, opts ...Hype switch linkType { case "External": + sheetPath := f.sheetMap[trimSheetName(sheet)] + sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels" + rID := f.setRels(linkData.RID, sheetRels, SourceRelationshipHyperLink, link, linkType) linkData = xlsxHyperlink{ Ref: axis, } - sheetPath := f.sheetMap[trimSheetName(sheet)] - sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels" - rID := f.addRels(sheetRels, SourceRelationshipHyperLink, link, linkType) linkData.RID = "rId" + strconv.Itoa(rID) f.addSheetNameSpace(sheet, SourceRelationship) case "Location": @@ -751,9 +758,12 @@ func (f *File) SetCellHyperLink(sheet, axis, link, linkType string, opts ...Hype linkData.Tooltip = *o.Tooltip } } - - ws.Hyperlinks.Hyperlink = append(ws.Hyperlinks.Hyperlink, linkData) - return nil + if idx == -1 { + ws.Hyperlinks.Hyperlink = append(ws.Hyperlinks.Hyperlink, linkData) + return err + } + ws.Hyperlinks.Hyperlink[idx] = linkData + return err } // getCellRichText returns rich text of cell by given string item. diff --git a/excelize.go b/excelize.go index 9fe3d8816d..d78d2b1ea5 100644 --- a/excelize.go +++ b/excelize.go @@ -327,6 +327,28 @@ func checkSheetR0(ws *xlsxWorksheet, sheetData *xlsxSheetData, r0 *xlsxRow) { ws.SheetData = *sheetData } +// setRels provides a function to set relationships by given relationship ID, +// XML path, relationship type, target and target mode. +func (f *File) setRels(rID, relPath, relType, target, targetMode string) int { + rels := f.relsReader(relPath) + if rels == nil || rID == "" { + return f.addRels(relPath, relType, target, targetMode) + } + rels.Lock() + defer rels.Unlock() + var ID int + for i, rel := range rels.Relationships { + if rel.ID == rID { + rels.Relationships[i].Type = relType + rels.Relationships[i].Target = target + rels.Relationships[i].TargetMode = targetMode + ID, _ = strconv.Atoi(strings.TrimPrefix(rID, "rId")) + break + } + } + return ID +} + // addRels provides a function to add relationships by given XML path, // relationship type, target and target mode. func (f *File) addRels(relPath, relType, target, targetMode string) int { diff --git a/excelize_test.go b/excelize_test.go index dc5dfccf80..389573e296 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -336,9 +336,7 @@ func TestAddDrawingVML(t *testing.T) { func TestSetCellHyperLink(t *testing.T) { f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) - if err != nil { - t.Log(err) - } + assert.NoError(t, err) // Test set cell hyperlink in a work sheet already have hyperlinks. assert.NoError(t, f.SetCellHyperLink("Sheet1", "B19", "https://github.com/xuri/excelize", "External")) // Test add first hyperlink in a work sheet. @@ -346,8 +344,7 @@ func TestSetCellHyperLink(t *testing.T) { // Test add Location hyperlink in a work sheet. assert.NoError(t, f.SetCellHyperLink("Sheet2", "D6", "Sheet1!D8", "Location")) // Test add Location hyperlink with display & tooltip in a work sheet. - display := "Display value" - tooltip := "Hover text" + display, tooltip := "Display value", "Hover text" assert.NoError(t, f.SetCellHyperLink("Sheet2", "D7", "Sheet1!D9", "Location", HyperlinkOpts{ Display: &display, Tooltip: &tooltip, @@ -376,6 +373,15 @@ func TestSetCellHyperLink(t *testing.T) { ws.(*xlsxWorksheet).MergeCells = &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:A"}}} err = f.SetCellHyperLink("Sheet1", "A1", "https://github.com/xuri/excelize", "External") assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) + + // Test update cell hyperlink + f = NewFile() + assert.NoError(t, f.SetCellHyperLink("Sheet1", "A1", "https://github.com", "External")) + assert.NoError(t, f.SetCellHyperLink("Sheet1", "A1", "https://github.com/xuri/excelize", "External")) + link, target, err := f.GetCellHyperLink("Sheet1", "A1") + assert.Equal(t, link, true) + assert.Equal(t, "https://github.com/xuri/excelize", target) + assert.NoError(t, err) } func TestGetCellHyperLink(t *testing.T) { diff --git a/rows.go b/rows.go index e0918bc60e..bcb8960de6 100644 --- a/rows.go +++ b/rows.go @@ -841,6 +841,11 @@ func (f *File) SetRowStyle(sheet string, start, end, styleID int) error { for row := start - 1; row < end; row++ { ws.SheetData.Row[row].S = styleID ws.SheetData.Row[row].CustomFormat = true + for i := range ws.SheetData.Row[row].C { + if _, rowNum, err := CellNameToCoordinates(ws.SheetData.Row[row].C[i].R); err == nil && rowNum-1 == row { + ws.SheetData.Row[row].C[i].S = styleID + } + } } return nil } diff --git a/rows_test.go b/rows_test.go index 22b038aa8a..ae3083811a 100644 --- a/rows_test.go +++ b/rows_test.go @@ -915,16 +915,19 @@ func TestCheckRow(t *testing.T) { func TestSetRowStyle(t *testing.T) { f := NewFile() - styleID, err := f.NewStyle(`{"fill":{"type":"pattern","color":["#E0EBF5"],"pattern":1}}`) + style1, err := f.NewStyle(`{"fill":{"type":"pattern","color":["#63BE7B"],"pattern":1}}`) assert.NoError(t, err) - assert.EqualError(t, f.SetRowStyle("Sheet1", 10, -1, styleID), newInvalidRowNumberError(-1).Error()) - assert.EqualError(t, f.SetRowStyle("Sheet1", 1, TotalRows+1, styleID), ErrMaxRows.Error()) + style2, err := f.NewStyle(`{"fill":{"type":"pattern","color":["#E0EBF5"],"pattern":1}}`) + assert.NoError(t, err) + assert.NoError(t, f.SetCellStyle("Sheet1", "B2", "B2", style1)) + assert.EqualError(t, f.SetRowStyle("Sheet1", 5, -1, style2), newInvalidRowNumberError(-1).Error()) + assert.EqualError(t, f.SetRowStyle("Sheet1", 1, TotalRows+1, style2), ErrMaxRows.Error()) 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)) + assert.EqualError(t, f.SetRowStyle("SheetN", 1, 1, style2), "sheet SheetN is not exist") + assert.NoError(t, f.SetRowStyle("Sheet1", 5, 1, style2)) cellStyleID, err := f.GetCellStyle("Sheet1", "B2") assert.NoError(t, err) - assert.Equal(t, styleID, cellStyleID) + assert.Equal(t, style2, cellStyleID) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetRowStyle.xlsx"))) }