diff --git a/excelize.go b/excelize.go
index 8b1b7634d6..e0959aa646 100644
--- a/excelize.go
+++ b/excelize.go
@@ -605,7 +605,7 @@ func (f *File) metadataReader() (*xlsxMetadata, error) {
// deserialization of xl/richData/richvalue.xml.
func (f *File) richValueReader() (*xlsxRichValueData, error) {
var richValue xlsxRichValueData
- if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLRichDataRichValue)))).
+ if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLRdRichValuePart)))).
Decode(&richValue); err != nil && err != io.EOF {
return &richValue, err
}
@@ -616,18 +616,43 @@ func (f *File) richValueReader() (*xlsxRichValueData, error) {
// after deserialization of xl/richData/richValueRel.xml.
func (f *File) richValueRelReader() (*xlsxRichValueRels, error) {
var richValueRels xlsxRichValueRels
- if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLRichDataRichValueRel)))).
+ if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLRdRichValueRel)))).
Decode(&richValueRels); err != nil && err != io.EOF {
return &richValueRels, err
}
return &richValueRels, nil
}
-// getRichDataRichValueRelRelationships provides a function to get drawing
-// relationships from xl/richData/_rels/richValueRel.xml.rels by given
-// relationship ID.
+// richValueWebImageReader provides a function to get the pointer to the
+// structure after deserialization of xl/richData/rdRichValueWebImage.xml.
+func (f *File) richValueWebImageReader() (*xlsxWebImagesSupportingRichData, error) {
+ var richValueWebImages xlsxWebImagesSupportingRichData
+ if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLRdRichValueWebImagePart)))).
+ Decode(&richValueWebImages); err != nil && err != io.EOF {
+ return &richValueWebImages, err
+ }
+ return &richValueWebImages, nil
+}
+
+// getRichDataRichValueRelRelationships provides a function to get relationships
+// from xl/richData/_rels/richValueRel.xml.rels by given relationship ID.
func (f *File) getRichDataRichValueRelRelationships(rID string) *xlsxRelationship {
- if rels, _ := f.relsReader(defaultXMLRichDataRichValueRelRels); rels != nil {
+ if rels, _ := f.relsReader(defaultXMLRdRichValueRelRels); rels != nil {
+ rels.mu.Lock()
+ defer rels.mu.Unlock()
+ for _, v := range rels.Relationships {
+ if v.ID == rID {
+ return &v
+ }
+ }
+ }
+ return nil
+}
+
+// getRichValueWebImageRelationships provides a function to get relationships
+// from xl/richData/_rels/rdRichValueWebImage.xml.rels by given relationship ID.
+func (f *File) getRichValueWebImageRelationships(rID string) *xlsxRelationship {
+ if rels, _ := f.relsReader(defaultXMLRdRichValueWebImagePartRels); rels != nil {
rels.mu.Lock()
defer rels.mu.Unlock()
for _, v := range rels.Relationships {
diff --git a/picture.go b/picture.go
index cc0c2b8c9b..27d6b7fe8c 100644
--- a/picture.go
+++ b/picture.go
@@ -31,6 +31,7 @@ type PictureInsertType int
const (
PictureInsertTypePlaceOverCells PictureInsertType = iota
PictureInsertTypePlaceInCell
+ PictureInsertTypeIMAGE
PictureInsertTypeDISPIMG
)
@@ -450,8 +451,7 @@ func (f *File) addMedia(file []byte, ext string) string {
// GetPictures provides a function to get picture meta info and raw content
// embed in spreadsheet by given worksheet and cell name. This function
// returns the image contents as []byte data types. This function is
-// concurrency safe. Note that, this function doesn't support getting cell image
-// inserted by IMAGE formula function currently. For example:
+// concurrency safe. For example:
//
// f, err := excelize.OpenFile("Book1.xlsx")
// if err != nil {
@@ -507,8 +507,7 @@ func (f *File) GetPictures(sheet, cell string) ([]Picture, error) {
}
// GetPictureCells returns all picture cell references in a worksheet by a
-// specific worksheet name. Note that, this function doesn't support getting
-// cell image inserted by IMAGE formula function currently.
+// specific worksheet name.
func (f *File) GetPictureCells(sheet string) ([]string, error) {
f.mu.Lock()
ws, err := f.workSheetReader(sheet)
@@ -812,7 +811,7 @@ func (f *File) getImageCells(sheet string) ([]string, error) {
}
cells = append(cells, c.R)
}
- r, err := f.getImageCellRel(&c)
+ r, err := f.getImageCellRel(&c, &Picture{})
if err != nil {
return cells, err
}
@@ -825,30 +824,52 @@ func (f *File) getImageCells(sheet string) ([]string, error) {
return cells, err
}
-// getImageCellRichValueIdx returns index of the cell image rich value by given
-// cell value meta index and meta blocks.
-func (f *File) getImageCellRichValueIdx(vm uint, blocks *xlsxMetadataBlocks) (int, error) {
- richValueIdx := blocks.Bk[vm-1].Rc[0].V
- richValue, err := f.richValueReader()
+// getRichDataRichValueRel returns relationship of the cell image by given meta
+// blocks value.
+func (f *File) getRichDataRichValueRel(val string) (*xlsxRelationship, error) {
+ var r *xlsxRelationship
+ idx, err := strconv.Atoi(val)
if err != nil {
- return -1, err
+ return r, err
}
- if richValueIdx >= len(richValue.Rv) {
- return -1, err
+ richValueRel, err := f.richValueRelReader()
+ if err != nil {
+ return r, err
}
- rv := richValue.Rv[richValueIdx].V
- if len(rv) != 2 || rv[1] != "5" {
- return -1, err
+ if idx >= len(richValueRel.Rels) {
+ return r, err
+ }
+ rID := richValueRel.Rels[idx].ID
+ if r = f.getRichDataRichValueRelRelationships(rID); r != nil && r.Type != SourceRelationshipImage {
+ return nil, err
+ }
+ return r, err
+}
+
+// getRichDataWebImagesRel returns relationship of a web image by given meta
+// blocks value.
+func (f *File) getRichDataWebImagesRel(val string) (*xlsxRelationship, error) {
+ var r *xlsxRelationship
+ idx, err := strconv.Atoi(val)
+ if err != nil {
+ return r, err
}
- richValueRelIdx, err := strconv.Atoi(rv[0])
+ richValueWebImages, err := f.richValueWebImageReader()
if err != nil {
- return -1, err
+ return r, err
+ }
+ if idx >= len(richValueWebImages.WebImageSrd) {
+ return r, err
}
- return richValueRelIdx, err
+ rID := richValueWebImages.WebImageSrd[idx].Blip.RID
+ if r = f.getRichValueWebImageRelationships(rID); r != nil && r.Type != SourceRelationshipImage {
+ return nil, err
+ }
+ return r, err
}
// getImageCellRel returns the cell image relationship.
-func (f *File) getImageCellRel(c *xlsxC) (*xlsxRelationship, error) {
+func (f *File) getImageCellRel(c *xlsxC, pic *Picture) (*xlsxRelationship, error) {
var r *xlsxRelationship
if c.Vm == nil || c.V != formulaErrorVALUE {
return r, nil
@@ -861,20 +882,23 @@ func (f *File) getImageCellRel(c *xlsxC) (*xlsxRelationship, error) {
if vmd == nil || int(*c.Vm) > len(vmd.Bk) || len(vmd.Bk[*c.Vm-1].Rc) == 0 {
return r, err
}
- richValueRelIdx, err := f.getImageCellRichValueIdx(*c.Vm, vmd)
- if err != nil || richValueRelIdx == -1 {
- return r, err
- }
- richValueRel, err := f.richValueRelReader()
+ richValueIdx := vmd.Bk[*c.Vm-1].Rc[0].V
+ richValue, err := f.richValueReader()
if err != nil {
return r, err
}
- if richValueRelIdx >= len(richValueRel.Rels) {
+ if richValueIdx >= len(richValue.Rv) {
return r, err
}
- rID := richValueRel.Rels[richValueRelIdx].ID
- if r = f.getRichDataRichValueRelRelationships(rID); r != nil && r.Type != SourceRelationshipImage {
- return nil, err
+ rv := richValue.Rv[richValueIdx].V
+ if len(rv) == 2 && rv[1] == "5" {
+ pic.InsertType = PictureInsertTypePlaceInCell
+ return f.getRichDataRichValueRel(rv[0])
+ }
+ // cell image inserted by IMAGE formula function
+ if len(rv) > 3 && rv[1]+rv[2] == "10" {
+ pic.InsertType = PictureInsertTypeIMAGE
+ return f.getRichDataWebImagesRel(rv[0])
}
return r, err
}
@@ -888,11 +912,12 @@ func (f *File) getCellImages(sheet, cell string) ([]Picture, error) {
return pics, err
}
_, err = f.getCellStringFunc(sheet, cell, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {
- r, err := f.getImageCellRel(c)
+ pic := Picture{Format: &GraphicOptions{}, InsertType: PictureInsertTypePlaceInCell}
+ r, err := f.getImageCellRel(c, &pic)
if err != nil || r == nil {
return "", true, err
}
- pic := Picture{Extension: filepath.Ext(r.Target), Format: &GraphicOptions{}, InsertType: PictureInsertTypePlaceInCell}
+ pic.Extension = filepath.Ext(r.Target)
if buffer, _ := f.Pkg.Load(strings.TrimPrefix(strings.ReplaceAll(r.Target, "..", "xl"), "/")); buffer != nil {
pic.File = buffer.([]byte)
pics = append(pics, pic)
diff --git a/picture_test.go b/picture_test.go
index bb906345e8..23054c1da1 100644
--- a/picture_test.go
+++ b/picture_test.go
@@ -246,7 +246,7 @@ func TestGetPicture(t *testing.T) {
assert.NoError(t, err)
assert.NoError(t, f.SetCellFormula("Sheet1", "F21", "=_xlfn.DISPIMG(\"ID_********************************\",1)"))
f.Pkg.Store(defaultXMLPathCellImages, []byte(``))
- f.Pkg.Store(defaultXMLPathCellImagesRels, []byte(``))
+ f.Pkg.Store(defaultXMLPathCellImagesRels, []byte(fmt.Sprintf(``, SourceRelationshipImage)))
pics, err = f.GetPictures("Sheet1", "F21")
assert.NoError(t, err)
assert.Len(t, pics, 2)
@@ -457,9 +457,9 @@ func TestGetCellImages(t *testing.T) {
f := NewFile()
assert.NoError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.png"), nil))
f.Pkg.Store(defaultXMLMetadata, []byte(``))
- f.Pkg.Store(defaultXMLRichDataRichValue, []byte(`05`))
- f.Pkg.Store(defaultXMLRichDataRichValueRel, []byte(``))
- f.Pkg.Store(defaultXMLRichDataRichValueRelRels, []byte(fmt.Sprintf(``, SourceRelationshipImage)))
+ f.Pkg.Store(defaultXMLRdRichValuePart, []byte(`05`))
+ f.Pkg.Store(defaultXMLRdRichValueRel, []byte(``))
+ f.Pkg.Store(defaultXMLRdRichValueRelRels, []byte(fmt.Sprintf(``, SourceRelationshipImage)))
f.Sheet.Store("xl/worksheets/sheet1.xml", &xlsxWorksheet{
SheetData: xlsxSheetData{Row: []xlsxRow{
{R: 1, C: []xlsxC{{R: "A1", T: "e", V: formulaErrorVALUE, Vm: uintPtr(1)}}},
@@ -477,19 +477,19 @@ func TestGetCellImages(t *testing.T) {
assert.Equal(t, []string{"A1"}, cells)
// Test get the cell images without image relationships parts
- f.Relationships.Delete(defaultXMLRichDataRichValueRelRels)
- f.Pkg.Store(defaultXMLRichDataRichValueRelRels, []byte(fmt.Sprintf(``, SourceRelationshipHyperLink)))
+ f.Relationships.Delete(defaultXMLRdRichValueRelRels)
+ f.Pkg.Store(defaultXMLRdRichValueRelRels, []byte(fmt.Sprintf(``, SourceRelationshipHyperLink)))
pics, err = f.GetPictures("Sheet1", "A1")
assert.NoError(t, err)
assert.Empty(t, pics)
// Test get the cell images with unsupported charset rich data rich value relationships
- f.Relationships.Delete(defaultXMLRichDataRichValueRelRels)
- f.Pkg.Store(defaultXMLRichDataRichValueRelRels, MacintoshCyrillicCharset)
+ f.Relationships.Delete(defaultXMLRdRichValueRelRels)
+ f.Pkg.Store(defaultXMLRdRichValueRelRels, MacintoshCyrillicCharset)
pics, err = f.GetPictures("Sheet1", "A1")
assert.NoError(t, err)
assert.Empty(t, pics)
// Test get the cell images with unsupported charset rich data rich value
- f.Pkg.Store(defaultXMLRichDataRichValueRel, MacintoshCyrillicCharset)
+ f.Pkg.Store(defaultXMLRdRichValueRel, MacintoshCyrillicCharset)
_, err = f.GetPictures("Sheet1", "A1")
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
// Test get the image cells without block of metadata records
@@ -498,7 +498,7 @@ func TestGetCellImages(t *testing.T) {
assert.Empty(t, cells)
// Test get the cell images with rich data rich value relationships
f.Pkg.Store(defaultXMLMetadata, []byte(``))
- f.Pkg.Store(defaultXMLRichDataRichValueRel, []byte(``))
+ f.Pkg.Store(defaultXMLRdRichValueRel, []byte(``))
pics, err = f.GetPictures("Sheet1", "A1")
assert.NoError(t, err)
assert.Empty(t, pics)
@@ -514,17 +514,17 @@ func TestGetCellImages(t *testing.T) {
f = prepareWorkbook()
// Test get the cell images with empty image cell rich value
- f.Pkg.Store(defaultXMLRichDataRichValue, []byte(`5`))
+ f.Pkg.Store(defaultXMLRdRichValuePart, []byte(`5`))
pics, err = f.GetPictures("Sheet1", "A1")
assert.EqualError(t, err, "strconv.Atoi: parsing \"\": invalid syntax")
assert.Empty(t, pics)
// Test get the cell images without image cell rich value
- f.Pkg.Store(defaultXMLRichDataRichValue, []byte(`01`))
+ f.Pkg.Store(defaultXMLRdRichValuePart, []byte(`01`))
pics, err = f.GetPictures("Sheet1", "A1")
assert.NoError(t, err)
assert.Empty(t, pics)
// Test get the cell images with unsupported charset rich value
- f.Pkg.Store(defaultXMLRichDataRichValue, MacintoshCyrillicCharset)
+ f.Pkg.Store(defaultXMLRdRichValuePart, MacintoshCyrillicCharset)
_, err = f.GetPictures("Sheet1", "A1")
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
@@ -534,6 +534,44 @@ func TestGetCellImages(t *testing.T) {
pics, err = f.GetPictures("Sheet1", "A1")
assert.NoError(t, err)
assert.Empty(t, pics)
+
+ f = prepareWorkbook()
+ // Test get the cell images inserted by IMAGE formula function
+ f.Pkg.Store(defaultXMLRdRichValuePart, []byte(`0100`))
+ f.Pkg.Store(defaultXMLRdRichValueWebImagePart, []byte(`
+ `))
+ f.Pkg.Store(defaultXMLRdRichValueWebImagePartRels, []byte(fmt.Sprintf(``, SourceRelationshipHyperLink, SourceRelationshipImage)))
+ pics, err = f.GetPictures("Sheet1", "A1")
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(pics))
+ assert.Equal(t, PictureInsertTypeIMAGE, pics[0].InsertType)
+
+ // Test get the cell images inserted by IMAGE formula function with unsupported charset web images relationships
+ f.Relationships.Delete(defaultXMLRdRichValueWebImagePartRels)
+ f.Pkg.Store(defaultXMLRdRichValueWebImagePartRels, MacintoshCyrillicCharset)
+ pics, err = f.GetPictures("Sheet1", "A1")
+ assert.NoError(t, err)
+ assert.Empty(t, pics)
+
+ // Test get the cell images inserted by IMAGE formula function without image part
+ f.Relationships.Delete(defaultXMLRdRichValueWebImagePartRels)
+ f.Pkg.Store(defaultXMLRdRichValueWebImagePartRels, []byte(fmt.Sprintf(``, SourceRelationshipHyperLink, SourceRelationshipHyperLink)))
+ pics, err = f.GetPictures("Sheet1", "A1")
+ assert.NoError(t, err)
+ assert.Empty(t, pics)
+ // Test get the cell images inserted by IMAGE formula function with unsupported charset web images part
+ f.Pkg.Store(defaultXMLRdRichValueWebImagePart, MacintoshCyrillicCharset)
+ _, err = f.GetPictures("Sheet1", "A1")
+ assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
+ // Test get the cell images inserted by IMAGE formula function with empty charset web images part
+ f.Pkg.Store(defaultXMLRdRichValueWebImagePart, []byte(``))
+ pics, err = f.GetPictures("Sheet1", "A1")
+ assert.NoError(t, err)
+ assert.Empty(t, pics)
+ // Test get the cell images inserted by IMAGE formula function with invalid rich value index
+ f.Pkg.Store(defaultXMLRdRichValuePart, []byte(`100`))
+ _, err = f.GetPictures("Sheet1", "A1")
+ assert.EqualError(t, err, "strconv.Atoi: parsing \"\": invalid syntax")
}
func TestGetImageCells(t *testing.T) {
diff --git a/templates.go b/templates.go
index 7d72e3b0b1..3bf4c5fe69 100644
--- a/templates.go
+++ b/templates.go
@@ -266,23 +266,25 @@ var supportedChartDataLabelsPosition = map[ChartType][]ChartDataLabelPositionTyp
}
const (
- defaultTempFileSST = "sharedStrings"
- defaultXMLMetadata = "xl/metadata.xml"
- defaultXMLPathCalcChain = "xl/calcChain.xml"
- defaultXMLPathCellImages = "xl/cellimages.xml"
- defaultXMLPathCellImagesRels = "xl/_rels/cellimages.xml.rels"
- defaultXMLPathContentTypes = "[Content_Types].xml"
- defaultXMLPathDocPropsApp = "docProps/app.xml"
- defaultXMLPathDocPropsCore = "docProps/core.xml"
- defaultXMLPathSharedStrings = "xl/sharedStrings.xml"
- defaultXMLPathStyles = "xl/styles.xml"
- defaultXMLPathTheme = "xl/theme/theme1.xml"
- defaultXMLPathVolatileDeps = "xl/volatileDependencies.xml"
- defaultXMLPathWorkbook = "xl/workbook.xml"
- defaultXMLPathWorkbookRels = "xl/_rels/workbook.xml.rels"
- defaultXMLRichDataRichValue = "xl/richData/rdrichvalue.xml"
- defaultXMLRichDataRichValueRel = "xl/richData/richValueRel.xml"
- defaultXMLRichDataRichValueRelRels = "xl/richData/_rels/richValueRel.xml.rels"
+ defaultTempFileSST = "sharedStrings"
+ defaultXMLMetadata = "xl/metadata.xml"
+ defaultXMLPathCalcChain = "xl/calcChain.xml"
+ defaultXMLPathCellImages = "xl/cellimages.xml"
+ defaultXMLPathCellImagesRels = "xl/_rels/cellimages.xml.rels"
+ defaultXMLPathContentTypes = "[Content_Types].xml"
+ defaultXMLPathDocPropsApp = "docProps/app.xml"
+ defaultXMLPathDocPropsCore = "docProps/core.xml"
+ defaultXMLPathSharedStrings = "xl/sharedStrings.xml"
+ defaultXMLPathStyles = "xl/styles.xml"
+ defaultXMLPathTheme = "xl/theme/theme1.xml"
+ defaultXMLPathVolatileDeps = "xl/volatileDependencies.xml"
+ defaultXMLPathWorkbook = "xl/workbook.xml"
+ defaultXMLPathWorkbookRels = "xl/_rels/workbook.xml.rels"
+ defaultXMLRdRichValuePart = "xl/richData/rdrichvalue.xml"
+ defaultXMLRdRichValueRel = "xl/richData/richValueRel.xml"
+ defaultXMLRdRichValueRelRels = "xl/richData/_rels/richValueRel.xml.rels"
+ defaultXMLRdRichValueWebImagePart = "xl/richData/rdRichValueWebImage.xml"
+ defaultXMLRdRichValueWebImagePartRels = "xl/richData/_rels/rdRichValueWebImage.xml.rels"
)
// IndexedColorMapping is the table of default mappings from indexed color value
diff --git a/xmlMetaData.go b/xmlMetaData.go
index b3f97b6a3f..016e348674 100644
--- a/xmlMetaData.go
+++ b/xmlMetaData.go
@@ -98,3 +98,20 @@ type xlsxRichValueRels struct {
type xlsxRichValueRelRelationship struct {
ID string `xml:"id,attr"`
}
+
+// xlsxWebImagesSupportingRichData directly maps the webImagesSrd element. This
+// element specifies a list of sets of properties associated with web image rich
+// values.
+type xlsxWebImagesSupportingRichData struct {
+ XMLName xml.Name `xml:"webImagesSrd"`
+ WebImageSrd []xlsxWebImageSupportingRichData `xml:"webImageSrd"`
+ ExtLst *xlsxInnerXML `xml:"extLst"`
+}
+
+// xlsxWebImageSupportingRichData directly maps the webImageSrd element. This
+// element specifies a set of properties for a web image rich value.
+type xlsxWebImageSupportingRichData struct {
+ Address xlsxExternalReference `xml:"address"`
+ MoreImagesAddress xlsxExternalReference `xml:"moreImagesAddress"`
+ Blip xlsxExternalReference `xml:"blip"`
+}