From cbc3fd21b79fbb819c1c341fc825701c04a0b473 Mon Sep 17 00:00:00 2001 From: xuri Date: Wed, 22 Jan 2020 01:08:18 +0800 Subject: [PATCH] Resolve #455, init delete picture from spreadsheet support --- chart.go | 37 +------------------------------------ chart_test.go | 6 ------ drawing.go | 43 +++++++++++++++++++++++++++++++++++++++++++ picture.go | 21 +++++++++++++++++++++ picture_test.go | 15 +++++++++++++++ 5 files changed, 80 insertions(+), 42 deletions(-) diff --git a/chart.go b/chart.go index 2629f0b9..227cdee6 100644 --- a/chart.go +++ b/chart.go @@ -10,11 +10,8 @@ package excelize import ( - "bytes" "encoding/json" "errors" - "fmt" - "io" "strconv" "strings" ) @@ -766,7 +763,6 @@ func (f *File) AddChart(sheet, cell, format string, combo ...string) error { // DeleteChart provides a function to delete chart in XLSX by given worksheet // and cell name. func (f *File) DeleteChart(sheet, cell string) (err error) { - var wsDr *xlsxWsDr col, row, err := CellNameToCoordinates(cell) if err != nil { return @@ -781,38 +777,7 @@ func (f *File) DeleteChart(sheet, cell string) (err error) { return } drawingXML := strings.Replace(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl", -1) - wsDr, _ = f.drawingParser(drawingXML) - for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ { - if err = nil; wsDr.TwoCellAnchor[idx].From != nil && wsDr.TwoCellAnchor[idx].Pic == nil { - if wsDr.TwoCellAnchor[idx].From.Col == col && wsDr.TwoCellAnchor[idx].From.Row == row { - wsDr.TwoCellAnchor = append(wsDr.TwoCellAnchor[:idx], wsDr.TwoCellAnchor[idx+1:]...) - idx-- - } - } - } - return f.deleteChart(col, row, drawingXML, wsDr) -} - -// deleteChart provides a function to delete chart graphic frame by given by -// given coordinates. -func (f *File) deleteChart(col, row int, drawingXML string, wsDr *xlsxWsDr) (err error) { - var deTwoCellAnchor *decodeTwoCellAnchor - for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ { - deTwoCellAnchor = new(decodeTwoCellAnchor) - if err = f.xmlNewDecoder(bytes.NewReader([]byte("" + wsDr.TwoCellAnchor[idx].GraphicFrame + ""))). - Decode(deTwoCellAnchor); err != nil && err != io.EOF { - err = fmt.Errorf("xml decode error: %s", err) - return - } - if err = nil; deTwoCellAnchor.From != nil && deTwoCellAnchor.Pic == nil { - if deTwoCellAnchor.From.Col == col && deTwoCellAnchor.From.Row == row { - wsDr.TwoCellAnchor = append(wsDr.TwoCellAnchor[:idx], wsDr.TwoCellAnchor[idx+1:]...) - idx-- - } - } - } - f.Drawings[drawingXML] = wsDr - return err + return f.deleteDrawing(col, row, drawingXML, "Chart") } // countCharts provides a function to get chart files count storage in the diff --git a/chart_test.go b/chart_test.go index d8d36d85..98f3555a 100644 --- a/chart_test.go +++ b/chart_test.go @@ -217,12 +217,6 @@ func TestDeleteChart(t *testing.T) { assert.EqualError(t, f.DeleteChart("SheetN", "A1"), "sheet SheetN is not exist") // Test delete chart with invalid coordinates. assert.EqualError(t, f.DeleteChart("Sheet1", ""), `cannot convert cell "" to coordinates: invalid cell name ""`) - // Test delete chart with unsupport charset. - f, err = OpenFile(filepath.Join("test", "Book1.xlsx")) - assert.NoError(t, err) - delete(f.Sheet, "xl/drawings/drawing1.xml") - f.XLSX["xl/drawings/drawing1.xml"] = MacintoshCyrillicCharset - assert.EqualError(t, f.DeleteChart("Sheet1", "A1"), "xml decode error: XML syntax error on line 1: invalid UTF-8") // Test delete chart on no chart worksheet. assert.NoError(t, NewFile().DeleteChart("Sheet1", "A1")) } diff --git a/drawing.go b/drawing.go index 316897b6..e51b6afc 100644 --- a/drawing.go +++ b/drawing.go @@ -12,6 +12,7 @@ package excelize import ( "bytes" "encoding/xml" + "fmt" "io" "log" "reflect" @@ -1207,3 +1208,45 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI f.Drawings[drawingXML] = content return err } + +// deleteDrawing provides a function to delete chart graphic frame by given by +// given coordinates and graphic type. +func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (err error) { + var ( + wsDr *xlsxWsDr + deTwoCellAnchor *decodeTwoCellAnchor + ) + xdrCellAnchorFuncs := map[string]func(anchor *xdrCellAnchor) bool{ + "Chart": func(anchor *xdrCellAnchor) bool { return anchor.Pic == nil }, + "Pic": func(anchor *xdrCellAnchor) bool { return anchor.Pic != nil }, + } + decodeTwoCellAnchorFuncs := map[string]func(anchor *decodeTwoCellAnchor) bool{ + "Chart": func(anchor *decodeTwoCellAnchor) bool { return anchor.Pic == nil }, + "Pic": func(anchor *decodeTwoCellAnchor) bool { return anchor.Pic != nil }, + } + wsDr, _ = f.drawingParser(drawingXML) + for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ { + if err = nil; wsDr.TwoCellAnchor[idx].From != nil && xdrCellAnchorFuncs[drawingType](wsDr.TwoCellAnchor[idx]) { + if wsDr.TwoCellAnchor[idx].From.Col == col && wsDr.TwoCellAnchor[idx].From.Row == row { + wsDr.TwoCellAnchor = append(wsDr.TwoCellAnchor[:idx], wsDr.TwoCellAnchor[idx+1:]...) + idx-- + } + } + } + for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ { + deTwoCellAnchor = new(decodeTwoCellAnchor) + if err = f.xmlNewDecoder(bytes.NewReader([]byte("" + wsDr.TwoCellAnchor[idx].GraphicFrame + ""))). + Decode(deTwoCellAnchor); err != nil && err != io.EOF { + err = fmt.Errorf("xml decode error: %s", err) + return + } + if err = nil; deTwoCellAnchor.From != nil && decodeTwoCellAnchorFuncs[drawingType](deTwoCellAnchor) { + if deTwoCellAnchor.From.Col == col && deTwoCellAnchor.From.Row == row { + wsDr.TwoCellAnchor = append(wsDr.TwoCellAnchor[:idx], wsDr.TwoCellAnchor[idx+1:]...) + idx-- + } + } + } + f.Drawings[drawingXML] = wsDr + return err +} diff --git a/picture.go b/picture.go index 639cb662..213bae9f 100644 --- a/picture.go +++ b/picture.go @@ -462,6 +462,27 @@ 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 +// document currently. +func (f *File) DeletePicture(sheet, cell string) (err error) { + col, row, err := CellNameToCoordinates(cell) + if err != nil { + return + } + col-- + row-- + ws, err := f.workSheetReader(sheet) + if err != nil { + return + } + if ws.Drawing == nil { + return + } + drawingXML := strings.Replace(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl", -1) + return f.deleteDrawing(col, row, drawingXML, "Pic") +} + // getPicture provides a function to get picture base name and raw content // embed in XLSX by given coordinates and drawing relationships. func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) (ret string, buf []byte, err error) { diff --git a/picture_test.go b/picture_test.go index ca38f412..fdc6f0db 100644 --- a/picture_test.go +++ b/picture_test.go @@ -166,3 +166,18 @@ func TestAddPictureFromBytes(t *testing.T) { assert.Equal(t, 1, imageCount, "Duplicate image should only be stored once.") assert.EqualError(t, f.AddPictureFromBytes("SheetN", fmt.Sprint("A", 1), "", "logo", ".png", imgFile), "sheet SheetN is not exist") } + +func TestDeletePicture(t *testing.T) { + f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) + assert.NoError(t, err) + assert.NoError(t, f.DeletePicture("Sheet1", "A1")) + assert.NoError(t, f.AddPicture("Sheet1", "P1", filepath.Join("test", "images", "excel.jpg"), "")) + assert.NoError(t, f.DeletePicture("Sheet1", "P1")) + assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeletePicture.xlsx"))) + // Test delete picture on not exists worksheet. + assert.EqualError(t, f.DeletePicture("SheetN", "A1"), "sheet SheetN is not exist") + // Test delete picture with invalid coordinates. + assert.EqualError(t, f.DeletePicture("Sheet1", ""), `cannot convert cell "" to coordinates: invalid cell name ""`) + // Test delete picture on no chart worksheet. + assert.NoError(t, NewFile().DeletePicture("Sheet1", "A1")) +}