Resolve #455, init delete picture from spreadsheet support

This commit is contained in:
xuri 2020-01-22 01:08:18 +08:00
parent e2bd08c911
commit cbc3fd21b7
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
5 changed files with 80 additions and 42 deletions

View File

@ -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("<decodeTwoCellAnchor>" + wsDr.TwoCellAnchor[idx].GraphicFrame + "</decodeTwoCellAnchor>"))).
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

View File

@ -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"))
}

View File

@ -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("<decodeTwoCellAnchor>" + wsDr.TwoCellAnchor[idx].GraphicFrame + "</decodeTwoCellAnchor>"))).
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
}

View File

@ -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) {

View File

@ -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"))
}