From a94dcb9918b5fec133faf5df65144d48e8722ca8 Mon Sep 17 00:00:00 2001
From: Michael <osiris2918@gmail.com>
Date: Sat, 23 Mar 2019 20:07:57 -0500
Subject: Do not save duplicate images

Adding the same image should create a drawing referencing the
already stored copy of the image.

Closes #359
---
 README.md       |  3 ++-
 picture.go      | 21 +++++++++++++++------
 picture_test.go | 18 ++++++++++++++++++
 3 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/README.md b/README.md
index 84ddde0..7774a50 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,8 @@
 
 ## Introduction
 
-Excelize is a library written in pure Go and providing a set of functions that allow you to write to and read from XLSX files. Support reads and writes XLSX file generated by Microsoft Excel&trade; 2007 and later. Support save file without losing original charts of XLSX. This library needs Go version 1.8 or later. The full API docs can be seen using go's built-in documentation tool, or online at [godoc.org](https://godoc.org/github.com/360EntSecGroup-Skylar/excelize) and [docs reference](https://xuri.me/excelize/).
+Excelize is a library written in pure Go providing a set of functions that allow you to write to and read from XLSX files. Supports reading and writing XLSX file generated by Microsoft Excel&trade; 2007 and later.
+Supports saving a file without losing original charts of XLSX. This library needs Go version 1.8 or later. The full API docs can be seen using go's built-in documentation tool, or online at [godoc.org](https://godoc.org/github.com/360EntSecGroup-Skylar/excelize) and [docs reference](https://xuri.me/excelize/).
 
 ## Basic Usage
 
diff --git a/picture.go b/picture.go
index 9a9ff09..16572d4 100644
--- a/picture.go
+++ b/picture.go
@@ -151,10 +151,10 @@ func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string,
 	xlsx := f.workSheetReader(sheet)
 	// Add first picture for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.
 	drawingID := f.countDrawings() + 1
-	pictureID := f.countMedia() + 1
 	drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
 	drawingID, drawingXML = f.prepareDrawing(xlsx, drawingID, sheet, drawingXML)
-	drawingRID := f.addDrawingRelationships(drawingID, SourceRelationshipImage, "../media/image"+strconv.Itoa(pictureID)+ext, hyperlinkType)
+	mediaStr := ".." + strings.TrimPrefix(f.addMedia(file, ext), "xl")
+	drawingRID := f.addDrawingRelationships(drawingID, SourceRelationshipImage, mediaStr, hyperlinkType)
 	// Add picture with hyperlink.
 	if formatSet.Hyperlink != "" && formatSet.HyperlinkType != "" {
 		if formatSet.HyperlinkType == "External" {
@@ -166,7 +166,6 @@ func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string,
 	if err != nil {
 		return err
 	}
-	f.addMedia(file, ext)
 	f.addContentTypePart(drawingID, "drawings")
 	return err
 }
@@ -363,12 +362,22 @@ func (f *File) countMedia() int {
 	return count
 }
 
-// addMedia provides a function to add picture into folder xl/media/image by
-// given file and extension name.
-func (f *File) addMedia(file []byte, ext string) {
+// addMedia provides a function to add a picture into folder xl/media/image by
+// given file and extension name. Duplicate images are only actually stored once
+// and drawings that use it will reference the same image.
+func (f *File) addMedia(file []byte, ext string) string {
 	count := f.countMedia()
+	for name, existing := range f.XLSX {
+		if !strings.HasPrefix(name, "xl/media/image") {
+			continue
+		}
+		if bytes.Equal(file, existing) {
+			return name
+		}
+	}
 	media := "xl/media/image" + strconv.Itoa(count+1) + ext
 	f.XLSX[media] = file
+	return media
 }
 
 // setContentTypePartImageExtensions provides a function to set the content
diff --git a/picture_test.go b/picture_test.go
index 518713f..2b39ed8 100644
--- a/picture_test.go
+++ b/picture_test.go
@@ -6,6 +6,7 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"strings"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -146,3 +147,20 @@ func TestAddDrawingPicture(t *testing.T) {
 	f := NewFile()
 	assert.EqualError(t, f.addDrawingPicture("sheet1", "", "A", "", 0, 0, 0, 0, nil), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
 }
+
+func TestAddPictureFromBytes(t *testing.T) {
+	f := NewFile()
+	imgFile, err := ioutil.ReadFile("logo.png")
+	if err != nil {
+		t.Error("Unable to load logo for test")
+	}
+	f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", 1), "", "logo", ".png", imgFile)
+	f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", 50), "", "logo", ".png", imgFile)
+	imageCount := 0
+	for fileName := range f.XLSX {
+		if strings.Contains(fileName, "media/image") {
+			imageCount++
+		}
+	}
+	assert.Equal(t, 1, imageCount, "Duplicate image should only be stored once.")
+}
-- 
cgit v1.2.1