Skip to content

Commit caf22e4

Browse files
committed
This closes qax-os#2059, support delete one cell anchor image
- Fix delete wrong images in some case which caused by image reference detection issue - Update unit tests
1 parent 3f6ecff commit caf22e4

File tree

4 files changed

+101
-66
lines changed

4 files changed

+101
-66
lines changed

chart_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ func TestDeleteDrawing(t *testing.T) {
124124
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
125125
f, err = OpenFile(filepath.Join("test", "Book1.xlsx"))
126126
assert.NoError(t, err)
127+
f.Drawings.Store(path, &xlsxWsDr{OneCellAnchor: []*xdrCellAnchor{{
128+
GraphicFrame: string(MacintoshCyrillicCharset),
129+
}}})
130+
_, err = f.deleteDrawing(0, 0, path, "Chart")
131+
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
127132
f.Drawings.Store(path, &xlsxWsDr{TwoCellAnchor: []*xdrCellAnchor{{
128133
GraphicFrame: string(MacintoshCyrillicCharset),
129134
}}})

drawing.go

Lines changed: 58 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,13 +1484,14 @@ func (f *File) addSheetDrawingChart(drawingXML string, rID int, opts *GraphicOpt
14841484
// deleteDrawing provides a function to delete the chart graphic frame and
14851485
// returns deleted embed relationships ID (for unique picture cell anchor) by
14861486
// given coordinates and graphic type.
1487-
func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (string, error) {
1487+
func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) ([]string, error) {
14881488
var (
1489-
err error
1490-
rID string
1491-
rIDs []string
1492-
wsDr *xlsxWsDr
1493-
deTwoCellAnchor *decodeCellAnchor
1489+
err error
1490+
rID string
1491+
delRID, refRID []string
1492+
rIDMaps = map[string]int{}
1493+
wsDr *xlsxWsDr
1494+
deCellAnchor *decodeCellAnchor
14941495
)
14951496
xdrCellAnchorFuncs := map[string]func(anchor *xdrCellAnchor) bool{
14961497
"Chart": func(anchor *xdrCellAnchor) bool { return anchor.Pic == nil },
@@ -1502,54 +1503,70 @@ func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (stri
15021503
}
15031504
onAnchorCell := func(c, r int) bool { return c == col && r == row }
15041505
if wsDr, _, err = f.drawingParser(drawingXML); err != nil {
1505-
return rID, err
1506-
}
1507-
for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ {
1508-
if err = nil; wsDr.TwoCellAnchor[idx].From != nil && xdrCellAnchorFuncs[drawingType](wsDr.TwoCellAnchor[idx]) {
1509-
if onAnchorCell(wsDr.TwoCellAnchor[idx].From.Col, wsDr.TwoCellAnchor[idx].From.Row) {
1510-
rID, _ = extractEmbedRID(wsDr.TwoCellAnchor[idx].Pic, nil, rIDs)
1511-
wsDr.TwoCellAnchor = append(wsDr.TwoCellAnchor[:idx], wsDr.TwoCellAnchor[idx+1:]...)
1512-
idx--
1506+
return delRID, err
1507+
}
1508+
deleteCellAnchor := func(ca []*xdrCellAnchor) ([]*xdrCellAnchor, error) {
1509+
for idx := 0; idx < len(ca); idx++ {
1510+
if err = nil; ca[idx].From != nil && xdrCellAnchorFuncs[drawingType](ca[idx]) {
1511+
rID = extractEmbedRID(ca[idx].Pic, nil)
1512+
rIDMaps[rID]++
1513+
if onAnchorCell(ca[idx].From.Col, ca[idx].From.Row) {
1514+
refRID = append(refRID, rID)
1515+
ca = append(ca[:idx], ca[idx+1:]...)
1516+
idx--
1517+
rIDMaps[rID]--
1518+
}
15131519
continue
15141520
}
1515-
_, rIDs = extractEmbedRID(wsDr.TwoCellAnchor[idx].Pic, nil, rIDs)
1516-
}
1517-
}
1518-
for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ {
1519-
deTwoCellAnchor = new(decodeCellAnchor)
1520-
if err = f.xmlNewDecoder(strings.NewReader("<decodeCellAnchor>" + wsDr.TwoCellAnchor[idx].GraphicFrame + "</decodeCellAnchor>")).
1521-
Decode(deTwoCellAnchor); err != nil && err != io.EOF {
1522-
return rID, err
1523-
}
1524-
if err = nil; deTwoCellAnchor.From != nil && decodeCellAnchorFuncs[drawingType](deTwoCellAnchor) {
1525-
if onAnchorCell(deTwoCellAnchor.From.Col, deTwoCellAnchor.From.Row) {
1526-
rID, _ = extractEmbedRID(nil, deTwoCellAnchor.Pic, rIDs)
1527-
wsDr.TwoCellAnchor = append(wsDr.TwoCellAnchor[:idx], wsDr.TwoCellAnchor[idx+1:]...)
1528-
idx--
1529-
continue
1521+
deCellAnchor = new(decodeCellAnchor)
1522+
if err = f.xmlNewDecoder(strings.NewReader("<decodeCellAnchor>" + ca[idx].GraphicFrame + "</decodeCellAnchor>")).
1523+
Decode(deCellAnchor); err != nil && err != io.EOF {
1524+
return ca, err
1525+
}
1526+
if err = nil; deCellAnchor.From != nil && decodeCellAnchorFuncs[drawingType](deCellAnchor) {
1527+
rID = extractEmbedRID(nil, deCellAnchor.Pic)
1528+
rIDMaps[rID]++
1529+
if onAnchorCell(deCellAnchor.From.Col, deCellAnchor.From.Row) {
1530+
refRID = append(refRID, rID)
1531+
ca = append(ca[:idx], ca[idx+1:]...)
1532+
idx--
1533+
rIDMaps[rID]--
1534+
}
15301535
}
1531-
_, rIDs = extractEmbedRID(nil, deTwoCellAnchor.Pic, rIDs)
15321536
}
1537+
return ca, err
15331538
}
1534-
if inStrSlice(rIDs, rID, true) != -1 {
1535-
rID = ""
1539+
if wsDr.OneCellAnchor, err = deleteCellAnchor(wsDr.OneCellAnchor); err != nil {
1540+
return delRID, err
1541+
}
1542+
if wsDr.TwoCellAnchor, err = deleteCellAnchor(wsDr.TwoCellAnchor); err != nil {
1543+
return delRID, err
15361544
}
15371545
f.Drawings.Store(drawingXML, wsDr)
1538-
return rID, err
1546+
return getUnusedCellAnchorRID(delRID, refRID, rIDMaps), err
15391547
}
15401548

1541-
// extractEmbedRID returns embed relationship ID and all relationship ID lists
1542-
// for giving cell anchor.
1543-
func extractEmbedRID(pic *xlsxPic, decodePic *decodePic, rIDs []string) (string, []string) {
1549+
// extractEmbedRID returns embed relationship ID by giving cell anchor.
1550+
func extractEmbedRID(pic *xlsxPic, decodePic *decodePic) string {
1551+
var rID string
15441552
if pic != nil {
1545-
rIDs = append(rIDs, pic.BlipFill.Blip.Embed)
1546-
return pic.BlipFill.Blip.Embed, rIDs
1553+
rID = pic.BlipFill.Blip.Embed
15471554
}
15481555
if decodePic != nil {
1549-
rIDs = append(rIDs, decodePic.BlipFill.Blip.Embed)
1550-
return decodePic.BlipFill.Blip.Embed, rIDs
1556+
rID = decodePic.BlipFill.Blip.Embed
1557+
}
1558+
return rID
1559+
}
1560+
1561+
// getUnusedCellAnchorRID returns relationship ID lists in the cell anchor which
1562+
// for remove.
1563+
func getUnusedCellAnchorRID(delRID, refRID []string, rIDMaps map[string]int) []string {
1564+
for _, rID := range refRID {
1565+
if rIDMaps[rID] == 0 && inStrSlice(delRID, rID, false) == -1 {
1566+
delRID = append(delRID, rID)
1567+
}
15511568
}
1552-
return "", rIDs
1569+
return delRID
15531570
}
15541571

15551572
// deleteDrawingRels provides a function to delete relationships in

picture.go

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -566,36 +566,41 @@ func (f *File) DeletePicture(sheet, cell string) error {
566566
}
567567
drawingXML := strings.ReplaceAll(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl")
568568
drawingRels := "xl/drawings/_rels/" + filepath.Base(drawingXML) + ".rels"
569-
rID, err := f.deleteDrawing(col, row, drawingXML, "Pic")
569+
rIDs, err := f.deleteDrawing(col, row, drawingXML, "Pic")
570570
if err != nil {
571571
return err
572572
}
573-
rels := f.getDrawingRelationships(drawingRels, rID)
574-
if rels == nil {
575-
return err
576-
}
577-
var used bool
578-
checkPicRef := func(k, v interface{}) bool {
579-
if strings.Contains(k.(string), "xl/drawings/_rels/drawing") {
580-
r, err := f.relsReader(k.(string))
581-
if err != nil {
582-
return true
583-
}
584-
for _, rel := range r.Relationships {
585-
if rel.ID != rels.ID && rel.Type == SourceRelationshipImage &&
586-
filepath.Base(rel.Target) == filepath.Base(rels.Target) {
587-
used = true
573+
for _, rID := range rIDs {
574+
rels := f.getDrawingRelationships(drawingRels, rID)
575+
if rels == nil {
576+
return err
577+
}
578+
var used bool
579+
checkPicRef := func(k, v interface{}) bool {
580+
if strings.Contains(k.(string), "xl/drawings/_rels/drawing") {
581+
if k.(string) == drawingRels {
582+
return true
583+
}
584+
r, err := f.relsReader(k.(string))
585+
if err != nil {
586+
return true
587+
}
588+
for _, rel := range r.Relationships {
589+
if rel.Type == SourceRelationshipImage &&
590+
filepath.Base(rel.Target) == filepath.Base(rels.Target) {
591+
used = true
592+
}
588593
}
589594
}
595+
return true
590596
}
591-
return true
592-
}
593-
f.Relationships.Range(checkPicRef)
594-
f.Pkg.Range(checkPicRef)
595-
if !used {
596-
f.Pkg.Delete(strings.Replace(rels.Target, "../", "xl/", -1))
597+
f.Relationships.Range(checkPicRef)
598+
f.Pkg.Range(checkPicRef)
599+
if !used {
600+
f.Pkg.Delete(strings.Replace(rels.Target, "../", "xl/", -1))
601+
}
602+
f.deleteDrawingRels(drawingRels, rID)
597603
}
598-
f.deleteDrawingRels(drawingRels, rID)
599604
return err
600605
}
601606

picture_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,9 @@ func TestDeletePicture(t *testing.T) {
334334
f, err = OpenFile(filepath.Join("test", "TestDeletePicture.xlsx"))
335335
assert.NoError(t, err)
336336
// Test delete same picture on different worksheet, the images should be removed
337-
assert.NoError(t, f.DeletePicture("Sheet1", "F10"))
338-
assert.NoError(t, f.DeletePicture("Sheet2", "F1"))
337+
assert.NoError(t, f.DeletePicture("Sheet1", "F20"))
339338
assert.NoError(t, f.DeletePicture("Sheet1", "I20"))
339+
assert.NoError(t, f.DeletePicture("Sheet2", "F1"))
340340
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeletePicture2.xlsx")))
341341

342342
// Test delete picture on not exists worksheet
@@ -364,6 +364,14 @@ func TestDeletePicture(t *testing.T) {
364364
assert.NoError(t, f.DeletePicture("Sheet2", "F1"))
365365
assert.NoError(t, f.Close())
366366

367+
f, err = OpenFile(filepath.Join("test", "TestDeletePicture.xlsx"))
368+
assert.NoError(t, err)
369+
// Test delete picture without drawing relationships
370+
f.Relationships.Delete("xl/drawings/_rels/drawing1.xml.rels")
371+
f.Pkg.Delete("xl/drawings/_rels/drawing1.xml.rels")
372+
assert.NoError(t, f.DeletePicture("Sheet1", "I20"))
373+
assert.NoError(t, f.Close())
374+
367375
f = NewFile()
368376
assert.NoError(t, err)
369377
assert.NoError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.jpg"), nil))

0 commit comments

Comments
 (0)