Skip to content

Commit e0b65db

Browse files
shcabinshcabin
authored andcommitted
This closes #2157, add support parsing rdrichvaluestructure.xml
1 parent 249b593 commit e0b65db

File tree

6 files changed

+134
-1
lines changed

6 files changed

+134
-1
lines changed

excelize.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,3 +672,14 @@ func (f *File) getRichValueWebImageRelationships(rID string) *xlsxRelationship {
672672
}
673673
return nil
674674
}
675+
676+
// richValueStructureReader provides a function to get the pointer to the structure after
677+
// deserialization of xl/richData/rdrichvaluestructure.xml.
678+
func (f *File) richValueStructureReader() (*xlsxRichValueStructuresData, error) {
679+
var richValueStruct xlsxRichValueStructuresData
680+
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLRdRichValueStructurePart)))).
681+
Decode(&richValueStruct); err != nil && err != io.EOF {
682+
return &richValueStruct, err
683+
}
684+
return &richValueStruct, nil
685+
}

picture.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,24 @@ type PictureInsertType byte
3131
const (
3232
PictureInsertTypePlaceOverCells PictureInsertType = iota
3333
PictureInsertTypePlaceInCell
34-
PictureInsertTypeIMAGE
34+
PictureInsertTypeIMAGE // created directly by formula (ex, =IMAGE), A type of PlaceInCell
3535
PictureInsertTypeDISPIMG
3636
)
3737

38+
// CalcOrigin: indicates how the rich value was created.
39+
const (
40+
CalcOriginNone = string(iota + '0')
41+
CalcOriginFormula // RichValue created directly by formula (ex, =IMAGE)
42+
CalcOriginComplexFormula
43+
CalcOriginDotNotation
44+
CalcOriginReference
45+
CalcOriginStandalone // Standalone RichValue directly stored in a cell without formula dependency (copy/paste as value or LocalImageValue)
46+
CalcOriginStandaloneDecorative // Standalone RichValue created from the alt text pane after selecting "decorative"
47+
CalcOriginNested
48+
CalcOriginJSApi
49+
CalcOriginPythonResult
50+
)
51+
3852
// parseGraphicOptions provides a function to parse the format settings of
3953
// the picture with default value.
4054
func parseGraphicOptions(opts *GraphicOptions) *GraphicOptions {
@@ -943,6 +957,33 @@ func (f *File) getImageCellRel(c *xlsxC, pic *Picture) (*xlsxRelationship, error
943957
return r, err
944958
}
945959
rv := richValue.Rv[richValueIdx].V
960+
rs := richValue.Rv[richValueIdx].S
961+
962+
richValueStructure, err := f.richValueStructureReader()
963+
if err != nil {
964+
return r, err
965+
}
966+
if rs < len(richValueStructure.S) {
967+
pic.InsertType = PictureInsertTypePlaceInCell
968+
for idx, key := range richValueStructure.S[rs].K {
969+
if idx >= len(rv) {
970+
break // invalid key
971+
}
972+
switch key.N {
973+
case "_rvRel:LocalImageIdentifier", "WebImageIdentifier":
974+
r, err = f.getRichDataRichValueRel(rv[idx])
975+
case "Text":
976+
pic.Format.AltText = rv[idx]
977+
case "CalcOrigin":
978+
// cell image inserted by IMAGE formula function
979+
if rv[idx] == CalcOriginFormula {
980+
pic.InsertType = PictureInsertTypeIMAGE
981+
}
982+
}
983+
}
984+
return r, err
985+
}
986+
// fallback, there is no valid struct definition
946987
if len(rv) == 2 && rv[1] == "5" {
947988
pic.InsertType = PictureInsertTypePlaceInCell
948989
return f.getRichDataRichValueRel(rv[0])

picture_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,11 @@ func TestGetCellImages(t *testing.T) {
500500
})
501501
return f
502502
}
503+
addStructure := func(f *File) *File {
504+
f.Pkg.Store(defaultXMLRdRichValueStructurePart, []byte(`<rvStructures xmlns="http://schemas.microsoft.com/office/spreadsheetml/2017/richdata" count="1">
505+
<s t="_localImage"><k n="_rvRel:LocalImageIdentifier" t="i"/><k n="CalcOrigin" t="i"/><k n="Text" t="s"/></s></rvStructures>`))
506+
return f
507+
}
503508
f = prepareWorkbook()
504509
pics, err := f.GetPictures("Sheet1", "A1")
505510
assert.NoError(t, err)
@@ -509,6 +514,17 @@ func TestGetCellImages(t *testing.T) {
509514
assert.NoError(t, err)
510515
assert.Equal(t, []string{"A1"}, cells)
511516

517+
f = addStructure(prepareWorkbook())
518+
// Test get the cell images with rich value struct
519+
pics, err = f.GetPictures("Sheet1", "A1")
520+
assert.NoError(t, err)
521+
assert.Equal(t, 1, len(pics))
522+
assert.Equal(t, PictureInsertTypePlaceInCell, pics[0].InsertType)
523+
cells, err = f.GetPictureCells("Sheet1")
524+
assert.NoError(t, err)
525+
assert.Equal(t, []string{"A1"}, cells)
526+
527+
f = prepareWorkbook()
512528
// Test get the cell images without image relationships parts
513529
f.Relationships.Delete(defaultXMLRdRichValueRelRels)
514530
f.Pkg.Store(defaultXMLRdRichValueRelRels, []byte(fmt.Sprintf(`<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="%s" Target="../media/image1.png"/></Relationships>`, SourceRelationshipHyperLink)))
@@ -605,6 +621,18 @@ func TestGetCellImages(t *testing.T) {
605621
f.Pkg.Store(defaultXMLRdRichValuePart, []byte(`<rvData count="1"><rv s="1"><v></v><v>1</v><v>0</v><v>0</v></rv></rvData>`))
606622
_, err = f.GetPictures("Sheet1", "A1")
607623
assert.EqualError(t, err, "strconv.Atoi: parsing \"\": invalid syntax")
624+
625+
f = addStructure(prepareWorkbook())
626+
// Test get the cell images with no valid definition and fallback old-style
627+
f.Pkg.Store(defaultXMLRdRichValueStructurePart, []byte(`<rvStructures xmlns="http://schemas.microsoft.com/office/spreadsheetml/2017/richdata" count="1"></rvStructures>`))
628+
pics, err = f.GetPictures("Sheet1", "A1")
629+
assert.NoError(t, err)
630+
assert.Equal(t, 1, len(pics))
631+
632+
// Test get the cell images with unsupported charset rich value
633+
f.Pkg.Store(defaultXMLRdRichValueStructurePart, MacintoshCyrillicCharset)
634+
_, err = f.GetPictures("Sheet1", "A1")
635+
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
608636
}
609637

610638
func TestGetImageCells(t *testing.T) {
@@ -615,3 +643,33 @@ func TestGetImageCells(t *testing.T) {
615643
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
616644
assert.NoError(t, f.Close())
617645
}
646+
647+
func TestGetCellImagesAndAltText(t *testing.T) {
648+
f, err := OpenFile(filepath.Join("test", "CellImage.xlsx"))
649+
assert.NoError(t, err)
650+
type imageType struct {
651+
cell string
652+
insertType PictureInsertType
653+
altText string
654+
}
655+
want := []imageType{
656+
{"B1", PictureInsertTypePlaceInCell, "Smiling alarm clock face"},
657+
{"B2", PictureInsertTypePlaceInCell, ""},
658+
{"B3", PictureInsertTypePlaceInCell, "Bullseye outline"},
659+
{"B4", PictureInsertTypeIMAGE, ""},
660+
{"B5", PictureInsertTypeIMAGE, "other alt_text"},
661+
662+
{"D1", PictureInsertTypePlaceInCell, "Smiling alarm clock face"},
663+
{"D2", PictureInsertTypePlaceInCell, ""},
664+
{"D3", PictureInsertTypePlaceInCell, "Bullseye outline"},
665+
{"D4", PictureInsertTypePlaceInCell, ""},
666+
{"D5", PictureInsertTypePlaceInCell, "other alt_text"},
667+
}
668+
for _, c := range want {
669+
p, err := f.GetPictures("Sheet1", c.cell)
670+
assert.NoError(t, err)
671+
assert.Equal(t, 1, len(p))
672+
assert.Equal(t, c.insertType, p[0].InsertType, c.cell)
673+
}
674+
assert.NoError(t, f.Close())
675+
}

templates.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ const (
309309
defaultXMLRdRichValueRelRels = "xl/richData/_rels/richValueRel.xml.rels"
310310
defaultXMLRdRichValueWebImagePart = "xl/richData/rdRichValueWebImage.xml"
311311
defaultXMLRdRichValueWebImagePartRels = "xl/richData/_rels/rdRichValueWebImage.xml.rels"
312+
defaultXMLRdRichValueStructurePart = "xl/richData/rdrichvaluestructure.xml"
312313
)
313314

314315
// IndexedColorMapping is the table of default mappings from indexed color value

test/CellImage.xlsx

126 KB
Binary file not shown.

xmlMetaData.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,25 @@ type xlsxWebImageSupportingRichData struct {
115115
MoreImagesAddress xlsxExternalReference `xml:"moreImagesAddress"`
116116
Blip xlsxExternalReference `xml:"blip"`
117117
}
118+
119+
// xlsxRichValueStructuresData directly maps the rvStructures element that specifies
120+
// rich value data.
121+
type xlsxRichValueStructuresData struct {
122+
XMLName xml.Name `xml:"rvStructures"`
123+
Count string `xml:"count,attr"`
124+
S []xlsxRichValueStructure `xml:"s"`
125+
}
126+
127+
// xlsxRichValueStructure directly maps the RichValueStructure element that specifies rich
128+
// value data.
129+
type xlsxRichValueStructure struct {
130+
T string `xml:"t,attr"`
131+
K []xlsxRichValueKey `xml:"k"`
132+
}
133+
134+
// xlsxRichValueKey directly maps the rich value key element that specifies rich value
135+
// data.
136+
type xlsxRichValueKey struct {
137+
N string `xml:"n,attr"`
138+
T string `xml:"t,attr"`
139+
}

0 commit comments

Comments
 (0)