Go language library for reading and writing Microsoft Excel™ (XLAM / XLSM / XLSX / XLTM / XLTX) spreadsheets
修订版 | 76f336809f5419343702de5b3284d46feb9ed266 (tree) |
---|---|
时间 | 2022-08-20 00:24:13 |
作者 | NaturalGao <43291304+NaturalGao@user...> |
Commiter | GitHub |
@@ -140,6 +140,39 @@ func (f *File) AddComment(sheet, cell, format string) error { | ||
140 | 140 | return err |
141 | 141 | } |
142 | 142 | |
143 | +// DeleteComment provides the method to delete comment in a sheet by given | |
144 | +// worksheet. For example, delete the comment in Sheet1!$A$30: | |
145 | +// | |
146 | +// err := f.DeleteComment("Sheet1", "A30") | |
147 | +func (f *File) DeleteComment(sheet, cell string) (err error) { | |
148 | + sheetXMLPath, ok := f.getSheetXMLPath(sheet) | |
149 | + if !ok { | |
150 | + err = newNoExistSheetError(sheet) | |
151 | + return | |
152 | + } | |
153 | + commentsXML := f.getSheetComments(filepath.Base(sheetXMLPath)) | |
154 | + if !strings.HasPrefix(commentsXML, "/") { | |
155 | + commentsXML = "xl" + strings.TrimPrefix(commentsXML, "..") | |
156 | + } | |
157 | + commentsXML = strings.TrimPrefix(commentsXML, "/") | |
158 | + if comments := f.commentsReader(commentsXML); comments != nil { | |
159 | + for i, cmt := range comments.CommentList.Comment { | |
160 | + if cmt.Ref == cell { | |
161 | + if len(comments.CommentList.Comment) > 1 { | |
162 | + comments.CommentList.Comment = append( | |
163 | + comments.CommentList.Comment[:i], | |
164 | + comments.CommentList.Comment[i+1:]..., | |
165 | + ) | |
166 | + continue | |
167 | + } | |
168 | + comments.CommentList.Comment = nil | |
169 | + } | |
170 | + } | |
171 | + f.Comments[commentsXML] = comments | |
172 | + } | |
173 | + return | |
174 | +} | |
175 | + | |
143 | 176 | // addDrawingVML provides a function to create comment as |
144 | 177 | // xl/drawings/vmlDrawing%d.vml by given commit ID and cell. |
145 | 178 | func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount, colCount int) error { |
@@ -46,6 +46,32 @@ func TestAddComments(t *testing.T) { | ||
46 | 46 | assert.EqualValues(t, len(NewFile().GetComments()), 0) |
47 | 47 | } |
48 | 48 | |
49 | +func TestDeleteComment(t *testing.T) { | |
50 | + f, err := prepareTestBook1() | |
51 | + if !assert.NoError(t, err) { | |
52 | + t.FailNow() | |
53 | + } | |
54 | + | |
55 | + assert.NoError(t, f.AddComment("Sheet2", "A40", `{"author":"Excelize: ","text":"This is a comment1."}`)) | |
56 | + assert.NoError(t, f.AddComment("Sheet2", "A41", `{"author":"Excelize: ","text":"This is a comment2."}`)) | |
57 | + assert.NoError(t, f.AddComment("Sheet2", "C41", `{"author":"Excelize: ","text":"This is a comment3."}`)) | |
58 | + | |
59 | + assert.NoError(t, f.DeleteComment("Sheet2", "A40")) | |
60 | + | |
61 | + assert.EqualValues(t, 2, len(f.GetComments()["Sheet2"])) | |
62 | + assert.EqualValues(t, len(NewFile().GetComments()), 0) | |
63 | + | |
64 | + // Test delete all comments in a worksheet | |
65 | + assert.NoError(t, f.DeleteComment("Sheet2", "A41")) | |
66 | + assert.NoError(t, f.DeleteComment("Sheet2", "C41")) | |
67 | + assert.EqualValues(t, 0, len(f.GetComments()["Sheet2"])) | |
68 | + // Test delete comment on not exists worksheet | |
69 | + assert.EqualError(t, f.DeleteComment("SheetN", "A1"), "sheet SheetN is not exist") | |
70 | + // Test delete comment with worksheet part | |
71 | + f.Pkg.Delete("xl/worksheets/sheet1.xml") | |
72 | + assert.NoError(t, f.DeleteComment("Sheet1", "A22")) | |
73 | +} | |
74 | + | |
49 | 75 | func TestDecodeVMLDrawingReader(t *testing.T) { |
50 | 76 | f := NewFile() |
51 | 77 | path := "xl/drawings/vmlDrawing1.xml" |
@@ -14,7 +14,6 @@ package excelize | ||
14 | 14 | import ( |
15 | 15 | "bytes" |
16 | 16 | "encoding/xml" |
17 | - "fmt" | |
18 | 17 | "io" |
19 | 18 | "reflect" |
20 | 19 | ) |
@@ -76,7 +75,7 @@ func (f *File) SetAppProps(appProperties *AppProperties) (err error) { | ||
76 | 75 | app = new(xlsxProperties) |
77 | 76 | if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsApp)))). |
78 | 77 | Decode(app); err != nil && err != io.EOF { |
79 | - err = fmt.Errorf("xml decode error: %s", err) | |
78 | + err = newDecodeXMLError(err) | |
80 | 79 | return |
81 | 80 | } |
82 | 81 | fields = []string{"Application", "ScaleCrop", "DocSecurity", "Company", "LinksUpToDate", "HyperlinksChanged", "AppVersion"} |
@@ -103,7 +102,7 @@ func (f *File) GetAppProps() (ret *AppProperties, err error) { | ||
103 | 102 | app := new(xlsxProperties) |
104 | 103 | if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsApp)))). |
105 | 104 | Decode(app); err != nil && err != io.EOF { |
106 | - err = fmt.Errorf("xml decode error: %s", err) | |
105 | + err = newDecodeXMLError(err) | |
107 | 106 | return |
108 | 107 | } |
109 | 108 | ret, err = &AppProperties{ |
@@ -181,7 +180,7 @@ func (f *File) SetDocProps(docProperties *DocProperties) (err error) { | ||
181 | 180 | core = new(decodeCoreProperties) |
182 | 181 | if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsCore)))). |
183 | 182 | Decode(core); err != nil && err != io.EOF { |
184 | - err = fmt.Errorf("xml decode error: %s", err) | |
183 | + err = newDecodeXMLError(err) | |
185 | 184 | return |
186 | 185 | } |
187 | 186 | newProps, err = &xlsxCoreProperties{ |
@@ -236,7 +235,7 @@ func (f *File) GetDocProps() (ret *DocProperties, err error) { | ||
236 | 235 | |
237 | 236 | if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsCore)))). |
238 | 237 | Decode(core); err != nil && err != io.EOF { |
239 | - err = fmt.Errorf("xml decode error: %s", err) | |
238 | + err = newDecodeXMLError(err) | |
240 | 239 | return |
241 | 240 | } |
242 | 241 | ret, err = &DocProperties{ |
@@ -14,7 +14,6 @@ package excelize | ||
14 | 14 | import ( |
15 | 15 | "bytes" |
16 | 16 | "encoding/xml" |
17 | - "fmt" | |
18 | 17 | "io" |
19 | 18 | "log" |
20 | 19 | "reflect" |
@@ -1322,7 +1321,7 @@ func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (err | ||
1322 | 1321 | deTwoCellAnchor = new(decodeTwoCellAnchor) |
1323 | 1322 | if err = f.xmlNewDecoder(strings.NewReader("<decodeTwoCellAnchor>" + wsDr.TwoCellAnchor[idx].GraphicFrame + "</decodeTwoCellAnchor>")). |
1324 | 1323 | Decode(deTwoCellAnchor); err != nil && err != io.EOF { |
1325 | - err = fmt.Errorf("xml decode error: %s", err) | |
1324 | + err = newDecodeXMLError(err) | |
1326 | 1325 | return |
1327 | 1326 | } |
1328 | 1327 | if err = nil; deTwoCellAnchor.From != nil && decodeTwoCellAnchorFuncs[drawingType](deTwoCellAnchor) { |
@@ -70,6 +70,23 @@ func newCellNameToCoordinatesError(cell string, err error) error { | ||
70 | 70 | return fmt.Errorf("cannot convert cell %q to coordinates: %v", cell, err) |
71 | 71 | } |
72 | 72 | |
73 | +// newNoExistSheetError defined the error message on receiving the not exist | |
74 | +// sheet name. | |
75 | +func newNoExistSheetError(name string) error { | |
76 | + return fmt.Errorf("sheet %s is not exist", name) | |
77 | +} | |
78 | + | |
79 | +// newNotWorksheetError defined the error message on receiving a sheet which | |
80 | +// not a worksheet. | |
81 | +func newNotWorksheetError(name string) error { | |
82 | + return fmt.Errorf("sheet %s is not a worksheet", name) | |
83 | +} | |
84 | + | |
85 | +// newDecodeXMLError defined the error message on decode XML error. | |
86 | +func newDecodeXMLError(err error) error { | |
87 | + return fmt.Errorf("xml decode error: %s", err) | |
88 | +} | |
89 | + | |
73 | 90 | var ( |
74 | 91 | // ErrStreamSetColWidth defined the error message on set column width in |
75 | 92 | // stream writing mode. |
@@ -231,7 +231,7 @@ func (f *File) workSheetReader(sheet string) (ws *xlsxWorksheet, err error) { | ||
231 | 231 | ok bool |
232 | 232 | ) |
233 | 233 | if name, ok = f.getSheetXMLPath(sheet); !ok { |
234 | - err = fmt.Errorf("sheet %s is not exist", sheet) | |
234 | + err = newNoExistSheetError(sheet) | |
235 | 235 | return |
236 | 236 | } |
237 | 237 | if worksheet, ok := f.Sheet.Load(name); ok && worksheet != nil { |
@@ -240,7 +240,7 @@ func (f *File) workSheetReader(sheet string) (ws *xlsxWorksheet, err error) { | ||
240 | 240 | } |
241 | 241 | for _, sheetType := range []string{"xl/chartsheets", "xl/dialogsheet", "xl/macrosheet"} { |
242 | 242 | if strings.HasPrefix(name, sheetType) { |
243 | - err = fmt.Errorf("sheet %s is not a worksheet", sheet) | |
243 | + err = newNotWorksheetError(sheet) | |
244 | 244 | return |
245 | 245 | } |
246 | 246 | } |
@@ -251,7 +251,7 @@ func (f *File) workSheetReader(sheet string) (ws *xlsxWorksheet, err error) { | ||
251 | 251 | } |
252 | 252 | if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readBytes(name)))). |
253 | 253 | Decode(ws); err != nil && err != io.EOF { |
254 | - err = fmt.Errorf("xml decode error: %s", err) | |
254 | + err = newDecodeXMLError(err) | |
255 | 255 | return |
256 | 256 | } |
257 | 257 | err = nil |
@@ -424,7 +424,7 @@ func (f *File) UpdateLinkedValue() error { | ||
424 | 424 | for _, name := range f.GetSheetList() { |
425 | 425 | ws, err := f.workSheetReader(name) |
426 | 426 | if err != nil { |
427 | - if err.Error() == fmt.Sprintf("sheet %s is not a worksheet", trimSheetName(name)) { | |
427 | + if err.Error() == newNotWorksheetError(name).Error() { | |
428 | 428 | continue |
429 | 429 | } |
430 | 430 | return err |
@@ -15,7 +15,6 @@ import ( | ||
15 | 15 | "bytes" |
16 | 16 | "encoding/json" |
17 | 17 | "encoding/xml" |
18 | - "fmt" | |
19 | 18 | "image" |
20 | 19 | "io" |
21 | 20 | "io/ioutil" |
@@ -554,7 +553,7 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) | ||
554 | 553 | deWsDr = new(decodeWsDr) |
555 | 554 | if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(drawingXML)))). |
556 | 555 | Decode(deWsDr); err != nil && err != io.EOF { |
557 | - err = fmt.Errorf("xml decode error: %s", err) | |
556 | + err = newDecodeXMLError(err) | |
558 | 557 | return |
559 | 558 | } |
560 | 559 | err = nil |
@@ -562,7 +561,7 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) | ||
562 | 561 | deTwoCellAnchor = new(decodeTwoCellAnchor) |
563 | 562 | if err = f.xmlNewDecoder(strings.NewReader("<decodeTwoCellAnchor>" + anchor.Content + "</decodeTwoCellAnchor>")). |
564 | 563 | Decode(deTwoCellAnchor); err != nil && err != io.EOF { |
565 | - err = fmt.Errorf("xml decode error: %s", err) | |
564 | + err = newDecodeXMLError(err) | |
566 | 565 | return |
567 | 566 | } |
568 | 567 | if err = nil; deTwoCellAnchor.From != nil && deTwoCellAnchor.Pic != nil { |
@@ -92,7 +92,7 @@ type StreamWriter struct { | ||
92 | 92 | func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) { |
93 | 93 | sheetID := f.getSheetID(sheet) |
94 | 94 | if sheetID == -1 { |
95 | - return nil, fmt.Errorf("sheet %s is not exist", sheet) | |
95 | + return nil, newNoExistSheetError(sheet) | |
96 | 96 | } |
97 | 97 | sw := &StreamWriter{ |
98 | 98 | File: f, |