excelize/comment.go

275 lines
7.7 KiB
Go
Raw Normal View History

2018-09-14 00:35:47 +08:00
/*
Package excelize providing a set of functions that allow you to write to
and read from XLSX files. Support reads and writes XLSX file generated by
Microsoft Excel 2007 and later. Support save file without losing original
charts of XLSX. This library needs Go version 1.8 or later.
Copyright 2016 - 2018 The excelize Authors. All rights reserved. Use of
this source code is governed by a BSD-style license that can be found in
the LICENSE file.
*/
package excelize
import (
"encoding/json"
"encoding/xml"
2018-06-23 19:35:27 +08:00
"fmt"
"strconv"
"strings"
)
2018-08-06 10:21:24 +08:00
// parseFormatCommentsSet provides a function to parse the format settings of
// the comment with default value.
func parseFormatCommentsSet(formatSet string) (*formatComment, error) {
format := formatComment{
Author: "Author:",
Text: " ",
}
err := json.Unmarshal([]byte(formatSet), &format)
return &format, err
}
2018-08-06 10:21:24 +08:00
// GetComments retrieves all comments and returns a map of worksheet name to
// the worksheet comments.
func (f *File) GetComments() (comments map[string][]Comment) {
comments = map[string][]Comment{}
2018-06-30 18:37:14 +08:00
for n := range f.sheetMap {
commentID := f.GetSheetIndex(n)
commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml"
c, ok := f.XLSX[commentsXML]
if ok {
d := xlsxComments{}
xml.Unmarshal([]byte(c), &d)
sheetComments := []Comment{}
for _, comment := range d.CommentList.Comment {
sheetComment := Comment{}
if comment.AuthorID < len(d.Authors) {
sheetComment.Author = d.Authors[comment.AuthorID].Author
}
sheetComment.Ref = comment.Ref
sheetComment.AuthorID = comment.AuthorID
for _, text := range comment.Text.R {
sheetComment.Text += text.T
}
sheetComments = append(sheetComments, sheetComment)
}
comments[n] = sheetComments
2018-06-30 18:37:14 +08:00
}
}
return
}
// AddComment provides the method to add comment in a sheet by given worksheet
// index, cell and format set (such as author and text). Note that the max
// author length is 255 and the max text length is 32512. 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) error {
formatSet, err := parseFormatCommentsSet(format)
if err != nil {
return err
}
// 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)
2018-06-23 19:35:27 +08:00
var colCount int
for i, l := range strings.Split(formatSet.Text, "\n") {
if ll := len(l); ll > colCount {
if i == 0 {
ll += len(formatSet.Author)
}
colCount = ll
}
}
f.addDrawingVML(commentID, drawingVML, cell, strings.Count(formatSet.Text, "\n")+1, colCount)
2017-05-24 14:17:35 +08:00
f.addContentTypePart(commentID, "comments")
return err
}
2018-08-06 10:21:24 +08:00
// addDrawingVML provides a function to create comment as
// xl/drawings/vmlDrawing%d.vml by given commit ID and cell.
2018-06-23 19:35:27 +08:00
func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount, colCount int) {
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",
2018-06-23 19:35:27 +08:00
Connecttype: "miter",
},
},
}
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",
2018-06-23 19:35:27 +08:00
Anchor: fmt.Sprintf(
"%d, 23, %d, 0, %d, %d, %d, 5",
1+yAxis, 1+xAxis, 2+yAxis+lineCount, colCount+yAxis, 2+xAxis+lineCount),
2018-06-23 19:35:27 +08:00
AutoFill: "True",
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)
2018-05-07 16:12:51 +08:00
f.XLSX[drawingVML] = v
}
2018-08-06 10:21:24 +08:00
// 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, formatSet *formatComment) {
a := formatSet.Author
t := formatSet.Text
if len(a) > 255 {
a = a[0:255]
}
if len(t) > 32512 {
t = t[0:32512]
}
comments := xlsxComments{
Authors: []xlsxAuthor{
{
Author: formatSet.Author,
},
},
}
cmt := xlsxComment{
Ref: cell,
AuthorID: 0,
Text: xlsxText{
R: []xlsxR{
{
RPr: &xlsxRPr{
B: " ",
2017-09-01 12:59:15 +08:00
Sz: &attrValFloat{Val: 9},
Color: &xlsxColor{
Indexed: 81,
},
RFont: &attrValString{Val: "Calibri"},
Family: &attrValInt{Val: 2},
},
T: a,
},
{
RPr: &xlsxRPr{
2017-09-01 12:59:15 +08:00
Sz: &attrValFloat{Val: 9},
Color: &xlsxColor{
Indexed: 81,
},
RFont: &attrValString{Val: "Calibri"},
Family: &attrValInt{Val: 2},
},
T: t,
},
},
},
}
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)
2018-05-07 16:12:51 +08:00
f.saveFileList(commentsXML, v)
}
2018-08-06 10:21:24 +08:00
// countComments provides a 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
}