Skip to content

Commit 5618929

Browse files
authored
The GetPictures function support return partial format properties (qax-os#1639)
- Update unit tests
1 parent 85ebb61 commit 5618929

3 files changed

Lines changed: 211 additions & 43 deletions

File tree

picture.go

Lines changed: 123 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,8 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, ext string, rID, hyper
416416
}
417417
}
418418
pic.SpPr.PrstGeom.Prst = "rect"
419+
pic.SpPr.Xfrm.Ext.Cx = width * EMU
420+
pic.SpPr.Xfrm.Ext.Cy = height * EMU
419421

420422
if opts.Positioning == "oneCell" {
421423
cx := x2 * EMU
@@ -484,7 +486,10 @@ func (f *File) addMedia(file []byte, ext string) string {
484486
// GetPictures provides a function to get picture meta info and raw content
485487
// embed in spreadsheet by given worksheet and cell name. This function
486488
// returns the image contents as []byte data types. This function is
487-
// concurrency safe. For example:
489+
// concurrency safe. Note that this function currently does not support
490+
// retrieving all properties from the image's Format property, and the value of
491+
// the ScaleX and ScaleY property may be an approximate precision value in some
492+
// cases. For example:
488493
//
489494
// f, err := excelize.OpenFile("Book1.xlsx")
490495
// if err != nil {
@@ -634,27 +639,14 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
634639
defer wsDr.mu.Unlock()
635640
cond := func(from *xlsxFrom) bool { return from.Col == col && from.Row == row }
636641
cond2 := func(from *decodeFrom) bool { return from.Col == col && from.Row == row }
637-
cb := func(a *xdrCellAnchor, r *xlsxRelationship) {
638-
pic := Picture{Extension: filepath.Ext(r.Target), Format: &GraphicOptions{}, InsertType: PictureInsertTypePlaceOverCells}
639-
if buffer, _ := f.Pkg.Load(filepath.ToSlash(filepath.Clean("xl/drawings/" + r.Target))); buffer != nil {
640-
pic.File = buffer.([]byte)
641-
pic.Format.AltText = a.Pic.NvPicPr.CNvPr.Descr
642-
pics = append(pics, pic)
642+
cb := func(a *xdrCellAnchor, r *xlsxRelationship, drawingRelationships string) {
643+
if pic := f.extractPictureFromAnchor(drawingRelationships, a, r); pic != nil {
644+
pics = append(pics, *pic)
643645
}
644646
}
645-
cb2 := func(a *decodeCellAnchor, r *xlsxRelationship) {
646-
var target string
647-
if strings.HasPrefix(r.Target, "/") {
648-
target = strings.TrimPrefix(r.Target, "/")
649-
} else {
650-
target = filepath.ToSlash(filepath.Clean("xl/drawings/" + r.Target))
651-
}
652-
653-
pic := Picture{Extension: filepath.Ext(target), Format: &GraphicOptions{}, InsertType: PictureInsertTypePlaceOverCells}
654-
if buffer, _ := f.Pkg.Load(target); buffer != nil {
655-
pic.File = buffer.([]byte)
656-
pic.Format.AltText = a.Pic.NvPicPr.CNvPr.Descr
657-
pics = append(pics, pic)
647+
cb2 := func(a *decodeCellAnchor, r *xlsxRelationship, drawingRelationships string) {
648+
if pic := f.extractPictureFromDecodeAnchor(drawingRelationships, a, r); pic != nil {
649+
pics = append(pics, *pic)
658650
}
659651
}
660652
for _, anchor := range wsDr.TwoCellAnchor {
@@ -666,12 +658,116 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
666658
return
667659
}
668660

661+
// extractPictureFromAnchor extracts picture data from a cell anchor and
662+
// relationship.
663+
func (f *File) extractPictureFromAnchor(drawingRelationships string, a *xdrCellAnchor, r *xlsxRelationship) *Picture {
664+
var (
665+
cx, cy int
666+
pic *Picture
667+
)
668+
if buffer, _ := f.Pkg.Load(filepath.ToSlash(filepath.Clean("xl/drawings/" + r.Target))); buffer != nil {
669+
pic = &Picture{
670+
Extension: filepath.Ext(r.Target),
671+
File: buffer.([]byte),
672+
Format: &GraphicOptions{ScaleX: defaultDrawingScale, ScaleY: defaultDrawingScale},
673+
}
674+
if a.ClientData != nil {
675+
pic.Format.Locked = &a.ClientData.FLocksWithSheet
676+
pic.Format.PrintObject = &a.ClientData.FPrintsWithSheet
677+
}
678+
if a.To == nil {
679+
pic.Format.Positioning = "oneCell"
680+
}
681+
if a.Pic != nil {
682+
cx, cy = a.Pic.SpPr.Xfrm.Ext.Cx, a.Pic.SpPr.Xfrm.Ext.Cy
683+
if a.From != nil {
684+
pic.Format.OffsetX = int(a.From.ColOff / EMU)
685+
pic.Format.OffsetY = int(a.From.RowOff / EMU)
686+
}
687+
pic.Format.LockAspectRatio = a.Pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect
688+
pic.Format.AltText = a.Pic.NvPicPr.CNvPr.Descr
689+
if a.Pic.NvPicPr.CNvPr.HlinkClick != nil {
690+
if drawRel := f.getDrawingRelationships(drawingRelationships, a.Pic.NvPicPr.CNvPr.HlinkClick.RID); drawRel != nil {
691+
pic.Format.Hyperlink = drawRel.Target
692+
pic.Format.HyperlinkType = "Location"
693+
if drawRel.TargetMode == "External" {
694+
pic.Format.HyperlinkType = "External"
695+
}
696+
}
697+
}
698+
}
699+
f.calculatePictureScale(pic, cx, cy)
700+
}
701+
return pic
702+
}
703+
704+
// calculatePictureScale calculates and sets the scale factors for a picture.
705+
func (f *File) calculatePictureScale(pic *Picture, cx, cy int) {
706+
imgCfg, _, err := image.DecodeConfig(bytes.NewReader(pic.File))
707+
if err != nil || imgCfg.Width <= 0 || imgCfg.Height <= 0 || cx <= 0 || cy <= 0 {
708+
return
709+
}
710+
pic.Format.ScaleX = float64(cx) / float64(EMU) / float64(imgCfg.Width)
711+
pic.Format.ScaleY = float64(cy) / float64(EMU) / float64(imgCfg.Height)
712+
}
713+
714+
// extractPictureFromDecodeAnchor extracts picture data from a decoded cell
715+
// anchor and relationship.
716+
func (f *File) extractPictureFromDecodeAnchor(drawingRelationships string, a *decodeCellAnchor, r *xlsxRelationship) *Picture {
717+
var (
718+
cx, cy int
719+
pic *Picture
720+
target string
721+
)
722+
if strings.HasPrefix(r.Target, "/") {
723+
target = strings.TrimPrefix(r.Target, "/")
724+
} else {
725+
target = filepath.ToSlash(filepath.Clean("xl/drawings/" + r.Target))
726+
}
727+
if buffer, _ := f.Pkg.Load(target); buffer != nil {
728+
pic = &Picture{
729+
Extension: filepath.Ext(target),
730+
File: buffer.([]byte),
731+
Format: &GraphicOptions{ScaleX: defaultDrawingScale, ScaleY: defaultDrawingScale},
732+
}
733+
if a.ClientData != nil {
734+
pic.Format.Locked = &a.ClientData.FLocksWithSheet
735+
pic.Format.PrintObject = &a.ClientData.FPrintsWithSheet
736+
}
737+
if a.To == nil {
738+
pic.Format.Positioning = "oneCell"
739+
}
740+
if a.Pic != nil {
741+
cx, cy = a.Pic.SpPr.Xfrm.Ext.Cx, a.Pic.SpPr.Xfrm.Ext.Cy
742+
if a.From != nil {
743+
pic.Format.OffsetX = int(a.From.ColOff / EMU)
744+
pic.Format.OffsetY = int(a.From.RowOff / EMU)
745+
}
746+
pic.Format.LockAspectRatio = a.Pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect
747+
pic.Format.AltText = a.Pic.NvPicPr.CNvPr.Descr
748+
if a.Pic.NvPicPr.CNvPr.HlinkClick != nil {
749+
if drawRel := f.getDrawingRelationships(drawingRelationships, a.Pic.NvPicPr.CNvPr.HlinkClick.RID); drawRel != nil {
750+
pic.Format.Hyperlink = drawRel.Target
751+
pic.Format.HyperlinkType = "Location"
752+
if drawRel.TargetMode == "External" {
753+
pic.Format.HyperlinkType = "External"
754+
}
755+
}
756+
}
757+
}
758+
f.calculatePictureScale(pic, cx, cy)
759+
}
760+
return pic
761+
}
762+
669763
// extractCellAnchor extract drawing object from cell anchor by giving drawing
670764
// cell anchor, drawing relationships part path, conditional and callback
671765
// function.
672766
func (f *File) extractCellAnchor(anchor *xdrCellAnchor, drawingRelationships string,
673-
cond func(from *xlsxFrom) bool, cb func(anchor *xdrCellAnchor, rels *xlsxRelationship),
674-
cond2 func(from *decodeFrom) bool, cb2 func(anchor *decodeCellAnchor, rels *xlsxRelationship),
767+
cond func(from *xlsxFrom) bool,
768+
cb func(anchor *xdrCellAnchor, rels *xlsxRelationship, drawingRelationships string),
769+
cond2 func(from *decodeFrom) bool,
770+
cb2 func(anchor *decodeCellAnchor, rels *xlsxRelationship, drawingRelationships string),
675771
) {
676772
var drawRel *xlsxRelationship
677773
if anchor.GraphicFrame == "" {
@@ -680,7 +776,7 @@ func (f *File) extractCellAnchor(anchor *xdrCellAnchor, drawingRelationships str
680776
if drawRel = f.getDrawingRelationships(drawingRelationships,
681777
anchor.Pic.BlipFill.Blip.Embed); drawRel != nil {
682778
if _, ok := supportedImageTypes[strings.ToLower(filepath.Ext(drawRel.Target))]; ok {
683-
cb(anchor, drawRel)
779+
cb(anchor, drawRel, drawingRelationships)
684780
}
685781
}
686782
}
@@ -694,7 +790,7 @@ func (f *File) extractCellAnchor(anchor *xdrCellAnchor, drawingRelationships str
694790
// decoded drawing cell anchor, drawing relationships part path, conditional and
695791
// callback function.
696792
func (f *File) extractDecodeCellAnchor(anchor *xdrCellAnchor, drawingRelationships string,
697-
cond func(from *decodeFrom) bool, cb func(anchor *decodeCellAnchor, rels *xlsxRelationship),
793+
cond func(from *decodeFrom) bool, cb func(anchor *decodeCellAnchor, rels *xlsxRelationship, drawingRelationships string),
698794
) {
699795
var (
700796
drawRel *xlsxRelationship
@@ -705,7 +801,7 @@ func (f *File) extractDecodeCellAnchor(anchor *xdrCellAnchor, drawingRelationshi
705801
if cond(deCellAnchor.From) {
706802
if drawRel = f.getDrawingRelationships(drawingRelationships, deCellAnchor.Pic.BlipFill.Blip.Embed); drawRel != nil {
707803
if _, ok := supportedImageTypes[strings.ToLower(filepath.Ext(drawRel.Target))]; ok {
708-
cb(deCellAnchor, drawRel)
804+
cb(deCellAnchor, drawRel, drawingRelationships)
709805
}
710806
}
711807
}
@@ -800,14 +896,14 @@ func (f *File) getPictureCells(drawingXML, drawingRelationships string) ([]strin
800896
defer wsDr.mu.Unlock()
801897
cond := func(from *xlsxFrom) bool { return true }
802898
cond2 := func(from *decodeFrom) bool { return true }
803-
cb := func(a *xdrCellAnchor, r *xlsxRelationship) {
899+
cb := func(a *xdrCellAnchor, r *xlsxRelationship, drawingRelationships string) {
804900
if _, ok := f.Pkg.Load(filepath.ToSlash(filepath.Clean("xl/drawings/" + r.Target))); ok {
805901
if cell, err := CoordinatesToCellName(a.From.Col+1, a.From.Row+1); err == nil && inStrSlice(cells, cell, true) == -1 {
806902
cells = append(cells, cell)
807903
}
808904
}
809905
}
810-
cb2 := func(a *decodeCellAnchor, r *xlsxRelationship) {
906+
cb2 := func(a *decodeCellAnchor, r *xlsxRelationship, drawingRelationships string) {
811907
var target string
812908
if strings.HasPrefix(r.Target, "/") {
813909
target = strings.TrimPrefix(r.Target, "/")

picture_test.go

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,22 @@ func TestAddPicture(t *testing.T) {
3636
assert.NoError(t, err)
3737

3838
// Test add picture to worksheet with offset and location hyperlink
39-
assert.NoError(t, f.AddPicture("Sheet2", "I9", filepath.Join("test", "images", "excel.jpg"),
40-
&GraphicOptions{OffsetX: 140, OffsetY: 120, Hyperlink: "#Sheet2!D8", HyperlinkType: "Location"}))
39+
opts := []GraphicOptions{
40+
{Hyperlink: "#Sheet2!D8", HyperlinkType: "Location"},
41+
{OffsetX: 10, OffsetY: 10, ScaleX: 0.5, ScaleY: 0.5, Hyperlink: "https://github.com/xuri/excelize", HyperlinkType: "External", Positioning: "oneCell"},
42+
{OffsetX: 10, OffsetY: 10, ScaleX: 1.5, ScaleY: 1.5, Hyperlink: "https://github.com/xuri/excelize", HyperlinkType: "External"},
43+
{PrintObject: boolPtr(true), Locked: boolPtr(true), OffsetX: 200, ScaleX: 1, ScaleY: 1, Positioning: "oneCell"},
44+
{PrintObject: boolPtr(true), Locked: boolPtr(true), AltText: "Excel Logo", LockAspectRatio: true, ScaleX: 1, ScaleY: 1},
45+
}
46+
assert.NoError(t, f.AddPicture("Sheet2", "I9", filepath.Join("test", "images", "excel.jpg"), &opts[0]))
4147
// Test add picture to worksheet with offset, external hyperlink and positioning
42-
assert.NoError(t, f.AddPicture("Sheet1", "F21", filepath.Join("test", "images", "excel.jpg"),
43-
&GraphicOptions{OffsetX: 10, OffsetY: 10, Hyperlink: "https://github.com/xuri/excelize", HyperlinkType: "External", Positioning: "oneCell"}))
48+
assert.NoError(t, f.AddPicture("Sheet1", "F21", filepath.Join("test", "images", "excel.jpg"), &opts[1]))
49+
assert.NoError(t, f.AddPicture("Sheet1", "H21", filepath.Join("test", "images", "excel.jpg"), &opts[2]))
4450

4551
// Test add pictures to single cell with offsets
4652
assert.NoError(t, f.AddPicture("Sheet2", "K22", filepath.Join("test", "images", "excel.jpg"),
4753
&GraphicOptions{Positioning: "oneCell"}))
48-
assert.NoError(t, f.AddPicture("Sheet2", "K22", filepath.Join("test", "images", "excel.jpg"),
49-
&GraphicOptions{OffsetX: 200, Positioning: "oneCell"}))
54+
assert.NoError(t, f.AddPicture("Sheet2", "K22", filepath.Join("test", "images", "excel.jpg"), &opts[3]))
5055
assert.NoError(t, f.AddPicture("Sheet2", "K22", filepath.Join("test", "images", "excel.jpg"),
5156
&GraphicOptions{OffsetX: 400, Positioning: "oneCell"}))
5257
assert.NoError(t, f.AddPicture("Sheet2", "K22", filepath.Join("test", "images", "excel.jpg"),
@@ -68,7 +73,7 @@ func TestAddPicture(t *testing.T) {
6873
assert.NoError(t, f.AddPicture("AddPicture", "A1", filepath.Join("test", "images", "excel.jpg"), &GraphicOptions{AutoFit: true}))
6974

7075
// Test add picture to worksheet from bytes
71-
assert.NoError(t, f.AddPictureFromBytes("Sheet1", "Q1", &Picture{Extension: ".png", File: file, Format: &GraphicOptions{AltText: "Excel Logo"}}))
76+
assert.NoError(t, f.AddPictureFromBytes("Sheet1", "Q1", &Picture{Extension: ".png", File: file, Format: &opts[4]}))
7277
// Test add picture to worksheet from bytes with unsupported insert type
7378
assert.Equal(t, ErrParameterInvalid, f.AddPictureFromBytes("Sheet1", "Q1", &Picture{Extension: ".png", File: file, Format: &GraphicOptions{AltText: "Excel Logo"}, InsertType: PictureInsertTypePlaceInCell}))
7479
// Test add picture to worksheet from bytes with illegal cell reference
@@ -77,6 +82,31 @@ func TestAddPicture(t *testing.T) {
7782
for _, preset := range [][]string{{"Q8", "gif"}, {"Q15", "jpg"}, {"Q22", "tif"}, {"Q28", "bmp"}} {
7883
assert.NoError(t, f.AddPicture("Sheet1", preset[0], filepath.Join("test", "images", fmt.Sprintf("excel.%s", preset[1])), nil))
7984
}
85+
// Test get one cell anchor pictures from worksheet which added pictures with offset
86+
pics, err := f.GetPictures("Sheet2", "K22")
87+
assert.NoError(t, err)
88+
assert.Len(t, pics, 4)
89+
assert.Equal(t, opts[3], *pics[1].Format)
90+
// Test get one cell anchor pictures from worksheet which added pictures with offset and scale
91+
pics, err = f.GetPictures("Sheet1", "F21")
92+
assert.NoError(t, err)
93+
assert.Len(t, pics, 1)
94+
assert.Equal(t, opts[1], *pics[0].Format)
95+
// Test get two cell anchor pictures from worksheet which added pictures with offset and scale
96+
pics, err = f.GetPictures("Sheet1", "H21")
97+
assert.NoError(t, err)
98+
assert.Len(t, pics, 1)
99+
assert.Equal(t, opts[2], *pics[0].Format)
100+
// Test get two cell anchor pictures from worksheet which added pictures with alternative text
101+
pics, err = f.GetPictures("Sheet1", "Q1")
102+
assert.NoError(t, err)
103+
assert.Len(t, pics, 1)
104+
assert.Equal(t, opts[4], *pics[0].Format)
105+
// Test get two cell anchor pictures from worksheet which added pictures with location hyperlink
106+
pics, err = f.GetPictures("Sheet2", "I9")
107+
assert.NoError(t, err)
108+
assert.Len(t, pics, 1)
109+
assert.Equal(t, opts[0], *pics[0].Format)
80110

81111
// Test write file to given path
82112
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddPicture1.xlsx")))
@@ -86,14 +116,40 @@ func TestAddPicture(t *testing.T) {
86116
f, err = OpenFile(filepath.Join("test", "TestAddPicture1.xlsx"))
87117
assert.NoError(t, err)
88118
assert.NoError(t, f.AddPicture("Sheet1", "A30", filepath.Join("test", "images", "excel.jpg"), nil))
89-
pics, err := f.GetPictures("Sheet1", "A30")
119+
pics, err = f.GetPictures("Sheet1", "A30")
90120
assert.NoError(t, err)
91121
assert.Len(t, pics, 2)
92122

123+
// Test get one cell anchor pictures from worksheet which already contains pictures
124+
pics, err = f.GetPictures("Sheet2", "K22")
125+
assert.NoError(t, err)
126+
assert.Len(t, pics, 4)
127+
assert.Equal(t, opts[3], *pics[1].Format)
128+
// Test get one cell anchor pictures from worksheet which already contains pictures with offset and scale
129+
pics, err = f.GetPictures("Sheet1", "F21")
130+
assert.NoError(t, err)
131+
assert.Len(t, pics, 1)
132+
assert.Equal(t, opts[1], *pics[0].Format)
133+
// Test get two cell anchor pictures from worksheet which already contains pictures
134+
pics, err = f.GetPictures("Sheet1", "H21")
135+
assert.NoError(t, err)
136+
assert.Len(t, pics, 1)
137+
assert.Equal(t, opts[2], *pics[0].Format)
138+
// Test get two cell anchor pictures from worksheet which already contains pictures with alternative text
139+
pics, err = f.GetPictures("Sheet1", "Q1")
140+
assert.NoError(t, err)
141+
assert.Len(t, pics, 1)
142+
assert.Equal(t, opts[4], *pics[0].Format)
143+
// Test get two cell anchor pictures from worksheet which already contains pictures with location hyperlink
144+
pics, err = f.GetPictures("Sheet2", "I9")
145+
assert.NoError(t, err)
146+
assert.Len(t, pics, 1)
147+
assert.Equal(t, opts[0], *pics[0].Format)
148+
93149
// Test get picture cells
94150
cells, err := f.GetPictureCells("Sheet1")
95151
assert.NoError(t, err)
96-
assert.Equal(t, []string{"A30", "B30", "C30", "Q1", "Q8", "Q15", "Q22", "Q28", "F21"}, cells)
152+
assert.Equal(t, []string{"H21", "A30", "B30", "C30", "Q1", "Q8", "Q15", "Q22", "Q28", "F21"}, cells)
97153
assert.NoError(t, f.Close())
98154

99155
f, err = OpenFile(filepath.Join("test", "TestAddPicture1.xlsx"))
@@ -102,7 +158,7 @@ func TestAddPicture(t *testing.T) {
102158
f.Drawings.Delete(path)
103159
cells, err = f.GetPictureCells("Sheet1")
104160
assert.NoError(t, err)
105-
assert.Equal(t, []string{"A30", "B30", "C30", "Q1", "Q8", "Q15", "Q22", "Q28", "F21"}, cells)
161+
assert.Equal(t, []string{"H21", "A30", "B30", "C30", "Q1", "Q8", "Q15", "Q22", "Q28", "F21"}, cells)
106162
// Test get picture cells with unsupported charset
107163
f.Drawings.Delete(path)
108164
f.Pkg.Store(path, MacintoshCyrillicCharset)
@@ -473,7 +529,7 @@ func TestGetPictureCells(t *testing.T) {
473529
func TestExtractDecodeCellAnchor(t *testing.T) {
474530
f := NewFile()
475531
cond := func(a *decodeFrom) bool { return true }
476-
cb := func(a *decodeCellAnchor, r *xlsxRelationship) {}
532+
cb := func(a *decodeCellAnchor, r *xlsxRelationship, drawingRelationships string) {}
477533
f.extractDecodeCellAnchor(&xdrCellAnchor{GraphicFrame: string(MacintoshCyrillicCharset)}, "", cond, cb)
478534
}
479535

xmlDecodeDrawing.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,27 @@ type decodeWsDr struct {
118118
// information that does not affect the appearance of the picture to be
119119
// stored.
120120
type decodeCNvPr struct {
121-
XMLName xml.Name `xml:"cNvPr"`
122-
ID int `xml:"id,attr"`
123-
Name string `xml:"name,attr"`
124-
Descr string `xml:"descr,attr"`
125-
Title string `xml:"title,attr,omitempty"`
121+
XMLName xml.Name `xml:"cNvPr"`
122+
ID int `xml:"id,attr"`
123+
Name string `xml:"name,attr"`
124+
Descr string `xml:"descr,attr"`
125+
Title string `xml:"title,attr,omitempty"`
126+
HlinkClick *decodeHlinkClick `xml:"hlinkClick,omitempty"`
127+
}
128+
129+
// decodeHlinkClick directly maps the hlinkClick (Hyperlink Click).This element
130+
// specifies the on-click hyperlink information to be applied to a run of text.
131+
// When the hyperlink text is clicked the link is fetched.
132+
// clicked the link is fetched.
133+
type decodeHlinkClick struct {
134+
RID string `xml:"id,attr,omitempty"`
135+
InvalidURL string `xml:"invalidUrl,attr,omitempty"`
136+
Action string `xml:"action,attr,omitempty"`
137+
TgtFrame string `xml:"tgtFrame,attr,omitempty"`
138+
Tooltip string `xml:"tooltip,attr,omitempty"`
139+
History bool `xml:"history,attr,omitempty"`
140+
HighlightClick bool `xml:"highlightClick,attr,omitempty"`
141+
EndSnd bool `xml:"endSnd,attr,omitempty"`
126142
}
127143

128144
// decodePicLocks directly maps the picLocks (Picture Locks). This element

0 commit comments

Comments
 (0)