This closes #1204, breaking changes for add comments

- Allowing insert SVG format images
- Unit tests updated
This commit is contained in:
xuri 2022-11-02 08:42:00 +08:00
parent a410b22bdd
commit db2d084ada
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
10 changed files with 181 additions and 137 deletions

18
.gitignore vendored
View File

@ -1,15 +1,15 @@
.DS_Store
.idea
*.json
*.out
*.test
~$*.xlsx ~$*.xlsx
test/*.png
test/BadWorkbook.SaveAsEmptyStruct.xlsx
test/Encryption*.xlsx
test/excelize-*
test/Test*.xlam test/Test*.xlam
test/Test*.xlsm test/Test*.xlsm
test/Test*.xlsx test/Test*.xlsx
test/Test*.xltm test/Test*.xltm
test/Test*.xltx test/Test*.xltx
# generated files
test/Encryption*.xlsx
test/BadWorkbook.SaveAsEmptyStruct.xlsx
test/*.png
test/excelize-*
*.out
*.test
.idea
.DS_Store

55
cell.go
View File

@ -902,31 +902,7 @@ func getCellRichText(si *xlsxSI) (runs []RichTextRun) {
Text: v.T.Val, Text: v.T.Val,
} }
if v.RPr != nil { if v.RPr != nil {
font := Font{Underline: "none"} run.Font = newFont(v.RPr)
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
} }
runs = append(runs, run) runs = append(runs, run)
} }
@ -985,6 +961,35 @@ func newRpr(fnt *Font) *xlsxRPr {
return &rpr 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. // setRichText provides a function to set rich text of a cell.
func setRichText(runs []RichTextRun) ([]xlsxR, error) { func setRichText(runs []RichTextRun) ([]xlsxR, error) {
var ( var (

View File

@ -13,7 +13,6 @@ package excelize
import ( import (
"bytes" "bytes"
"encoding/json"
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"io" "io"
@ -23,17 +22,6 @@ import (
"strings" "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 // GetComments retrieves all comments and returns a map of worksheet name to
// the worksheet comments. // the worksheet comments.
func (f *File) GetComments() (comments map[string][]Comment) { 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) { if comment.AuthorID < len(d.Authors.Author) {
sheetComment.Author = d.Authors.Author[comment.AuthorID] sheetComment.Author = d.Authors.Author[comment.AuthorID]
} }
sheetComment.Ref = comment.Ref sheetComment.Cell = comment.Ref
sheetComment.AuthorID = comment.AuthorID sheetComment.AuthorID = comment.AuthorID
if comment.Text.T != nil { if comment.Text.T != nil {
sheetComment.Text += *comment.Text.T sheetComment.Text += *comment.Text.T
} }
for _, text := range comment.Text.R { for _, text := range comment.Text.R {
if text.T != nil { 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) 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 // author length is 255 and the max text length is 32512. For example, add a
// comment in Sheet1!$A$30: // comment in Sheet1!$A$30:
// //
// err := f.AddComment("Sheet1", "A30", `{"author":"Excelize: ","text":"This is a comment."}`) // err := f.AddComment(sheet, excelize.Comment{
func (f *File) AddComment(sheet, cell, opts string) error { // Cell: "A12",
options, err := parseCommentOptions(opts) // Author: "Excelize",
if err != nil { // Runs: []excelize.RichTextRun{
return err // {Text: "Excelize: ", Font: &excelize.Font{Bold: true}},
} // {Text: "This is a comment."},
// },
// })
func (f *File) AddComment(sheet string, comment Comment) error {
// Read sheet data. // Read sheet data.
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
@ -122,20 +117,19 @@ func (f *File) AddComment(sheet, cell, opts string) error {
f.addSheetLegacyDrawing(sheet, rID) f.addSheetLegacyDrawing(sheet, rID)
} }
commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml" commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml"
var colCount int var rows, cols int
for i, l := range strings.Split(options.Text, "\n") { for _, runs := range comment.Runs {
if ll := len(l); ll > colCount { for _, subStr := range strings.Split(runs.Text, "\n") {
if i == 0 { rows++
ll += len(options.Author) 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 return err
} }
f.addComment(commentsXML, cell, options) f.addComment(commentsXML, comment)
f.addContentTypePart(commentID, "comments") f.addContentTypePart(commentID, "comments")
return err return err
} }
@ -280,44 +274,42 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount,
// addComment provides a function to create chart as xl/comments%d.xml by // addComment provides a function to create chart as xl/comments%d.xml by
// given cell and format sets. // given cell and format sets.
func (f *File) addComment(commentsXML, cell string, opts *commentOptions) { func (f *File) addComment(commentsXML string, comment Comment) {
a := opts.Author if comment.Author == "" {
t := opts.Text comment.Author = "Author"
if len(a) > MaxFieldLength {
a = a[:MaxFieldLength]
} }
if len(t) > 32512 { if len(comment.Author) > MaxFieldLength {
t = t[:32512] comment.Author = comment.Author[:MaxFieldLength]
} }
comments := f.commentsReader(commentsXML) comments, authorID := f.commentsReader(commentsXML), 0
authorID := 0
if comments == nil { 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 { if inStrSlice(comments.Authors.Author, comment.Author, true) == -1 {
comments.Authors.Author = append(comments.Authors.Author, opts.Author) comments.Authors.Author = append(comments.Authors.Author, comment.Author)
authorID = len(comments.Authors.Author) - 1 authorID = len(comments.Authors.Author) - 1
} }
defaultFont := f.GetDefaultFont() defaultFont, chars, cmt := f.GetDefaultFont(), 0, xlsxComment{
bold := "" Ref: comment.Cell,
cmt := xlsxComment{
Ref: cell,
AuthorID: authorID, AuthorID: authorID,
Text: xlsxText{ Text: xlsxText{R: []xlsxR{}},
R: []xlsxR{ }
{ if comment.Text != "" {
RPr: &xlsxRPr{ if len(comment.Text) > TotalCellChars {
B: &bold, comment.Text = comment.Text[:TotalCellChars]
Sz: &attrValFloat{Val: float64Ptr(9)}, }
Color: &xlsxColor{ cmt.Text.T = stringPtr(comment.Text)
Indexed: 81, chars += len(comment.Text)
}, }
RFont: &attrValString{Val: stringPtr(defaultFont)}, for _, run := range comment.Runs {
Family: &attrValInt{Val: intPtr(2)}, if chars == TotalCellChars {
}, break
T: &xlsxT{Val: a}, }
}, if chars+len(run.Text) > TotalCellChars {
{ run.Text = run.Text[:TotalCellChars-chars]
}
chars += len(run.Text)
r := xlsxR{
RPr: &xlsxRPr{ RPr: &xlsxRPr{
Sz: &attrValFloat{Val: float64Ptr(9)}, Sz: &attrValFloat{Val: float64Ptr(9)},
Color: &xlsxColor{ Color: &xlsxColor{
@ -326,10 +318,15 @@ func (f *File) addComment(commentsXML, cell string, opts *commentOptions) {
RFont: &attrValString{Val: stringPtr(defaultFont)}, RFont: &attrValString{Val: stringPtr(defaultFont)},
Family: &attrValInt{Val: intPtr(2)}, Family: &attrValInt{Val: intPtr(2)},
}, },
T: &xlsxT{Val: t}, 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) comments.CommentList.Comment = append(comments.CommentList.Comment, cmt)
f.Comments[commentsXML] = comments f.Comments[commentsXML] = comments

View File

@ -26,14 +26,14 @@ func TestAddComments(t *testing.T) {
t.FailNow() t.FailNow()
} }
s := strings.Repeat("c", 32768) s := strings.Repeat("c", TotalCellChars+1)
assert.NoError(t, f.AddComment("Sheet1", "A30", `{"author":"`+s+`","text":"`+s+`"}`)) 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", "B7", `{"author":"Excelize: ","text":"This is a comment."}`)) 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. // 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 // 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"))) { if assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddComments.xlsx"))) {
assert.Len(t, f.GetComments(), 2) assert.Len(t, f.GetComments(), 2)
} }
@ -52,12 +52,12 @@ func TestDeleteComment(t *testing.T) {
t.FailNow() t.FailNow()
} }
assert.NoError(t, f.AddComment("Sheet2", "A40", `{"author":"Excelize: ","text":"This is a comment1."}`)) assert.NoError(t, f.AddComment("Sheet2", Comment{Cell: "A40", Text: "Excelize: This is a comment1."}))
assert.NoError(t, f.AddComment("Sheet2", "A41", `{"author":"Excelize: ","text":"This is a comment2."}`)) 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", "C41", `{"author":"Excelize: ","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."}}}))
assert.NoError(t, f.AddComment("Sheet2", "C41", `{"author":"Excelize: ","text":"This is a comment3-1."}`)) 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", "C42", `{"author":"Excelize: ","text":"This is a comment4."}`)) 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", "C41", `{"author":"Excelize: ","text":"This is a comment3-2."}`)) 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")) assert.NoError(t, f.DeleteComment("Sheet2", "A40"))

View File

@ -946,8 +946,7 @@ func TestSetDeleteSheet(t *testing.T) {
t.FailNow() t.FailNow()
} }
f.DeleteSheet("Sheet1") f.DeleteSheet("Sheet1")
assert.EqualError(t, f.AddComment("Sheet1", "A1", ""), "unexpected end of JSON input") 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.AddComment("Sheet1", "A1", `{"author":"Excelize: ","text":"This is a comment."}`))
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetDeleteSheet.TestBook4.xlsx"))) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetDeleteSheet.TestBook4.xlsx")))
}) })
} }

View File

@ -183,7 +183,7 @@ func (f *File) AddPictureFromBytes(sheet, cell, opts, name, extension string, fi
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, img.Width, img.Height, drawingRID, drawingHyperlinkRID, options) err = f.addDrawingPicture(sheet, drawingXML, cell, name, ext, drawingRID, drawingHyperlinkRID, img, options)
if err != nil { if err != nil {
return err return err
} }
@ -263,11 +263,12 @@ func (f *File) countDrawings() (count 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 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) col, row, err := CellNameToCoordinates(cell)
if err != nil { if err != nil {
return err return err
} }
width, height := img.Width, img.Height
if opts.Autofit { if opts.Autofit {
width, height, col, row, err = f.drawingResize(sheet, cell, float64(width), float64(height), opts) width, height, col, row, err = f.drawingResize(sheet, cell, float64(width), float64(height), opts)
if err != nil { 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.R = SourceRelationship.Value
pic.BlipFill.Blip.Embed = "rId" + strconv.Itoa(rID) 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" pic.SpPr.PrstGeom.Prst = "rect"
twoCellAnchor.Pic = &pic twoCellAnchor.Pic = &pic
@ -362,7 +376,10 @@ func (f *File) addMedia(file []byte, ext string) string {
// setContentTypePartImageExtensions provides a function to set the content // setContentTypePartImageExtensions provides a function to set the content
// type for relationship parts and the Main Document part. // type for relationship parts and the Main Document part.
func (f *File) setContentTypePartImageExtensions() { 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 := f.contentTypesReader()
content.Lock() content.Lock()
defer content.Unlock() defer content.Unlock()

View File

@ -90,10 +90,12 @@ func TestAddPictureErrors(t *testing.T) {
image.RegisterFormat("wmf", "", decode, decodeConfig) image.RegisterFormat("wmf", "", decode, decodeConfig)
image.RegisterFormat("emz", "", decode, decodeConfig) image.RegisterFormat("emz", "", decode, decodeConfig)
image.RegisterFormat("wmz", "", 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", "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", "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", "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", "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.SaveAs(filepath.Join("test", "TestAddPicture2.xlsx")))
assert.NoError(t, f.Close()) assert.NoError(t, f.Close())
} }
@ -175,7 +177,7 @@ func TestGetPicture(t *testing.T) {
func TestAddDrawingPicture(t *testing.T) { func TestAddDrawingPicture(t *testing.T) {
// Test addDrawingPicture with illegal cell reference. // Test addDrawingPicture with illegal cell reference.
f := NewFile() 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) { func TestAddPictureFromBytes(t *testing.T) {

View File

@ -72,16 +72,11 @@ type xlsxPhoneticRun struct {
T string `xml:"t"` 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. // Comment directly maps the comment information.
type Comment struct { type Comment struct {
Author string `json:"author"` Author string `json:"author"`
AuthorID int `json:"author_id"` AuthorID int `json:"author_id"`
Ref string `json:"ref"` Cell string `json:"cell"`
Text string `json:"text"` Text string `json:"string"`
Runs []RichTextRun `json:"runs"`
} }

View File

@ -29,6 +29,7 @@ var (
NameSpaceDrawingML = xml.Attr{Name: xml.Name{Local: "a", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/drawingml/2006/main"} 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"} 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"} 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"} 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"} 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"} 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}" ExtURITimelineRefs = "{7E03D99C-DC04-49d9-9315-930204A7B6E9}"
ExtURIDrawingBlip = "{28A0092B-C50C-407E-A947-70E740481C1C}" ExtURIDrawingBlip = "{28A0092B-C50C-407E-A947-70E740481C1C}"
ExtURIMacExcelMX = "{64002731-A6B0-56B0-2670-7721B7C09600}" ExtURIMacExcelMX = "{64002731-A6B0-56B0-2670-7721B7C09600}"
ExtURISVG = "{96DAC541-7B7A-43D3-8B79-37D633B846F1}"
) )
// Excel specifications and limits // Excel specifications and limits
@ -163,7 +165,11 @@ var IndexedColorMapping = []string{
} }
// supportedImageTypes defined supported image types. // 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. // supportedContentTypes defined supported file format types.
var supportedContentTypes = map[string]string{ var supportedContentTypes = map[string]string{
@ -234,6 +240,7 @@ type xlsxBlip struct {
Embed string `xml:"r:embed,attr"` Embed string `xml:"r:embed,attr"`
Cstate string `xml:"cstate,attr,omitempty"` Cstate string `xml:"cstate,attr,omitempty"`
R string `xml:"xmlns:r,attr"` R string `xml:"xmlns:r,attr"`
ExtList *xlsxEGOfficeArtExtensionList `xml:"a:extLst"`
} }
// xlsxStretch directly maps the stretch element. This element specifies that a // xlsxStretch directly maps the stretch element. This element specifies that a
@ -293,6 +300,28 @@ type xlsxNvPicPr struct {
CNvPicPr xlsxCNvPicPr `xml:"xdr:cNvPicPr"` 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 // xlsxBlipFill directly maps the blipFill (Picture Fill). This element
// specifies the kind of picture fill that the picture object has. Because a // 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 // picture has a picture fill already by default, it is possible to have two

View File

@ -83,6 +83,6 @@ type xlsxRPr struct {
// RichTextRun directly maps the settings of the rich text run. // RichTextRun directly maps the settings of the rich text run.
type RichTextRun struct { type RichTextRun struct {
Font *Font Font *Font `json:"font"`
Text string Text string `json:"text"`
} }