Skip to content

Commit a880146

Browse files
committed
Ref qax-os#2208, add new Name field in GraphicOptions to support set and get graphic name
- Add new exported error variables ErrMaxGraphicAltTextLength and ErrMaxGraphicNameLength - Add gold sponsors - AddPicture and AddPictureFromBytes functions support set name of picture - AddChart and AddShape functions support set name and alternative text - AddSlicer function support set alternative text - Export 2 constants MaxGraphicAltTextLength and MaxGraphicNameLength - Check graphic name and alternative text length, return error if the length length exceeds the limit - Check and truncate text length in UTF16 - Update unit tests
1 parent 76eeba8 commit a880146

19 files changed

Lines changed: 102 additions & 54 deletions

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,3 +246,7 @@ This program is under the terms of the BSD 3-Clause License. See [https://openso
246246
The Excel logo is a trademark of [Microsoft Corporation](https://aka.ms/trademarks-usage). This artwork is an adaptation.
247247

248248
The Go gopher was created by [Renee French](https://go.dev/doc/gopher/README). Licensed under the [Creative Commons 4.0 Attributions license](http://creativecommons.org/licenses/by/4.0/).
249+
250+
## Gold Sponsors
251+
252+
<a href="https://www.gravityclimate.com" alt="Gravity"><img width="120" src="https://xuri.me/excelize/images/vendor/gravityclimate.com.svg" alt="Gravity"></a>

README_zh.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,3 +246,7 @@ func main() {
246246
Excel 徽标是 [Microsoft Corporation](https://aka.ms/trademarks-usage) 的商标,项目的图片是一种改编。
247247

248248
Go gopher 由 [Renee French](https://go.dev/doc/gopher/README) 创作,遵循 [Creative Commons 4.0 Attributions license](http://creativecommons.org/licenses/by/4.0/) 创作共用授权条款。
249+
250+
## 金牌赞助商
251+
252+
<a href="https://www.gravityclimate.com" alt="Gravity"><img width="120" src="https://xuri.me/excelize/images/vendor/gravityclimate.com.svg" alt="Gravity"></a>

calc.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3824,7 +3824,7 @@ func (fn *formulaFuncs) ARABIC(argsList *list.List) formulaArg {
38243824
return newErrorFormulaArg(formulaErrorVALUE, "ARABIC requires 1 numeric argument")
38253825
}
38263826
text := argsList.Front().Value.(formulaArg).Value()
3827-
if len(text) > MaxFieldLength {
3827+
if countUTF16String(text) > MaxFieldLength {
38283828
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
38293829
}
38303830
text = strings.ToUpper(text)
@@ -14054,9 +14054,9 @@ func (fn *formulaFuncs) leftRight(name string, argsList *list.List) formulaArg {
1405414054
return newStringFormulaArg(text)
1405514055
}
1405614056
// LEFT/RIGHT
14057-
if utf8.RuneCountInString(text) > numChars {
14057+
if countUTF16String(text) > numChars {
1405814058
if name == "LEFT" {
14059-
return newStringFormulaArg(string([]rune(text)[:numChars]))
14059+
return newStringFormulaArg(truncateUTF16Units(text, numChars))
1406014060
}
1406114061
// RIGHT
1406214062
return newStringFormulaArg(string([]rune(text)[utf8.RuneCountInString(text)-numChars:]))
@@ -14072,7 +14072,7 @@ func (fn *formulaFuncs) LEN(argsList *list.List) formulaArg {
1407214072
if argsList.Len() != 1 {
1407314073
return newErrorFormulaArg(formulaErrorVALUE, "LEN requires 1 string argument")
1407414074
}
14075-
return newNumberFormulaArg(float64(utf8.RuneCountInString(argsList.Front().Value.(formulaArg).Value())))
14075+
return newNumberFormulaArg(float64(countUTF16String(argsList.Front().Value.(formulaArg).Value())))
1407614076
}
1407714077

1407814078
// LENB returns the number of bytes used to represent the characters in a text
@@ -14172,7 +14172,7 @@ func (fn *formulaFuncs) mid(name string, argsList *list.List) formulaArg {
1417214172
return newStringFormulaArg(result)
1417314173
}
1417414174
// MID
14175-
textLen := utf8.RuneCountInString(text)
14175+
textLen := countUTF16String(text)
1417614176
if startNum > textLen {
1417714177
return newStringFormulaArg("")
1417814178
}
@@ -15052,7 +15052,7 @@ func matchPattern(findText, withinText string, dbcs bool, startNum int) (int, bo
1505215052
}
1505315053
offset++
1505415054
}
15055-
return offset, utf8.RuneCountInString(withinText) != offset-1
15055+
return offset, countUTF16String(withinText) != offset-1
1505615056
}
1505715057

1505815058
// compareFormulaArg compares the left-hand sides and the right-hand sides'

chart_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,8 @@ func TestDeleteChart(t *testing.T) {
507507
{Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37"},
508508
}
509509
format := GraphicOptions{
510+
Name: "Chart 3",
511+
AltText: "chart",
510512
ScaleX: defaultDrawingScale,
511513
ScaleY: defaultDrawingScale,
512514
OffsetX: 15,

crypt.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ func (e *encryption) encrypt(input []byte) []byte {
358358

359359
// standardKeyEncryption encrypt convert the password to an encryption key.
360360
func (e *encryption) standardKeyEncryption(password string) ([]byte, error) {
361-
if len(password) == 0 || len(password) > MaxFieldLength {
361+
if countUTF16String(password) == 0 || countUTF16String(password) > MaxFieldLength {
362362
return nil, ErrPasswordLengthInvalid
363363
}
364364
var storage cfb
@@ -589,7 +589,7 @@ func randomBytes(n int) ([]byte, error) {
589589
// plaintext password, name of the cryptographic hash algorithm, salt value
590590
// and spin count.
591591
func genISOPasswdHash(passwd, hashAlgorithm, salt string, spinCount int) (hashValue, saltValue string, err error) {
592-
if len(passwd) < 1 || len(passwd) > MaxFieldLength {
592+
if countUTF16String(passwd) < 1 || countUTF16String(passwd) > MaxFieldLength {
593593
err = ErrPasswordLengthInvalid
594594
return
595595
}

drawing.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,8 +1518,9 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
15181518
graphicFrame := xlsxGraphicFrame{
15191519
NvGraphicFramePr: xlsxNvGraphicFramePr{
15201520
CNvPr: &xlsxCNvPr{
1521-
ID: cNvPrID,
1522-
Name: "Chart " + strconv.Itoa(cNvPrID),
1521+
ID: cNvPrID,
1522+
Name: "Chart " + strconv.Itoa(cNvPrID),
1523+
Descr: opts.AltText,
15231524
},
15241525
},
15251526
Graphic: &xlsxGraphic{
@@ -1533,6 +1534,9 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI
15331534
},
15341535
},
15351536
}
1537+
if len(opts.Name) > 0 {
1538+
graphicFrame.NvGraphicFramePr.CNvPr.Name = opts.Name
1539+
}
15361540
graphic, _ := xml.Marshal(graphicFrame)
15371541
twoCellAnchor.GraphicFrame = string(graphic)
15381542
twoCellAnchor.ClientData = &xdrClientData{

errors.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ var (
8383
// ErrNameLength defined the error message on receiving the defined name or
8484
// table name length exceeds the limit.
8585
ErrNameLength = fmt.Errorf("the name length exceeds the %d characters limit", MaxFieldLength)
86+
// ErrMaxGraphicAltTextLength defined the error message on receiving the
87+
// graphic alt text length exceeds the limit.
88+
ErrMaxGraphicAltTextLength = fmt.Errorf("the alt text length exceeds the %d characters limit", MaxGraphicAltTextLength)
89+
// ErrMaxGraphicNameLength defined the error message on receiving the
90+
// graphic name length exceeds the limit.
91+
ErrMaxGraphicNameLength = fmt.Errorf("the name length exceeds the %d characters limit", MaxGraphicNameLength)
8692
// ErrOptionsUnzipSizeLimit defined the error message for receiving
8793
// invalid UnzipSizeLimit and UnzipXMLSizeLimit.
8894
ErrOptionsUnzipSizeLimit = errors.New("the value of UnzipSizeLimit should be greater than or equal to UnzipXMLSizeLimit")

file.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func (f *File) Save(opts ...Options) error {
6969
// SaveAs provides a function to create or update to a spreadsheet at the
7070
// provided path.
7171
func (f *File) SaveAs(name string, opts ...Options) error {
72-
if len(name) > MaxFilePathLength {
72+
if countUTF16String(name) > MaxFilePathLength {
7373
return ErrMaxFilePathLength
7474
}
7575
f.Path = name

picture.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ func (opts *GraphicOptions) parseGraphicOptions(defaults *GraphicOptions) (*Grap
4242
if opts == nil {
4343
return defaults, nil
4444
}
45+
if countUTF16String(opts.AltText) > MaxGraphicAltTextLength {
46+
return defaults, ErrMaxGraphicAltTextLength
47+
}
48+
if countUTF16String(opts.Name) > MaxGraphicNameLength {
49+
return defaults, ErrMaxGraphicNameLength
50+
}
4551
if opts.PrintObject == nil {
4652
opts.PrintObject = boolPtr(true)
4753
}
@@ -411,6 +417,9 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, ext string, rID, hyper
411417
pic.NvPicPr.CNvPr.ID = cNvPrID
412418
pic.NvPicPr.CNvPr.Descr = opts.AltText
413419
pic.NvPicPr.CNvPr.Name = "Picture " + strconv.Itoa(cNvPrID)
420+
if len(opts.Name) > 0 {
421+
pic.NvPicPr.CNvPr.Name = opts.Name
422+
}
414423
if hyperlinkRID != 0 {
415424
pic.NvPicPr.CNvPr.HlinkClick = &xlsxHlinkClick{
416425
R: SourceRelationship.Value,
@@ -703,6 +712,7 @@ func (f *File) extractPictureFromAnchor(drawingRelationships string, a *xdrCellA
703712
}
704713
pic.Format.LockAspectRatio = a.Pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect
705714
pic.Format.AltText = a.Pic.NvPicPr.CNvPr.Descr
715+
pic.Format.Name = a.Pic.NvPicPr.CNvPr.Name
706716
if a.Pic.NvPicPr.CNvPr.HlinkClick != nil {
707717
if drawRel := f.getDrawingRelationships(drawingRelationships, a.Pic.NvPicPr.CNvPr.HlinkClick.RID); drawRel != nil {
708718
pic.Format.Hyperlink = drawRel.Target
@@ -762,6 +772,7 @@ func (f *File) extractPictureFromDecodeAnchor(drawingRelationships string, a *de
762772
}
763773
pic.Format.LockAspectRatio = a.Pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect
764774
pic.Format.AltText = a.Pic.NvPicPr.CNvPr.Descr
775+
pic.Format.Name = a.Pic.NvPicPr.CNvPr.Name
765776
if a.Pic.NvPicPr.CNvPr.HlinkClick != nil {
766777
if drawRel := f.getDrawingRelationships(drawingRelationships, a.Pic.NvPicPr.CNvPr.HlinkClick.RID); drawRel != nil {
767778
pic.Format.Hyperlink = drawRel.Target
@@ -1148,6 +1159,7 @@ func (f *File) getDispImages(sheet, cell string) ([]Picture, error) {
11481159
if buffer, _ := f.Pkg.Load("xl/" + r.Target); buffer != nil {
11491160
pic.File = buffer.([]byte)
11501161
pic.Format.AltText = cellImg.Pic.NvPicPr.CNvPr.Descr
1162+
pic.Format.Name = cellImg.Pic.NvPicPr.CNvPr.Name
11511163
pics = append(pics, pic)
11521164
}
11531165
}

picture_test.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ func TestAddPicture(t *testing.T) {
3737

3838
// Test add picture to worksheet with offset and location hyperlink
3939
opts := []GraphicOptions{
40-
{Hyperlink: "#Sheet2!D8", HyperlinkType: "Location"},
41-
{OffsetX: 10, OffsetY: 10, ScaleX: 0.5, ScaleY: 0.5, Hyperlink: "https://github.com/xuri/excelize", HyperlinkType: "External", Positioning: "oneCell"},
42-
{OffsetX: 10, OffsetY: 10, ScaleX: 0.88, ScaleY: 0.88, Hyperlink: "https://github.com/xuri/excelize", HyperlinkType: "External"},
43-
{PrintObject: boolPtr(true), Locked: boolPtr(true), OffsetX: 200, ScaleX: 1, ScaleY: 1, Positioning: "oneCell"},
44-
{PrintObject: boolPtr(true), Locked: boolPtr(true), AltText: "Excel Logo", LockAspectRatio: true, ScaleX: 1, ScaleY: 1},
40+
{Name: "Picture 4", Hyperlink: "#Sheet2!D8", HyperlinkType: "Location"},
41+
{Name: "Picture 4", OffsetX: 10, OffsetY: 10, ScaleX: 0.5, ScaleY: 0.5, Hyperlink: "https://github.com/xuri/excelize", HyperlinkType: "External", Positioning: "oneCell"},
42+
{Name: "Picture 5", OffsetX: 10, OffsetY: 10, ScaleX: 0.88, ScaleY: 0.88, Hyperlink: "https://github.com/xuri/excelize", HyperlinkType: "External"},
43+
{Name: "Picture 4", PrintObject: boolPtr(true), Locked: boolPtr(true), OffsetX: 200, ScaleX: 1, ScaleY: 1, Positioning: "oneCell"},
44+
{Name: "Picture 9", PrintObject: boolPtr(true), Locked: boolPtr(true), AltText: "Excel Logo", LockAspectRatio: true, ScaleX: 1, ScaleY: 1},
4545
}
4646
assert.NoError(t, f.AddPicture("Sheet2", "I9", filepath.Join("test", "images", "excel.jpg"), &opts[0]))
4747
// Test add picture to worksheet with offset, external hyperlink and positioning
@@ -199,6 +199,12 @@ func TestAddPictureErrors(t *testing.T) {
199199
// Test add picture to worksheet with invalid file data
200200
assert.EqualError(t, f.AddPictureFromBytes("Sheet1", "G21", &Picture{Extension: ".jpg", File: make([]byte, 1), Format: &GraphicOptions{AltText: "Excel Logo"}}), image.ErrFormat.Error())
201201

202+
// Test add picture to worksheet with name length exceeds the limit
203+
assert.EqualError(t, f.AddPicture("Sheet1", "Q25", "excelize.svg", &GraphicOptions{Name: strings.Repeat("s", MaxGraphicNameLength+1)}), ErrMaxGraphicNameLength.Error())
204+
205+
// Test add picture to worksheet with alt text length exceeds the limit
206+
assert.EqualError(t, f.AddPicture("Sheet1", "Q25", "excelize.svg", &GraphicOptions{AltText: strings.Repeat("s", MaxGraphicAltTextLength+1)}), ErrMaxGraphicAltTextLength.Error())
207+
202208
// Test add picture with custom image decoder and encoder
203209
decode := func(r io.Reader) (image.Image, error) { return nil, nil }
204210
decodeConfig := func(r io.Reader) (image.Config, error) { return image.Config{Height: 100, Width: 90}, nil }
@@ -218,6 +224,7 @@ func TestGetPicture(t *testing.T) {
218224
assert.NoError(t, err)
219225
assert.Len(t, pics[0].File, 4718)
220226
assert.Empty(t, pics[0].Format.AltText)
227+
assert.Equal(t, "Picture 2", pics[0].Format.Name)
221228
assert.Equal(t, PictureInsertTypePlaceOverCells, pics[0].InsertType)
222229

223230
f, err = prepareTestBook1()
@@ -369,7 +376,7 @@ func TestAddPictureFromBytes(t *testing.T) {
369376
imgFile, err := os.ReadFile("logo.png")
370377
assert.NoError(t, err, "Unable to load logo for test")
371378

372-
assert.NoError(t, f.AddPictureFromBytes("Sheet1", "A1", &Picture{Extension: ".png", File: imgFile, Format: &GraphicOptions{AltText: "logo"}}))
379+
assert.NoError(t, f.AddPictureFromBytes("Sheet1", "A1", &Picture{Extension: ".png", File: imgFile, Format: &GraphicOptions{AltText: strings.Repeat("\u4e00", MaxGraphicAltTextLength), Name: strings.Repeat("\u4e00", MaxGraphicNameLength)}}))
373380
assert.NoError(t, f.AddPictureFromBytes("Sheet1", "A50", &Picture{Extension: ".png", File: imgFile, Format: &GraphicOptions{AltText: "logo"}}))
374381
imageCount := 0
375382
f.Pkg.Range(func(fileName, v interface{}) bool {

0 commit comments

Comments
 (0)