Breaking changes: changed the function signature for 2 exported functions

- Change
    `func (f *File) AddPictureFromBytes(sheet, cell, name, extension string, file []byte, opts *GraphicOptions) error`
    to
    `func (f *File) AddPictureFromBytes(sheet, cell string, pic *Picture) error`
- Change
    `func (f *File) GetPicture(sheet, cell string) (string, []byte, error)`
    to
    `func (f *File) GetPictures(sheet, cell string) ([]Picture, error)`

Co-authored-by: huangsk <645636204@qq.com>
This commit is contained in:
xuri 2023-03-19 20:23:33 +08:00
parent 7631fd08e1
commit 478b528af1
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
5 changed files with 91 additions and 78 deletions

View File

@ -52,9 +52,8 @@ func TestConcurrency(t *testing.T) {
}, },
)) ))
// Concurrency get cell picture // Concurrency get cell picture
name, raw, err := f.GetPicture("Sheet1", "A1") pics, err := f.GetPictures("Sheet1", "A1")
assert.Equal(t, "", name) assert.Len(t, pics, 0)
assert.Nil(t, raw)
assert.NoError(t, err) assert.NoError(t, err)
// Concurrency iterate rows // Concurrency iterate rows
rows, err := f.Rows("Sheet1") rows, err := f.Rows("Sheet1")

View File

@ -1558,7 +1558,7 @@ func prepareTestBook1() (*File, error) {
return nil, err return nil, err
} }
err = f.AddPictureFromBytes("Sheet1", "Q1", "Excel Logo", ".jpg", file, nil) err = f.AddPictureFromBytes("Sheet1", "Q1", &Picture{Extension: ".jpg", File: file, Format: &GraphicOptions{AltText: "Excel Logo"}})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -145,19 +145,18 @@ func parseGraphicOptions(opts *GraphicOptions) *GraphicOptions {
// //
// The optional parameter "ScaleY" specifies the vertical scale of images, // The optional parameter "ScaleY" specifies the vertical scale of images,
// the default value of that is 1.0 which presents 100%. // the default value of that is 1.0 which presents 100%.
func (f *File) AddPicture(sheet, cell, picture string, opts *GraphicOptions) error { func (f *File) AddPicture(sheet, cell, name string, opts *GraphicOptions) error {
var err error var err error
// Check picture exists first. // Check picture exists first.
if _, err = os.Stat(picture); os.IsNotExist(err) { if _, err = os.Stat(name); os.IsNotExist(err) {
return err return err
} }
ext, ok := supportedImageTypes[path.Ext(picture)] ext, ok := supportedImageTypes[path.Ext(name)]
if !ok { if !ok {
return ErrImgExt return ErrImgExt
} }
file, _ := os.ReadFile(filepath.Clean(picture)) file, _ := os.ReadFile(filepath.Clean(name))
_, name := filepath.Split(picture) return f.AddPictureFromBytes(sheet, cell, &Picture{Extension: ext, File: file, Format: opts})
return f.AddPictureFromBytes(sheet, cell, name, ext, file, opts)
} }
// AddPictureFromBytes provides the method to add picture in a sheet by given // AddPictureFromBytes provides the method to add picture in a sheet by given
@ -188,7 +187,11 @@ func (f *File) AddPicture(sheet, cell, picture string, opts *GraphicOptions) err
// fmt.Println(err) // fmt.Println(err)
// return // return
// } // }
// if err := f.AddPictureFromBytes("Sheet1", "A2", "Excel Logo", ".jpg", file, nil); err != nil { // if err := f.AddPictureFromBytes("Sheet1", "A2", &excelize.Picture{
// Extension: ".jpg",
// File: file,
// Format: &excelize.GraphicOptions{AltText: "Excel Logo"},
// }); err != nil {
// fmt.Println(err) // fmt.Println(err)
// return // return
// } // }
@ -196,15 +199,15 @@ func (f *File) AddPicture(sheet, cell, picture string, opts *GraphicOptions) err
// fmt.Println(err) // fmt.Println(err)
// } // }
// } // }
func (f *File) AddPictureFromBytes(sheet, cell, name, extension string, file []byte, opts *GraphicOptions) error { func (f *File) AddPictureFromBytes(sheet, cell string, pic *Picture) error {
var drawingHyperlinkRID int var drawingHyperlinkRID int
var hyperlinkType string var hyperlinkType string
ext, ok := supportedImageTypes[extension] ext, ok := supportedImageTypes[pic.Extension]
if !ok { if !ok {
return ErrImgExt return ErrImgExt
} }
options := parseGraphicOptions(opts) options := parseGraphicOptions(pic.Format)
img, _, err := image.DecodeConfig(bytes.NewReader(file)) img, _, err := image.DecodeConfig(bytes.NewReader(pic.File))
if err != nil { if err != nil {
return err return err
} }
@ -219,7 +222,7 @@ func (f *File) AddPictureFromBytes(sheet, cell, name, extension string, file []b
drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml" drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
drawingID, drawingXML = f.prepareDrawing(ws, drawingID, sheet, drawingXML) drawingID, drawingXML = f.prepareDrawing(ws, drawingID, sheet, drawingXML)
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels" drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
mediaStr := ".." + strings.TrimPrefix(f.addMedia(file, ext), "xl") mediaStr := ".." + strings.TrimPrefix(f.addMedia(pic.File, ext), "xl")
drawingRID := f.addRels(drawingRels, SourceRelationshipImage, mediaStr, hyperlinkType) drawingRID := f.addRels(drawingRels, SourceRelationshipImage, mediaStr, hyperlinkType)
// Add picture with hyperlink. // Add picture with hyperlink.
if options.Hyperlink != "" && options.HyperlinkType != "" { if options.Hyperlink != "" && options.HyperlinkType != "" {
@ -229,7 +232,7 @@ func (f *File) AddPictureFromBytes(sheet, cell, name, extension string, file []b
drawingHyperlinkRID = f.addRels(drawingRels, SourceRelationshipHyperLink, options.Hyperlink, hyperlinkType) drawingHyperlinkRID = f.addRels(drawingRels, SourceRelationshipHyperLink, options.Hyperlink, hyperlinkType)
} }
ws.Unlock() ws.Unlock()
err = f.addDrawingPicture(sheet, drawingXML, cell, name, ext, drawingRID, drawingHyperlinkRID, img, options) err = f.addDrawingPicture(sheet, drawingXML, cell, ext, drawingRID, drawingHyperlinkRID, img, options)
if err != nil { if err != nil {
return err return err
} }
@ -319,7 +322,7 @@ func (f *File) countDrawings() int {
// addDrawingPicture provides a function to add picture by given sheet, // addDrawingPicture provides a function to add picture by given sheet,
// drawingXML, cell, file name, width, height relationship index and format // drawingXML, cell, file name, width, height relationship index and format
// sets. // sets.
func (f *File) addDrawingPicture(sheet, drawingXML, cell, file, ext string, rID, hyperlinkRID int, img image.Config, opts *GraphicOptions) error { func (f *File) addDrawingPicture(sheet, drawingXML, cell, ext string, rID, hyperlinkRID int, img image.Config, opts *GraphicOptions) error {
col, row, err := CellNameToCoordinates(cell) col, row, err := CellNameToCoordinates(cell)
if err != nil { if err != nil {
return err return err
@ -358,7 +361,7 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file, ext string, rID,
pic := xlsxPic{} pic := xlsxPic{}
pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect = opts.LockAspectRatio pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect = opts.LockAspectRatio
pic.NvPicPr.CNvPr.ID = cNvPrID pic.NvPicPr.CNvPr.ID = cNvPrID
pic.NvPicPr.CNvPr.Descr = file pic.NvPicPr.CNvPr.Descr = opts.AltText
pic.NvPicPr.CNvPr.Name = "Picture " + strconv.Itoa(cNvPrID) pic.NvPicPr.CNvPr.Name = "Picture " + strconv.Itoa(cNvPrID)
if hyperlinkRID != 0 { if hyperlinkRID != 0 {
pic.NvPicPr.CNvPr.HlinkClick = &xlsxHlinkClick{ pic.NvPicPr.CNvPr.HlinkClick = &xlsxHlinkClick{
@ -556,10 +559,10 @@ func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
return "" return ""
} }
// GetPicture provides a function to get picture base name and raw content // GetPictures provides a function to get picture meta info and raw content
// embed in spreadsheet by given worksheet and cell name. This function // embed in spreadsheet by given worksheet and cell name. This function
// returns the file name in spreadsheet and file contents as []byte data // returns the image contents as []byte data types. This function is
// types. This function is concurrency safe. For example: // concurrency safe. For example:
// //
// f, err := excelize.OpenFile("Book1.xlsx") // f, err := excelize.OpenFile("Book1.xlsx")
// if err != nil { // if err != nil {
@ -571,27 +574,29 @@ func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
// fmt.Println(err) // fmt.Println(err)
// } // }
// }() // }()
// file, raw, err := f.GetPicture("Sheet1", "A2") // pics, err := f.GetPictures("Sheet1", "A2")
// if err != nil { // if err != nil {
// fmt.Println(err) // fmt.Println(err)
// return
// } // }
// if err := os.WriteFile(file, raw, 0644); err != nil { // for idx, pic := range pics {
// fmt.Println(err) // name := fmt.Sprintf("image%d%s", idx+1, pic.Extension)
// if err := os.WriteFile(name, pic.File, 0644); err != nil {
// fmt.Println(err)
// }
// } // }
func (f *File) GetPicture(sheet, cell string) (string, []byte, error) { func (f *File) GetPictures(sheet, cell string) ([]Picture, error) {
col, row, err := CellNameToCoordinates(cell) col, row, err := CellNameToCoordinates(cell)
if err != nil { if err != nil {
return "", nil, err return nil, err
} }
col-- col--
row-- row--
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return "", nil, err return nil, err
} }
if ws.Drawing == nil { if ws.Drawing == nil {
return "", nil, err return nil, err
} }
target := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID) target := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)
drawingXML := strings.ReplaceAll(target, "..", "xl") drawingXML := strings.ReplaceAll(target, "..", "xl")
@ -601,7 +606,7 @@ 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 charts in spreadsheet by given // DeletePicture provides a function to delete all pictures in a cell by given
// worksheet name and cell reference. Note that the image file won't be deleted // worksheet name and cell reference. Note that the image file won't be deleted
// from the document currently. // from the document currently.
func (f *File) DeletePicture(sheet, cell string) error { func (f *File) DeletePicture(sheet, cell string) error {
@ -624,7 +629,7 @@ func (f *File) DeletePicture(sheet, cell string) error {
// 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 spreadsheet by given coordinates and drawing relationships. // embed in spreadsheet 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) (pics []Picture, err error) {
var ( var (
wsDr *xlsxWsDr wsDr *xlsxWsDr
ok bool ok bool
@ -636,7 +641,7 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
if wsDr, _, err = f.drawingParser(drawingXML); err != nil { if wsDr, _, err = f.drawingParser(drawingXML); err != nil {
return return
} }
if ret, buf = f.getPictureFromWsDr(row, col, drawingRelationships, wsDr); len(buf) > 0 { if pics = f.getPicturesFromWsDr(row, col, drawingRelationships, wsDr); len(pics) > 0 {
return return
} }
deWsDr = new(decodeWsDr) deWsDr = new(decodeWsDr)
@ -655,9 +660,11 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
if deTwoCellAnchor.From.Col == col && deTwoCellAnchor.From.Row == row { if deTwoCellAnchor.From.Col == col && deTwoCellAnchor.From.Row == row {
drawRel = f.getDrawingRelationships(drawingRelationships, deTwoCellAnchor.Pic.BlipFill.Blip.Embed) drawRel = f.getDrawingRelationships(drawingRelationships, deTwoCellAnchor.Pic.BlipFill.Blip.Embed)
if _, ok = supportedImageTypes[filepath.Ext(drawRel.Target)]; ok { if _, ok = supportedImageTypes[filepath.Ext(drawRel.Target)]; ok {
ret = filepath.Base(drawRel.Target) pic := Picture{Extension: filepath.Ext(drawRel.Target), Format: &GraphicOptions{}}
if buffer, _ := f.Pkg.Load(strings.ReplaceAll(drawRel.Target, "..", "xl")); buffer != nil { if buffer, _ := f.Pkg.Load(strings.ReplaceAll(drawRel.Target, "..", "xl")); buffer != nil {
buf = buffer.([]byte) pic.File = buffer.([]byte)
pic.Format.AltText = deTwoCellAnchor.Pic.NvPicPr.CNvPr.Descr
pics = append(pics, pic)
} }
return return
} }
@ -667,10 +674,10 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
return return
} }
// getPictureFromWsDr provides a function to get picture base name and raw // getPicturesFromWsDr provides a function to get picture base name and raw
// content in worksheet drawing by given coordinates and drawing // content in worksheet drawing by given coordinates and drawing
// relationships. // relationships.
func (f *File) getPictureFromWsDr(row, col int, drawingRelationships string, wsDr *xlsxWsDr) (ret string, buf []byte) { func (f *File) getPicturesFromWsDr(row, col int, drawingRelationships string, wsDr *xlsxWsDr) (pics []Picture) {
var ( var (
ok bool ok bool
anchor *xdrCellAnchor anchor *xdrCellAnchor
@ -684,11 +691,12 @@ func (f *File) getPictureFromWsDr(row, col int, drawingRelationships string, wsD
if drawRel = f.getDrawingRelationships(drawingRelationships, if drawRel = f.getDrawingRelationships(drawingRelationships,
anchor.Pic.BlipFill.Blip.Embed); drawRel != nil { anchor.Pic.BlipFill.Blip.Embed); drawRel != nil {
if _, ok = supportedImageTypes[filepath.Ext(drawRel.Target)]; ok { if _, ok = supportedImageTypes[filepath.Ext(drawRel.Target)]; ok {
ret = filepath.Base(drawRel.Target) pic := Picture{Extension: filepath.Ext(drawRel.Target), Format: &GraphicOptions{}}
if buffer, _ := f.Pkg.Load(strings.ReplaceAll(drawRel.Target, "..", "xl")); buffer != nil { if buffer, _ := f.Pkg.Load(strings.ReplaceAll(drawRel.Target, "..", "xl")); buffer != nil {
buf = buffer.([]byte) pic.File = buffer.([]byte)
pic.Format.AltText = anchor.Pic.NvPicPr.CNvPr.Descr
pics = append(pics, pic)
} }
return
} }
} }
} }

View File

@ -26,7 +26,7 @@ func BenchmarkAddPictureFromBytes(b *testing.B) {
} }
b.ResetTimer() b.ResetTimer()
for i := 1; i <= b.N; i++ { for i := 1; i <= b.N; i++ {
if err := f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", i), "excel", ".png", imgFile, nil); err != nil { if err := f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", i), &Picture{Extension: ".png", File: imgFile, Format: &GraphicOptions{AltText: "Excel"}}); err != nil {
b.Error(err) b.Error(err)
} }
} }
@ -58,9 +58,9 @@ func TestAddPicture(t *testing.T) {
assert.NoError(t, f.AddPicture("AddPicture", "A1", filepath.Join("test", "images", "excel.jpg"), &GraphicOptions{AutoFit: true})) assert.NoError(t, f.AddPicture("AddPicture", "A1", filepath.Join("test", "images", "excel.jpg"), &GraphicOptions{AutoFit: true}))
// Test add picture to worksheet from bytes // Test add picture to worksheet from bytes
assert.NoError(t, f.AddPictureFromBytes("Sheet1", "Q1", "Excel Logo", ".png", file, nil)) assert.NoError(t, f.AddPictureFromBytes("Sheet1", "Q1", &Picture{Extension: ".png", File: file, Format: &GraphicOptions{AltText: "Excel Logo"}}))
// Test add picture to worksheet from bytes with illegal cell reference // Test add picture to worksheet from bytes with illegal cell reference
assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "A", "Excel Logo", ".png", file, nil), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "A", &Picture{Extension: ".png", File: file, Format: &GraphicOptions{AltText: "Excel Logo"}}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
assert.NoError(t, f.AddPicture("Sheet1", "Q8", filepath.Join("test", "images", "excel.gif"), nil)) assert.NoError(t, f.AddPicture("Sheet1", "Q8", filepath.Join("test", "images", "excel.gif"), nil))
assert.NoError(t, f.AddPicture("Sheet1", "Q15", filepath.Join("test", "images", "excel.jpg"), nil)) assert.NoError(t, f.AddPicture("Sheet1", "Q15", filepath.Join("test", "images", "excel.jpg"), nil))
@ -75,7 +75,7 @@ func TestAddPicture(t *testing.T) {
f = NewFile() f = NewFile()
f.ContentTypes = nil f.ContentTypes = nil
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset) f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "Q1", "Excel Logo", ".png", file, nil), "XML syntax error on line 1: invalid UTF-8") assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "Q1", &Picture{Extension: ".png", File: file, Format: &GraphicOptions{AltText: "Excel Logo"}}), "XML syntax error on line 1: invalid UTF-8")
// Test add picture with invalid sheet name // Test add picture with invalid sheet name
assert.EqualError(t, f.AddPicture("Sheet:1", "A1", filepath.Join("test", "images", "excel.jpg"), nil), ErrSheetNameInvalid.Error()) assert.EqualError(t, f.AddPicture("Sheet:1", "A1", filepath.Join("test", "images", "excel.jpg"), nil), ErrSheetNameInvalid.Error())
@ -90,10 +90,11 @@ func TestAddPictureErrors(t *testing.T) {
// Test add picture to worksheet with unsupported file type // Test add picture to worksheet with unsupported file type
assert.EqualError(t, f.AddPicture("Sheet1", "G21", filepath.Join("test", "Book1.xlsx"), nil), ErrImgExt.Error()) assert.EqualError(t, f.AddPicture("Sheet1", "G21", filepath.Join("test", "Book1.xlsx"), nil), ErrImgExt.Error())
assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "G21", "Excel Logo", "jpg", make([]byte, 1), nil), ErrImgExt.Error())
assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "G21", &Picture{Extension: "jpg", File: make([]byte, 1), Format: &GraphicOptions{AltText: "Excel Logo"}}), ErrImgExt.Error())
// Test add picture to worksheet with invalid file data // Test add picture to worksheet with invalid file data
assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "G21", "Excel Logo", ".jpg", make([]byte, 1), nil), image.ErrFormat.Error()) assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "G21", &Picture{Extension: ".jpg", File: make([]byte, 1), Format: &GraphicOptions{AltText: "Excel Logo"}}), image.ErrFormat.Error())
// Test add picture with custom image decoder and encoder // Test add picture with custom image decoder and encoder
decode := func(r io.Reader) (image.Image, error) { return nil, nil } decode := func(r io.Reader) (image.Image, error) { return nil, nil }
@ -115,41 +116,39 @@ func TestAddPictureErrors(t *testing.T) {
func TestGetPicture(t *testing.T) { func TestGetPicture(t *testing.T) {
f := NewFile() f := NewFile()
assert.NoError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.png"), nil)) assert.NoError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.png"), nil))
name, content, err := f.GetPicture("Sheet1", "A1") pics, err := f.GetPictures("Sheet1", "A1")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 13233, len(content)) assert.Len(t, pics[0].File, 13233)
assert.Equal(t, "image1.png", name) assert.Empty(t, pics[0].Format.AltText)
f, err = prepareTestBook1() f, err = prepareTestBook1()
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
file, raw, err := f.GetPicture("Sheet1", "F21") pics, err = f.GetPictures("Sheet1", "F21")
assert.NoError(t, err) assert.NoError(t, err)
if !assert.NotEmpty(t, filepath.Join("test", file)) || !assert.NotEmpty(t, raw) || if !assert.NotEmpty(t, filepath.Join("test", fmt.Sprintf("image1%s", pics[0].Extension))) || !assert.NotEmpty(t, pics[0].File) ||
!assert.NoError(t, os.WriteFile(filepath.Join("test", file), raw, 0o644)) { !assert.NoError(t, os.WriteFile(filepath.Join("test", fmt.Sprintf("image1%s", pics[0].Extension)), pics[0].File, 0o644)) {
t.FailNow() t.FailNow()
} }
// Try to get picture from a worksheet with illegal cell reference // Try to get picture from a worksheet with illegal cell reference
_, _, err = f.GetPicture("Sheet1", "A") _, err = f.GetPictures("Sheet1", "A")
assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
// Try to get picture from a worksheet that doesn't contain any images // Try to get picture from a worksheet that doesn't contain any images
file, raw, err = f.GetPicture("Sheet3", "I9") pics, err = f.GetPictures("Sheet3", "I9")
assert.EqualError(t, err, "sheet Sheet3 does not exist") assert.EqualError(t, err, "sheet Sheet3 does not exist")
assert.Empty(t, file) assert.Len(t, pics, 0)
assert.Empty(t, raw)
// Try to get picture from a cell that doesn't contain an image // Try to get picture from a cell that doesn't contain an image
file, raw, err = f.GetPicture("Sheet2", "A2") pics, err = f.GetPictures("Sheet2", "A2")
assert.NoError(t, err) assert.NoError(t, err)
assert.Empty(t, file) assert.Len(t, pics, 0)
assert.Empty(t, raw)
// Test get picture with invalid sheet name // Test get picture with invalid sheet name
_, _, err = f.GetPicture("Sheet:1", "A2") _, err = f.GetPictures("Sheet:1", "A2")
assert.EqualError(t, err, ErrSheetNameInvalid.Error()) assert.EqualError(t, err, ErrSheetNameInvalid.Error())
f.getDrawingRelationships("xl/worksheets/_rels/sheet1.xml.rels", "rId8") f.getDrawingRelationships("xl/worksheets/_rels/sheet1.xml.rels", "rId8")
@ -163,36 +162,34 @@ func TestGetPicture(t *testing.T) {
f, err = OpenFile(filepath.Join("test", "TestGetPicture.xlsx")) f, err = OpenFile(filepath.Join("test", "TestGetPicture.xlsx"))
assert.NoError(t, err) assert.NoError(t, err)
file, raw, err = f.GetPicture("Sheet1", "F21") pics, err = f.GetPictures("Sheet1", "F21")
assert.NoError(t, err) assert.NoError(t, err)
if !assert.NotEmpty(t, filepath.Join("test", file)) || !assert.NotEmpty(t, raw) || if !assert.NotEmpty(t, filepath.Join("test", fmt.Sprintf("image1%s", pics[0].Extension))) || !assert.NotEmpty(t, pics[0].File) ||
!assert.NoError(t, os.WriteFile(filepath.Join("test", file), raw, 0o644)) { !assert.NoError(t, os.WriteFile(filepath.Join("test", fmt.Sprintf("image1%s", pics[0].Extension)), pics[0].File, 0o644)) {
t.FailNow() t.FailNow()
} }
// Try to get picture from a local storage file that doesn't contain an image // Try to get picture from a local storage file that doesn't contain an image
file, raw, err = f.GetPicture("Sheet1", "F22") pics, err = f.GetPictures("Sheet1", "F22")
assert.NoError(t, err) assert.NoError(t, err)
assert.Empty(t, file) assert.Len(t, pics, 0)
assert.Empty(t, raw)
assert.NoError(t, f.Close()) assert.NoError(t, f.Close())
// Test get picture from none drawing worksheet // Test get picture from none drawing worksheet
f = NewFile() f = NewFile()
file, raw, err = f.GetPicture("Sheet1", "F22") pics, err = f.GetPictures("Sheet1", "F22")
assert.NoError(t, err) assert.NoError(t, err)
assert.Empty(t, file) assert.Len(t, pics, 0)
assert.Empty(t, raw)
f, err = prepareTestBook1() f, err = prepareTestBook1()
assert.NoError(t, err) assert.NoError(t, err)
// Test get pictures with unsupported charset // Test get pictures with unsupported charset
path := "xl/drawings/drawing1.xml" path := "xl/drawings/drawing1.xml"
f.Pkg.Store(path, MacintoshCyrillicCharset) f.Pkg.Store(path, MacintoshCyrillicCharset)
_, _, err = f.getPicture(20, 5, path, "xl/drawings/_rels/drawing2.xml.rels") _, err = f.getPicture(20, 5, path, "xl/drawings/_rels/drawing2.xml.rels")
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
f.Drawings.Delete(path) f.Drawings.Delete(path)
_, _, err = f.getPicture(20, 5, path, "xl/drawings/_rels/drawing2.xml.rels") _, err = f.getPicture(20, 5, path, "xl/drawings/_rels/drawing2.xml.rels")
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
} }
@ -200,19 +197,20 @@ func TestAddDrawingPicture(t *testing.T) {
// Test addDrawingPicture with illegal cell reference // Test addDrawingPicture with illegal cell reference
f := NewFile() f := NewFile()
opts := &GraphicOptions{PrintObject: boolPtr(true), Locked: boolPtr(false)} opts := &GraphicOptions{PrintObject: boolPtr(true), Locked: boolPtr(false)}
assert.EqualError(t, f.addDrawingPicture("sheet1", "", "A", "", "", 0, 0, image.Config{}, opts), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) assert.EqualError(t, f.addDrawingPicture("sheet1", "", "A", "", 0, 0, image.Config{}, opts), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
path := "xl/drawings/drawing1.xml" path := "xl/drawings/drawing1.xml"
f.Pkg.Store(path, MacintoshCyrillicCharset) f.Pkg.Store(path, MacintoshCyrillicCharset)
assert.EqualError(t, f.addDrawingPicture("sheet1", path, "A1", "", "", 0, 0, image.Config{}, opts), "XML syntax error on line 1: invalid UTF-8") assert.EqualError(t, f.addDrawingPicture("sheet1", path, "A1", "", 0, 0, image.Config{}, opts), "XML syntax error on line 1: invalid UTF-8")
} }
func TestAddPictureFromBytes(t *testing.T) { func TestAddPictureFromBytes(t *testing.T) {
f := NewFile() f := NewFile()
imgFile, err := os.ReadFile("logo.png") imgFile, err := os.ReadFile("logo.png")
assert.NoError(t, err, "Unable to load logo for test") assert.NoError(t, err, "Unable to load logo for test")
assert.NoError(t, f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", 1), "logo", ".png", imgFile, nil))
assert.NoError(t, f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", 50), "logo", ".png", imgFile, nil)) assert.NoError(t, f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", 1), &Picture{Extension: ".png", File: imgFile, Format: &GraphicOptions{AltText: "logo"}}))
assert.NoError(t, f.AddPictureFromBytes("Sheet1", fmt.Sprint("A", 50), &Picture{Extension: ".png", File: imgFile, Format: &GraphicOptions{AltText: "logo"}}))
imageCount := 0 imageCount := 0
f.Pkg.Range(func(fileName, v interface{}) bool { f.Pkg.Range(func(fileName, v interface{}) bool {
if strings.Contains(fileName.(string), "media/image") { if strings.Contains(fileName.(string), "media/image") {
@ -221,9 +219,9 @@ func TestAddPictureFromBytes(t *testing.T) {
return true return true
}) })
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, nil), "sheet SheetN does not exist") assert.EqualError(t, f.AddPictureFromBytes("SheetN", fmt.Sprint("A", 1), &Picture{Extension: ".png", File: imgFile, Format: &GraphicOptions{AltText: "logo"}}), "sheet SheetN does not exist")
// Test add picture from bytes with invalid sheet name // Test add picture from bytes with invalid sheet name
assert.EqualError(t, f.AddPictureFromBytes("Sheet:1", fmt.Sprint("A", 1), "logo", ".png", imgFile, nil), ErrSheetNameInvalid.Error()) assert.EqualError(t, f.AddPictureFromBytes("Sheet:1", fmt.Sprint("A", 1), &Picture{Extension: ".png", File: imgFile, Format: &GraphicOptions{AltText: "logo"}}), ErrSheetNameInvalid.Error())
} }
func TestDeletePicture(t *testing.T) { func TestDeletePicture(t *testing.T) {

View File

@ -581,8 +581,16 @@ type xdrTxBody struct {
P []*aP `xml:"a:p"` P []*aP `xml:"a:p"`
} }
// Picture maps the format settings of the picture.
type Picture struct {
Extension string
File []byte
Format *GraphicOptions
}
// GraphicOptions directly maps the format settings of the picture. // GraphicOptions directly maps the format settings of the picture.
type GraphicOptions struct { type GraphicOptions struct {
AltText string
PrintObject *bool PrintObject *bool
Locked *bool Locked *bool
LockAspectRatio bool LockAspectRatio bool