Skip to content

Commit

Permalink
Initialize comments support & go test updated.
Browse files Browse the repository at this point in the history
  • Loading branch information
xuri committed May 13, 2017
1 parent 66349f8 commit d93a156
Show file tree
Hide file tree
Showing 9 changed files with 477 additions and 7 deletions.
209 changes: 209 additions & 0 deletions comment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
package excelize

import (
"encoding/json"
"encoding/xml"
"strconv"
"strings"
)

// parseFormatCommentsSet provides function to parse the format settings of the
// comment with default value.
func parseFormatCommentsSet(formatSet string) *formatComment {
format := formatComment{
Author: "Author:",
Text: " ",
}
json.Unmarshal([]byte(formatSet), &format)
return &format
}

// AddComment provides the method to add comment in a sheet by given worksheet
// index, cell and format set (such as author and text). For example, add a
// comment in Sheet1!$A$30:
//
// xlsx.AddComment("Sheet1", "A30", `{"author":"Excelize","text":"This is a comment."}`)
//
func (f *File) AddComment(sheet, cell, format string) {
formatSet := parseFormatCommentsSet(format)
// Read sheet data.
xlsx := f.workSheetReader(sheet)
commentID := f.countComments() + 1
drawingVML := "xl/drawings/vmlDrawing" + strconv.Itoa(commentID) + ".vml"
sheetRelationshipsComments := "../comments" + strconv.Itoa(commentID) + ".xml"
sheetRelationshipsDrawingVML := "../drawings/vmlDrawing" + strconv.Itoa(commentID) + ".vml"
if xlsx.LegacyDrawing != nil {
// The worksheet already has a comments relationships, use the relationships drawing ../drawings/vmlDrawing%d.vml.
sheetRelationshipsDrawingVML = f.getSheetRelationshipsTargetByID(sheet, xlsx.LegacyDrawing.RID)
commentID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingVML, "../drawings/vmlDrawing"), ".vml"))
drawingVML = strings.Replace(sheetRelationshipsDrawingVML, "..", "xl", -1)
} else {
// Add first comment for given sheet.
rID := f.addSheetRelationships(sheet, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "")
f.addSheetRelationships(sheet, SourceRelationshipComments, sheetRelationshipsComments, "")
f.addSheetLegacyDrawing(sheet, rID)
}
commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml"
f.addComment(commentsXML, cell, formatSet)
f.addDrawingVML(commentID, drawingVML, cell)
f.addCommentsContentTypePart(commentID)
}

// addDrawingVML provides function to create comment as
// xl/drawings/vmlDrawing%d.vml by given commit ID and cell.
func (f *File) addDrawingVML(commentID int, drawingVML, cell string) {
col := string(strings.Map(letterOnlyMapF, cell))
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, cell))
xAxis := row - 1
yAxis := titleToNumber(col)
vml := vmlDrawing{
XMLNSv: "urn:schemas-microsoft-com:vml",
XMLNSo: "urn:schemas-microsoft-com:office:office",
XMLNSx: "urn:schemas-microsoft-com:office:excel",
XMLNSmv: "http://macVmlSchemaUri",
Shapelayout: &xlsxShapelayout{
Ext: "edit",
IDmap: &xlsxIDmap{
Ext: "edit",
Data: commentID,
},
},
Shapetype: &xlsxShapetype{
ID: "_x0000_t202",
Coordsize: "21600,21600",
Spt: 202,
Path: "m0,0l0,21600,21600,21600,21600,0xe",
Stroke: &xlsxStroke{
Joinstyle: "miter",
},
VPath: &vPath{
Gradientshapeok: "t",
Connecttype: "rect",
},
},
}
sp := encodeShape{
Fill: &vFill{
Color2: "#fbfe82",
Angle: -180,
Type: "gradient",
Fill: &oFill{
Ext: "view",
Type: "gradientUnscaled",
},
},
Shadow: &vShadow{
On: "t",
Color: "black",
Obscured: "t",
},
Path: &vPath{
Connecttype: "none",
},
Textbox: &vTextbox{
Style: "mso-direction-alt:auto",
Div: &xlsxDiv{
Style: "text-align:left",
},
},
ClientData: &xClientData{
ObjectType: "Note",
Anchor: "3, 15, 8, 6, 4, 54, 13, 2",
AutoFill: "False",
Row: xAxis,
Column: yAxis,
},
}
s, _ := xml.Marshal(sp)
shape := xlsxShape{
ID: "_x0000_s1025",
Type: "#_x0000_t202",
Style: "position:absolute;73.5pt;width:108pt;height:59.25pt;z-index:1;visibility:hidden",
Fillcolor: "#fbf6d6",
Strokecolor: "#edeaa1",
Val: string(s[13 : len(s)-14]),
}
c, ok := f.XLSX[drawingVML]
if ok {
d := decodeVmlDrawing{}
xml.Unmarshal([]byte(c), &d)
for _, v := range d.Shape {
s := xlsxShape{
ID: "_x0000_s1025",
Type: "#_x0000_t202",
Style: "position:absolute;73.5pt;width:108pt;height:59.25pt;z-index:1;visibility:hidden",
Fillcolor: "#fbf6d6",
Strokecolor: "#edeaa1",
Val: v.Val,
}
vml.Shape = append(vml.Shape, s)
}
}
vml.Shape = append(vml.Shape, shape)
v, _ := xml.Marshal(vml)
f.XLSX[drawingVML] = string(v)
}

// addComment provides function to create chart as xl/comments%d.xml by given
// cell and format sets.
func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
comments := xlsxComments{
Authors: []xlsxAuthor{
xlsxAuthor{
Author: formatSet.Author,
},
},
}
cmt := xlsxComment{
Ref: cell,
AuthorID: 0,
Text: xlsxText{
R: []xlsxR{
xlsxR{
RPr: &xlsxRPr{
B: " ",
Sz: &attrValInt{Val: 9},
Color: &xlsxColor{
Indexed: 81,
},
RFont: &attrValString{Val: "Calibri"},
Family: &attrValInt{Val: 2},
},
T: formatSet.Author + ": ",
},
xlsxR{
RPr: &xlsxRPr{
Sz: &attrValInt{Val: 9},
Color: &xlsxColor{
Indexed: 81,
},
RFont: &attrValString{Val: "Calibri"},
Family: &attrValInt{Val: 2},
},
T: formatSet.Text,
},
},
},
}
c, ok := f.XLSX[commentsXML]
if ok {
d := xlsxComments{}
xml.Unmarshal([]byte(c), &d)
comments.CommentList.Comment = append(comments.CommentList.Comment, d.CommentList.Comment...)
}
comments.CommentList.Comment = append(comments.CommentList.Comment, cmt)
v, _ := xml.Marshal(comments)
f.saveFileList(commentsXML, string(v))
}

// countComments provides function to get comments files count storage in the
// folder xl.
func (f *File) countComments() int {
count := 0
for k := range f.XLSX {
if strings.Contains(k, "xl/comments") {
count++
}
}
return count
}
13 changes: 13 additions & 0 deletions excelize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,19 @@ func TestAddShape(t *testing.T) {
}
}

func TestAddComments(t *testing.T) {
xlsx, err := OpenFile("./test/Workbook_2.xlsx")
if err != nil {
t.Log(err)
}
xlsx.AddComment("Sheet1", "A30", `{"author":"Excelize","text":"This is first comment."}`)
xlsx.AddComment("Sheet2", "B7", `{"author":"Excelize","text":"This is second comment."}`)
err = xlsx.Save()
if err != nil {
t.Log(err)
}
}

func TestAddChart(t *testing.T) {
xlsx, err := OpenFile("./test/Workbook1.xlsx")
if err != nil {
Expand Down
43 changes: 43 additions & 0 deletions picture.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,15 @@ func (f *File) addSheetRelationships(sheet, relType, target, targetMode string)
return rID
}

// addSheetLegacyDrawing provides function to add legacy drawing element to
// xl/worksheets/sheet%d.xml by given sheet name and relationship index.
func (f *File) addSheetLegacyDrawing(sheet string, rID int) {
xlsx := f.workSheetReader(sheet)
xlsx.LegacyDrawing = &xlsxLegacyDrawing{
RID: "rId" + strconv.Itoa(rID),
}
}

// addSheetDrawing provides function to add drawing element to
// xl/worksheets/sheet%d.xml by given sheet name and relationship index.
func (f *File) addSheetDrawing(sheet string, rID int) {
Expand Down Expand Up @@ -308,6 +317,24 @@ func (f *File) setContentTypePartImageExtensions() {
}
}

// setContentTypePartVMLExtensions provides function to set the content type
// for relationship parts and the Main Document part.
func (f *File) setContentTypePartVMLExtensions() {
vml := false
content := f.contentTypesReader()
for _, v := range content.Defaults {
if v.Extension == "vml" {
vml = true
}
}
if !vml {
content.Defaults = append(content.Defaults, xlsxDefault{
Extension: "vml",
ContentType: "application/vnd.openxmlformats-officedocument.vmlDrawing",
})
}
}

// addDrawingContentTypePart provides function to add image part relationships
// in the file [Content_Types].xml by given drawing index.
func (f *File) addDrawingContentTypePart(index int) {
Expand All @@ -324,6 +351,22 @@ func (f *File) addDrawingContentTypePart(index int) {
})
}

// addCommentsContentTypePart provides function to add comments part
// relationships in the file [Content_Types].xml by given comment index.
func (f *File) addCommentsContentTypePart(index int) {
f.setContentTypePartVMLExtensions()
content := f.contentTypesReader()
for _, v := range content.Overrides {
if v.PartName == "/xl/comments"+strconv.Itoa(index)+".xml" {
return
}
}
content.Overrides = append(content.Overrides, xlsxOverride{
PartName: "/xl/comments" + strconv.Itoa(index) + ".xml",
ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
})
}

// getSheetRelationshipsTargetByID provides function to get Target attribute
// value in xl/worksheets/_rels/sheet%d.xml.rels by given sheet name and
// relationship index.
Expand Down
7 changes: 3 additions & 4 deletions sheet.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,7 @@ func (f *File) CopySheet(from, to int) error {
// target worksheet index.
func (f *File) copySheet(from, to int) {
sheet := f.workSheetReader("sheet" + strconv.Itoa(from))
var worksheet xlsxWorksheet
worksheet = *sheet
worksheet := *sheet
path := "xl/worksheets/sheet" + strconv.Itoa(to) + ".xml"
if len(worksheet.SheetViews.SheetView) > 0 {
worksheet.SheetViews.SheetView[0].TabSelected = false
Expand All @@ -444,7 +443,7 @@ func (f *File) HideSheet(name string) {
content := f.workbookReader()
count := 0
for _, v := range content.Sheets.Sheet {
if v.State != "hidden" {
if v.State != sheetStateHidden {
count++
}
}
Expand All @@ -456,7 +455,7 @@ func (f *File) HideSheet(name string) {
tabSelected = xlsx.SheetViews.SheetView[0].TabSelected
}
if v.Name == name && count > 1 && !tabSelected {
content.Sheets.Sheet[k].State = "hidden"
content.Sheets.Sheet[k].State = sheetStateHidden
}
}
}
Expand Down
Loading

0 comments on commit d93a156

Please sign in to comment.