Browse Source

Resolve #455, init delete picture from spreadsheet support

xuri 5 years ago
parent
commit
cbc3fd21b7
5 changed files with 80 additions and 42 deletions
  1. 1 36
      chart.go
  2. 0 6
      chart_test.go
  3. 43 0
      drawing.go
  4. 21 0
      picture.go
  5. 15 0
      picture_test.go

+ 1 - 36
chart.go

@@ -10,11 +10,8 @@
 package excelize
 package excelize
 
 
 import (
 import (
-	"bytes"
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
-	"fmt"
-	"io"
 	"strconv"
 	"strconv"
 	"strings"
 	"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
 // DeleteChart provides a function to delete chart in XLSX by given worksheet
 // and cell name.
 // and cell name.
 func (f *File) DeleteChart(sheet, cell string) (err error) {
 func (f *File) DeleteChart(sheet, cell string) (err error) {
-	var wsDr *xlsxWsDr
 	col, row, err := CellNameToCoordinates(cell)
 	col, row, err := CellNameToCoordinates(cell)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -781,38 +777,7 @@ func (f *File) DeleteChart(sheet, cell string) (err error) {
 		return
 		return
 	}
 	}
 	drawingXML := strings.Replace(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl", -1)
 	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
 // countCharts provides a function to get chart files count storage in the

+ 0 - 6
chart_test.go

@@ -217,12 +217,6 @@ func TestDeleteChart(t *testing.T) {
 	assert.EqualError(t, f.DeleteChart("SheetN", "A1"), "sheet SheetN is not exist")
 	assert.EqualError(t, f.DeleteChart("SheetN", "A1"), "sheet SheetN is not exist")
 	// Test delete chart with invalid coordinates.
 	// Test delete chart with invalid coordinates.
 	assert.EqualError(t, f.DeleteChart("Sheet1", ""), `cannot convert cell "" to coordinates: invalid cell name ""`)
 	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.
 	// Test delete chart on no chart worksheet.
 	assert.NoError(t, NewFile().DeleteChart("Sheet1", "A1"))
 	assert.NoError(t, NewFile().DeleteChart("Sheet1", "A1"))
 }
 }

+ 43 - 0
drawing.go

@@ -12,6 +12,7 @@ package excelize
 import (
 import (
 	"bytes"
 	"bytes"
 	"encoding/xml"
 	"encoding/xml"
+	"fmt"
 	"io"
 	"io"
 	"log"
 	"log"
 	"reflect"
 	"reflect"
@@ -1207,3 +1208,45 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
 	f.Drawings[drawingXML] = content
 	f.Drawings[drawingXML] = content
 	return err
 	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
+}

+ 21 - 0
picture.go

@@ -462,6 +462,27 @@ func (f *File) GetPicture(sheet, cell string) (string, []byte, error) {
 	return f.getPicture(row, col, drawingXML, drawingRelationships)
 	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
 // getPicture provides a function to get picture base name and raw content
 // embed in XLSX by given coordinates and drawing relationships.
 // 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) {
 func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) (ret string, buf []byte, err error) {

+ 15 - 0
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.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")
 	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"))
+}