From db2d084ada1a08a48967506b2f1852062168deec Mon Sep 17 00:00:00 2001 From: xuri Date: Wed, 2 Nov 2022 08:42:00 +0800 Subject: [PATCH] This closes #1204, breaking changes for add comments - Allowing insert SVG format images - Unit tests updated --- .gitignore | 18 +++--- cell.go | 55 ++++++++++-------- comment.go | 137 ++++++++++++++++++++++---------------------- comment_test.go | 22 +++---- excelize_test.go | 3 +- picture.go | 23 +++++++- picture_test.go | 4 +- xmlComments.go | 15 ++--- xmlDrawing.go | 37 ++++++++++-- xmlSharedStrings.go | 4 +- 10 files changed, 181 insertions(+), 137 deletions(-) diff --git a/.gitignore b/.gitignore index 44b8b09b..8bf9e7f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,15 @@ +.DS_Store +.idea +*.json +*.out +*.test ~$*.xlsx +test/*.png +test/BadWorkbook.SaveAsEmptyStruct.xlsx +test/Encryption*.xlsx +test/excelize-* test/Test*.xlam test/Test*.xlsm test/Test*.xlsx test/Test*.xltm test/Test*.xltx -# generated files -test/Encryption*.xlsx -test/BadWorkbook.SaveAsEmptyStruct.xlsx -test/*.png -test/excelize-* -*.out -*.test -.idea -.DS_Store diff --git a/cell.go b/cell.go index fbc84b7d..eb604419 100644 --- a/cell.go +++ b/cell.go @@ -902,31 +902,7 @@ func getCellRichText(si *xlsxSI) (runs []RichTextRun) { Text: v.T.Val, } if v.RPr != nil { - font := Font{Underline: "none"} - font.Bold = v.RPr.B != nil - font.Italic = v.RPr.I != nil - if v.RPr.U != nil { - font.Underline = "single" - if v.RPr.U.Val != nil { - font.Underline = *v.RPr.U.Val - } - } - if v.RPr.RFont != nil && v.RPr.RFont.Val != nil { - font.Family = *v.RPr.RFont.Val - } - if v.RPr.Sz != nil && v.RPr.Sz.Val != nil { - font.Size = *v.RPr.Sz.Val - } - font.Strike = v.RPr.Strike != nil - if v.RPr.Color != nil { - font.Color = strings.TrimPrefix(v.RPr.Color.RGB, "FF") - if v.RPr.Color.Theme != nil { - font.ColorTheme = v.RPr.Color.Theme - } - font.ColorIndexed = v.RPr.Color.Indexed - font.ColorTint = v.RPr.Color.Tint - } - run.Font = &font + run.Font = newFont(v.RPr) } runs = append(runs, run) } @@ -985,6 +961,35 @@ func newRpr(fnt *Font) *xlsxRPr { return &rpr } +// newFont create font format by given run properties for the rich text. +func newFont(rPr *xlsxRPr) *Font { + font := Font{Underline: "none"} + font.Bold = rPr.B != nil + font.Italic = rPr.I != nil + if rPr.U != nil { + font.Underline = "single" + if rPr.U.Val != nil { + font.Underline = *rPr.U.Val + } + } + if rPr.RFont != nil && rPr.RFont.Val != nil { + font.Family = *rPr.RFont.Val + } + if rPr.Sz != nil && rPr.Sz.Val != nil { + font.Size = *rPr.Sz.Val + } + font.Strike = rPr.Strike != nil + if rPr.Color != nil { + font.Color = strings.TrimPrefix(rPr.Color.RGB, "FF") + if rPr.Color.Theme != nil { + font.ColorTheme = rPr.Color.Theme + } + font.ColorIndexed = rPr.Color.Indexed + font.ColorTint = rPr.Color.Tint + } + return &font +} + // setRichText provides a function to set rich text of a cell. func setRichText(runs []RichTextRun) ([]xlsxR, error) { var ( diff --git a/comment.go b/comment.go index 3d083246..28c6cf82 100644 --- a/comment.go +++ b/comment.go @@ -13,7 +13,6 @@ package excelize import ( "bytes" - "encoding/json" "encoding/xml" "fmt" "io" @@ -23,17 +22,6 @@ import ( "strings" ) -// parseCommentOptions provides a function to parse the format settings of -// the comment with default value. -func parseCommentOptions(opts string) (*commentOptions, error) { - options := commentOptions{ - Author: "Author:", - Text: " ", - } - err := json.Unmarshal([]byte(opts), &options) - return &options, err -} - // GetComments retrieves all comments and returns a map of worksheet name to // the worksheet comments. func (f *File) GetComments() (comments map[string][]Comment) { @@ -53,14 +41,18 @@ func (f *File) GetComments() (comments map[string][]Comment) { if comment.AuthorID < len(d.Authors.Author) { sheetComment.Author = d.Authors.Author[comment.AuthorID] } - sheetComment.Ref = comment.Ref + sheetComment.Cell = comment.Ref sheetComment.AuthorID = comment.AuthorID if comment.Text.T != nil { sheetComment.Text += *comment.Text.T } for _, text := range comment.Text.R { if text.T != nil { - sheetComment.Text += text.T.Val + run := RichTextRun{Text: text.T.Val} + if text.RPr != nil { + run.Font = newFont(text.RPr) + } + sheetComment.Runs = append(sheetComment.Runs, run) } } sheetComments = append(sheetComments, sheetComment) @@ -92,12 +84,15 @@ func (f *File) getSheetComments(sheetFile string) string { // author length is 255 and the max text length is 32512. For example, add a // comment in Sheet1!$A$30: // -// err := f.AddComment("Sheet1", "A30", `{"author":"Excelize: ","text":"This is a comment."}`) -func (f *File) AddComment(sheet, cell, opts string) error { - options, err := parseCommentOptions(opts) - if err != nil { - return err - } +// err := f.AddComment(sheet, excelize.Comment{ +// Cell: "A12", +// Author: "Excelize", +// Runs: []excelize.RichTextRun{ +// {Text: "Excelize: ", Font: &excelize.Font{Bold: true}}, +// {Text: "This is a comment."}, +// }, +// }) +func (f *File) AddComment(sheet string, comment Comment) error { // Read sheet data. ws, err := f.workSheetReader(sheet) if err != nil { @@ -122,20 +117,19 @@ func (f *File) AddComment(sheet, cell, opts string) error { f.addSheetLegacyDrawing(sheet, rID) } commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml" - var colCount int - for i, l := range strings.Split(options.Text, "\n") { - if ll := len(l); ll > colCount { - if i == 0 { - ll += len(options.Author) + var rows, cols int + for _, runs := range comment.Runs { + for _, subStr := range strings.Split(runs.Text, "\n") { + rows++ + if chars := len(subStr); chars > cols { + cols = chars } - colCount = ll } } - err = f.addDrawingVML(commentID, drawingVML, cell, strings.Count(options.Text, "\n")+1, colCount) - if err != nil { + if err = f.addDrawingVML(commentID, drawingVML, comment.Cell, rows+1, cols); err != nil { return err } - f.addComment(commentsXML, cell, options) + f.addComment(commentsXML, comment) f.addContentTypePart(commentID, "comments") return err } @@ -280,56 +274,59 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount, // addComment provides a function to create chart as xl/comments%d.xml by // given cell and format sets. -func (f *File) addComment(commentsXML, cell string, opts *commentOptions) { - a := opts.Author - t := opts.Text - if len(a) > MaxFieldLength { - a = a[:MaxFieldLength] +func (f *File) addComment(commentsXML string, comment Comment) { + if comment.Author == "" { + comment.Author = "Author" } - if len(t) > 32512 { - t = t[:32512] + if len(comment.Author) > MaxFieldLength { + comment.Author = comment.Author[:MaxFieldLength] } - comments := f.commentsReader(commentsXML) - authorID := 0 + comments, authorID := f.commentsReader(commentsXML), 0 if comments == nil { - comments = &xlsxComments{Authors: xlsxAuthor{Author: []string{opts.Author}}} + comments = &xlsxComments{Authors: xlsxAuthor{Author: []string{comment.Author}}} } - if inStrSlice(comments.Authors.Author, opts.Author, true) == -1 { - comments.Authors.Author = append(comments.Authors.Author, opts.Author) + if inStrSlice(comments.Authors.Author, comment.Author, true) == -1 { + comments.Authors.Author = append(comments.Authors.Author, comment.Author) authorID = len(comments.Authors.Author) - 1 } - defaultFont := f.GetDefaultFont() - bold := "" - cmt := xlsxComment{ - Ref: cell, + defaultFont, chars, cmt := f.GetDefaultFont(), 0, xlsxComment{ + Ref: comment.Cell, AuthorID: authorID, - Text: xlsxText{ - R: []xlsxR{ - { - RPr: &xlsxRPr{ - B: &bold, - Sz: &attrValFloat{Val: float64Ptr(9)}, - Color: &xlsxColor{ - Indexed: 81, - }, - RFont: &attrValString{Val: stringPtr(defaultFont)}, - Family: &attrValInt{Val: intPtr(2)}, - }, - T: &xlsxT{Val: a}, - }, - { - RPr: &xlsxRPr{ - Sz: &attrValFloat{Val: float64Ptr(9)}, - Color: &xlsxColor{ - Indexed: 81, - }, - RFont: &attrValString{Val: stringPtr(defaultFont)}, - Family: &attrValInt{Val: intPtr(2)}, - }, - T: &xlsxT{Val: t}, + Text: xlsxText{R: []xlsxR{}}, + } + if comment.Text != "" { + if len(comment.Text) > TotalCellChars { + comment.Text = comment.Text[:TotalCellChars] + } + cmt.Text.T = stringPtr(comment.Text) + chars += len(comment.Text) + } + for _, run := range comment.Runs { + if chars == TotalCellChars { + break + } + if chars+len(run.Text) > TotalCellChars { + run.Text = run.Text[:TotalCellChars-chars] + } + chars += len(run.Text) + r := xlsxR{ + RPr: &xlsxRPr{ + Sz: &attrValFloat{Val: float64Ptr(9)}, + Color: &xlsxColor{ + Indexed: 81, }, + RFont: &attrValString{Val: stringPtr(defaultFont)}, + Family: &attrValInt{Val: intPtr(2)}, }, - }, + T: &xlsxT{Val: run.Text, Space: xml.Attr{ + Name: xml.Name{Space: NameSpaceXML, Local: "space"}, + Value: "preserve", + }}, + } + if run.Font != nil { + r.RPr = newRpr(run.Font) + } + cmt.Text.R = append(cmt.Text.R, r) } comments.CommentList.Comment = append(comments.CommentList.Comment, cmt) f.Comments[commentsXML] = comments diff --git a/comment_test.go b/comment_test.go index 2beca70c..019dc3b8 100644 --- a/comment_test.go +++ b/comment_test.go @@ -26,14 +26,14 @@ func TestAddComments(t *testing.T) { t.FailNow() } - s := strings.Repeat("c", 32768) - assert.NoError(t, f.AddComment("Sheet1", "A30", `{"author":"`+s+`","text":"`+s+`"}`)) - assert.NoError(t, f.AddComment("Sheet2", "B7", `{"author":"Excelize: ","text":"This is a comment."}`)) + s := strings.Repeat("c", TotalCellChars+1) + assert.NoError(t, f.AddComment("Sheet1", Comment{Cell: "A30", Author: s, Text: s, Runs: []RichTextRun{{Text: s}, {Text: s}}})) + assert.NoError(t, f.AddComment("Sheet2", Comment{Cell: "B7", Author: "Excelize", Text: s[:TotalCellChars-1], Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment."}}})) // Test add comment on not exists worksheet. - assert.EqualError(t, f.AddComment("SheetN", "B7", `{"author":"Excelize: ","text":"This is a comment."}`), "sheet SheetN does not exist") + assert.EqualError(t, f.AddComment("SheetN", Comment{Cell: "B7", Author: "Excelize", Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment."}}}), "sheet SheetN does not exist") // Test add comment on with illegal cell reference - assert.EqualError(t, f.AddComment("Sheet1", "A", `{"author":"Excelize: ","text":"This is a comment."}`), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) + assert.EqualError(t, f.AddComment("Sheet1", Comment{Cell: "A", Author: "Excelize", Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment."}}}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) if assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddComments.xlsx"))) { assert.Len(t, f.GetComments(), 2) } @@ -52,12 +52,12 @@ func TestDeleteComment(t *testing.T) { t.FailNow() } - assert.NoError(t, f.AddComment("Sheet2", "A40", `{"author":"Excelize: ","text":"This is a comment1."}`)) - assert.NoError(t, f.AddComment("Sheet2", "A41", `{"author":"Excelize: ","text":"This is a comment2."}`)) - assert.NoError(t, f.AddComment("Sheet2", "C41", `{"author":"Excelize: ","text":"This is a comment3."}`)) - assert.NoError(t, f.AddComment("Sheet2", "C41", `{"author":"Excelize: ","text":"This is a comment3-1."}`)) - assert.NoError(t, f.AddComment("Sheet2", "C42", `{"author":"Excelize: ","text":"This is a comment4."}`)) - assert.NoError(t, f.AddComment("Sheet2", "C41", `{"author":"Excelize: ","text":"This is a comment3-2."}`)) + assert.NoError(t, f.AddComment("Sheet2", Comment{Cell: "A40", Text: "Excelize: This is a comment1."})) + assert.NoError(t, f.AddComment("Sheet2", Comment{Cell: "A41", Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment2."}}})) + assert.NoError(t, f.AddComment("Sheet2", Comment{Cell: "C41", Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment3."}}})) + assert.NoError(t, f.AddComment("Sheet2", Comment{Cell: "C41", Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment3-1."}}})) + assert.NoError(t, f.AddComment("Sheet2", Comment{Cell: "C42", Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment4."}}})) + assert.NoError(t, f.AddComment("Sheet2", Comment{Cell: "C41", Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment2."}}})) assert.NoError(t, f.DeleteComment("Sheet2", "A40")) diff --git a/excelize_test.go b/excelize_test.go index 4c86d560..74895f53 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -946,8 +946,7 @@ func TestSetDeleteSheet(t *testing.T) { t.FailNow() } f.DeleteSheet("Sheet1") - assert.EqualError(t, f.AddComment("Sheet1", "A1", ""), "unexpected end of JSON input") - assert.NoError(t, f.AddComment("Sheet1", "A1", `{"author":"Excelize: ","text":"This is a comment."}`)) + assert.NoError(t, f.AddComment("Sheet1", Comment{Cell: "A1", Author: "Excelize", Runs: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment."}}})) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetDeleteSheet.TestBook4.xlsx"))) }) } diff --git a/picture.go b/picture.go index 05e4a516..a7c1edb2 100644 --- a/picture.go +++ b/picture.go @@ -183,7 +183,7 @@ func (f *File) AddPictureFromBytes(sheet, cell, opts, name, extension string, fi drawingHyperlinkRID = f.addRels(drawingRels, SourceRelationshipHyperLink, options.Hyperlink, hyperlinkType) } ws.Unlock() - err = f.addDrawingPicture(sheet, drawingXML, cell, name, img.Width, img.Height, drawingRID, drawingHyperlinkRID, options) + err = f.addDrawingPicture(sheet, drawingXML, cell, name, ext, drawingRID, drawingHyperlinkRID, img, options) if err != nil { return err } @@ -263,11 +263,12 @@ func (f *File) countDrawings() (count int) { // addDrawingPicture provides a function to add picture by given sheet, // drawingXML, cell, file name, width, height relationship index and format // sets. -func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, height, rID, hyperlinkRID int, opts *pictureOptions) error { +func (f *File) addDrawingPicture(sheet, drawingXML, cell, file, ext string, rID, hyperlinkRID int, img image.Config, opts *pictureOptions) error { col, row, err := CellNameToCoordinates(cell) if err != nil { return err } + width, height := img.Width, img.Height if opts.Autofit { width, height, col, row, err = f.drawingResize(sheet, cell, float64(width), float64(height), opts) if err != nil { @@ -308,6 +309,19 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, he } pic.BlipFill.Blip.R = SourceRelationship.Value pic.BlipFill.Blip.Embed = "rId" + strconv.Itoa(rID) + if ext == ".svg" { + pic.BlipFill.Blip.ExtList = &xlsxEGOfficeArtExtensionList{ + Ext: []xlsxCTOfficeArtExtension{ + { + URI: ExtURISVG, + SVGBlip: xlsxCTSVGBlip{ + XMLNSaAVG: NameSpaceDrawing2016SVG.Value, + Embed: pic.BlipFill.Blip.Embed, + }, + }, + }, + } + } pic.SpPr.PrstGeom.Prst = "rect" twoCellAnchor.Pic = &pic @@ -362,7 +376,10 @@ func (f *File) addMedia(file []byte, ext string) string { // setContentTypePartImageExtensions provides a function to set the content // type for relationship parts and the Main Document part. func (f *File) setContentTypePartImageExtensions() { - imageTypes := map[string]string{"jpeg": "image/", "png": "image/", "gif": "image/", "tiff": "image/", "emf": "image/x-", "wmf": "image/x-", "emz": "image/x-", "wmz": "image/x-"} + imageTypes := map[string]string{ + "jpeg": "image/", "png": "image/", "gif": "image/", "svg": "image/", "tiff": "image/", + "emf": "image/x-", "wmf": "image/x-", "emz": "image/x-", "wmz": "image/x-", + } content := f.contentTypesReader() content.Lock() defer content.Unlock() diff --git a/picture_test.go b/picture_test.go index e90de20a..c34780f7 100644 --- a/picture_test.go +++ b/picture_test.go @@ -90,10 +90,12 @@ func TestAddPictureErrors(t *testing.T) { image.RegisterFormat("wmf", "", decode, decodeConfig) image.RegisterFormat("emz", "", decode, decodeConfig) image.RegisterFormat("wmz", "", decode, decodeConfig) + image.RegisterFormat("svg", "", decode, decodeConfig) assert.NoError(t, f.AddPicture("Sheet1", "Q1", filepath.Join("test", "images", "excel.emf"), "")) assert.NoError(t, f.AddPicture("Sheet1", "Q7", filepath.Join("test", "images", "excel.wmf"), "")) assert.NoError(t, f.AddPicture("Sheet1", "Q13", filepath.Join("test", "images", "excel.emz"), "")) assert.NoError(t, f.AddPicture("Sheet1", "Q19", filepath.Join("test", "images", "excel.wmz"), "")) + assert.NoError(t, f.AddPicture("Sheet1", "Q25", "excelize.svg", `{"x_scale": 2.1}`)) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddPicture2.xlsx"))) assert.NoError(t, f.Close()) } @@ -175,7 +177,7 @@ func TestGetPicture(t *testing.T) { func TestAddDrawingPicture(t *testing.T) { // Test addDrawingPicture with illegal cell reference. f := NewFile() - assert.EqualError(t, f.addDrawingPicture("sheet1", "", "A", "", 0, 0, 0, 0, nil), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) + assert.EqualError(t, f.addDrawingPicture("sheet1", "", "A", "", "", 0, 0, image.Config{}, nil), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) } func TestAddPictureFromBytes(t *testing.T) { diff --git a/xmlComments.go b/xmlComments.go index 731f416a..7b67e678 100644 --- a/xmlComments.go +++ b/xmlComments.go @@ -72,16 +72,11 @@ type xlsxPhoneticRun struct { T string `xml:"t"` } -// commentOptions directly maps the format settings of the comment. -type commentOptions struct { - Author string `json:"author"` - Text string `json:"text"` -} - // Comment directly maps the comment information. type Comment struct { - Author string `json:"author"` - AuthorID int `json:"author_id"` - Ref string `json:"ref"` - Text string `json:"text"` + Author string `json:"author"` + AuthorID int `json:"author_id"` + Cell string `json:"cell"` + Text string `json:"string"` + Runs []RichTextRun `json:"runs"` } diff --git a/xmlDrawing.go b/xmlDrawing.go index dc48ccc0..56ddc0e7 100644 --- a/xmlDrawing.go +++ b/xmlDrawing.go @@ -29,6 +29,7 @@ var ( NameSpaceDrawingML = xml.Attr{Name: xml.Name{Local: "a", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/drawingml/2006/main"} NameSpaceDrawingMLChart = xml.Attr{Name: xml.Name{Local: "c", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/drawingml/2006/chart"} NameSpaceDrawingMLSpreadSheet = xml.Attr{Name: xml.Name{Local: "xdr", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"} + NameSpaceDrawing2016SVG = xml.Attr{Name: xml.Name{Local: "asvg", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/drawing/2016/SVG/main"} NameSpaceSpreadSheetX15 = xml.Attr{Name: xml.Name{Local: "x15", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/spreadsheetml/2010/11/main"} NameSpaceSpreadSheetExcel2006Main = xml.Attr{Name: xml.Name{Local: "xne", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/excel/2006/main"} NameSpaceMacExcel2008Main = xml.Attr{Name: xml.Name{Local: "mx", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/mac/excel/2008/main"} @@ -95,6 +96,7 @@ const ( ExtURITimelineRefs = "{7E03D99C-DC04-49d9-9315-930204A7B6E9}" ExtURIDrawingBlip = "{28A0092B-C50C-407E-A947-70E740481C1C}" ExtURIMacExcelMX = "{64002731-A6B0-56B0-2670-7721B7C09600}" + ExtURISVG = "{96DAC541-7B7A-43D3-8B79-37D633B846F1}" ) // Excel specifications and limits @@ -163,7 +165,11 @@ var IndexedColorMapping = []string{ } // supportedImageTypes defined supported image types. -var supportedImageTypes = map[string]string{".gif": ".gif", ".jpg": ".jpeg", ".jpeg": ".jpeg", ".png": ".png", ".tif": ".tiff", ".tiff": ".tiff", ".emf": ".emf", ".wmf": ".wmf", ".emz": ".emz", ".wmz": ".wmz"} +var supportedImageTypes = map[string]string{ + ".emf": ".emf", ".emz": ".emz", ".gif": ".gif", ".jpeg": ".jpeg", + ".jpg": ".jpeg", ".png": ".png", ".svg": ".svg", ".tif": ".tiff", + ".tiff": ".tiff", ".wmf": ".wmf", ".wmz": ".wmz", +} // supportedContentTypes defined supported file format types. var supportedContentTypes = map[string]string{ @@ -231,9 +237,10 @@ type xlsxPicLocks struct { // xlsxBlip element specifies the existence of an image (binary large image or // picture) and contains a reference to the image data. type xlsxBlip struct { - Embed string `xml:"r:embed,attr"` - Cstate string `xml:"cstate,attr,omitempty"` - R string `xml:"xmlns:r,attr"` + Embed string `xml:"r:embed,attr"` + Cstate string `xml:"cstate,attr,omitempty"` + R string `xml:"xmlns:r,attr"` + ExtList *xlsxEGOfficeArtExtensionList `xml:"a:extLst"` } // xlsxStretch directly maps the stretch element. This element specifies that a @@ -293,6 +300,28 @@ type xlsxNvPicPr struct { CNvPicPr xlsxCNvPicPr `xml:"xdr:cNvPicPr"` } +// xlsxCTSVGBlip specifies a graphic element in Scalable Vector Graphics (SVG) +// format. +type xlsxCTSVGBlip struct { + XMLNSaAVG string `xml:"xmlns:asvg,attr"` + Embed string `xml:"r:embed,attr"` + Link string `xml:"r:link,attr,omitempty"` +} + +// xlsxCTOfficeArtExtension used for future extensibility and is seen elsewhere +// throughout the drawing area. +type xlsxCTOfficeArtExtension struct { + XMLName xml.Name `xml:"a:ext"` + URI string `xml:"uri,attr"` + SVGBlip xlsxCTSVGBlip `xml:"asvg:svgBlip"` +} + +// xlsxEGOfficeArtExtensionList used for future extensibility and is seen +// elsewhere throughout the drawing area. +type xlsxEGOfficeArtExtensionList struct { + Ext []xlsxCTOfficeArtExtension `xml:"ext"` +} + // xlsxBlipFill directly maps the blipFill (Picture Fill). This element // specifies the kind of picture fill that the picture object has. Because a // picture has a picture fill already by default, it is possible to have two diff --git a/xmlSharedStrings.go b/xmlSharedStrings.go index 3249ecac..7dac5442 100644 --- a/xmlSharedStrings.go +++ b/xmlSharedStrings.go @@ -83,6 +83,6 @@ type xlsxRPr struct { // RichTextRun directly maps the settings of the rich text run. type RichTextRun struct { - Font *Font - Text string + Font *Font `json:"font"` + Text string `json:"text"` }