Skip to content

Commit a07c8cd

Browse files
committed
This closes #1588, closes #1591, breaking changes for the AddChart function
- Removed exported `ChartTitle` data type - The `AddChart` function now supports formatting and setting rich text titles for the chart - New exported function `GetFormControl` for getting form control - Made case in-sensitive for internal worksheet XML path to improve compatibility - Update the unit tests - Update the documentation and internal comments on the codes
1 parent 2e9c290 commit a07c8cd

File tree

12 files changed

+352
-212
lines changed

12 files changed

+352
-212
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,10 @@ func main() {
165165
Categories: "Sheet1!$B$1:$D$1",
166166
Values: "Sheet1!$B$4:$D$4",
167167
}},
168-
Title: excelize.ChartTitle{
169-
Name: "Fruit 3D Clustered Column Chart",
168+
Title: []excelize.RichTextRun{
169+
{
170+
Text: "Fruit 3D Clustered Column Chart",
171+
},
170172
},
171173
}); err != nil {
172174
fmt.Println(err)

README_zh.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,10 @@ func main() {
165165
Categories: "Sheet1!$B$1:$D$1",
166166
Values: "Sheet1!$B$4:$D$4",
167167
}},
168-
Title: excelize.ChartTitle{
169-
Name: "Fruit 3D Clustered Column Chart",
168+
Title: []excelize.RichTextRun{
169+
{
170+
Text: "Fruit 3D Clustered Column Chart",
171+
},
170172
},
171173
}); err != nil {
172174
fmt.Println(err)

chart.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -507,8 +507,16 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
507507
if opts.Legend.Position == "" {
508508
opts.Legend.Position = defaultChartLegendPosition
509509
}
510-
if opts.Title.Name == "" {
511-
opts.Title.Name = " "
510+
for i := range opts.Title {
511+
if opts.Title[i].Font == nil {
512+
opts.Title[i].Font = &Font{}
513+
}
514+
if opts.Title[i].Font.Color == "" {
515+
opts.Title[i].Font.Color = "595959"
516+
}
517+
if opts.Title[i].Font.Size == 0 {
518+
opts.Title[i].Font.Size = 14
519+
}
512520
}
513521
if opts.VaryColors == nil {
514522
opts.VaryColors = boolPtr(true)
@@ -569,8 +577,10 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
569577
// Values: "Sheet1!$B$4:$D$4",
570578
// },
571579
// },
572-
// Title: excelize.ChartTitle{
573-
// Name: "Fruit 3D Clustered Column Chart",
580+
// Title: []excelize.RichTextRun{
581+
// {
582+
// Text: "Fruit 3D Clustered Column Chart",
583+
// },
574584
// },
575585
// Legend: excelize.ChartLegend{
576586
// ShowLegendKey: false,
@@ -727,7 +737,7 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
727737
//
728738
// Title
729739
//
730-
// Name: Set the name (title) for the chart. The name is displayed above the
740+
// Title: Set the name (title) for the chart. The name is displayed above the
731741
// chart. The name can also be a formula such as Sheet1!$A$1 or a list with a
732742
// sheet name. The name property is optional. The default is to have no chart
733743
// title.
@@ -912,8 +922,10 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
912922
// LockAspectRatio: false,
913923
// Locked: &disable,
914924
// },
915-
// Title: excelize.ChartTitle{
916-
// Name: "Clustered Column - Line Chart",
925+
// Title: []excelize.RichTextRun{
926+
// {
927+
// Text: "Clustered Column - Line Chart",
928+
// },
917929
// },
918930
// Legend: excelize.ChartLegend{
919931
// Position: "left",

chart_test.go

Lines changed: 75 additions & 75 deletions
Large diffs are not rendered by default.

drawing.go

Lines changed: 1 addition & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -63,67 +63,7 @@ func (f *File) addChart(opts *Chart, comboCharts []*Chart) {
6363
Lang: &attrValString{Val: stringPtr("en-US")},
6464
RoundedCorners: &attrValBool{Val: boolPtr(false)},
6565
Chart: cChart{
66-
Title: &cTitle{
67-
Tx: cTx{
68-
Rich: &cRich{
69-
P: []aP{
70-
{
71-
PPr: &aPPr{
72-
DefRPr: aRPr{
73-
Kern: 1200,
74-
Strike: "noStrike",
75-
U: "none",
76-
Sz: 1400,
77-
SolidFill: &aSolidFill{
78-
SchemeClr: &aSchemeClr{
79-
Val: "tx1",
80-
LumMod: &attrValInt{
81-
Val: intPtr(65000),
82-
},
83-
LumOff: &attrValInt{
84-
Val: intPtr(35000),
85-
},
86-
},
87-
},
88-
Ea: &aEa{
89-
Typeface: "+mn-ea",
90-
},
91-
Cs: &aCs{
92-
Typeface: "+mn-cs",
93-
},
94-
Latin: &xlsxCTTextFont{
95-
Typeface: "+mn-lt",
96-
},
97-
},
98-
},
99-
R: &aR{
100-
RPr: aRPr{
101-
Lang: "en-US",
102-
AltLang: "en-US",
103-
},
104-
T: opts.Title.Name,
105-
},
106-
},
107-
},
108-
},
109-
},
110-
TxPr: cTxPr{
111-
P: aP{
112-
PPr: &aPPr{
113-
DefRPr: aRPr{
114-
Kern: 1200,
115-
U: "none",
116-
Sz: 14000,
117-
Strike: "noStrike",
118-
},
119-
},
120-
EndParaRPr: &aEndParaRPr{
121-
Lang: "en-US",
122-
},
123-
},
124-
},
125-
Overlay: &attrValBool{Val: boolPtr(false)},
126-
},
66+
Title: f.drawPlotAreaTitles(opts.Title, ""),
12767
View3D: &cView3D{
12868
RotX: &attrValInt{Val: intPtr(chartView3DRotX[opts.Type])},
12969
RotY: &attrValInt{Val: intPtr(chartView3DRotY[opts.Type])},

lib.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func (f *File) ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
5353
continue
5454
}
5555
}
56-
if strings.HasPrefix(fileName, "xl/worksheets/sheet") {
56+
if strings.HasPrefix(strings.ToLower(fileName), "xl/worksheets/sheet") {
5757
worksheets++
5858
if fileSize > f.options.UnzipXMLSizeLimit && !v.FileInfo().IsDir() {
5959
if tempFile, err := f.unzipToTemp(v); err == nil {

picture.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ func (f *File) AddPictureFromBytes(sheet, cell string, pic *Picture) error {
216216
if err != nil {
217217
return err
218218
}
219-
// Read sheet data.
219+
// Read sheet data
220220
f.mu.Lock()
221221
ws, err := f.workSheetReader(sheet)
222222
if err != nil {

shape.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ func (f *File) AddShape(sheet string, opts *Shape) error {
293293
if err != nil {
294294
return err
295295
}
296-
// Read sheet data.
296+
// Read sheet data
297297
ws, err := f.workSheetReader(sheet)
298298
if err != nil {
299299
return err

vml.go

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ func (f *File) DeleteFormControl(sheet, cell string) error {
390390
VPath: &vPath{GradientShapeOK: "t", ConnectType: "rect"},
391391
},
392392
}
393-
// load exist VML shapes from xl/drawings/vmlDrawing%d.vml
393+
// Load exist VML shapes from xl/drawings/vmlDrawing%d.vml
394394
d, err := f.decodeVMLDrawingReader(drawingVML)
395395
if err != nil {
396396
return err
@@ -477,7 +477,7 @@ func (f *File) vmlDrawingWriter() {
477477
// addVMLObject provides a function to create VML drawing parts and
478478
// relationships for comments and form controls.
479479
func (f *File) addVMLObject(opts vmlOptions) error {
480-
// Read sheet data.
480+
// Read sheet data
481481
ws, err := f.workSheetReader(opts.sheet)
482482
if err != nil {
483483
return err
@@ -836,7 +836,7 @@ func (f *File) addDrawingVML(dataID int, drawingVML string, opts *vmlOptions) er
836836
VPath: &vPath{GradientShapeOK: "t", ConnectType: "rect"},
837837
},
838838
}
839-
// load exist VML shapes from xl/drawings/vmlDrawing%d.vml
839+
// Load exist VML shapes from xl/drawings/vmlDrawing%d.vml
840840
d, err := f.decodeVMLDrawingReader(drawingVML)
841841
if err != nil {
842842
return err
@@ -883,3 +883,87 @@ func (f *File) addDrawingVML(dataID int, drawingVML string, opts *vmlOptions) er
883883
f.VMLDrawing[drawingVML] = vml
884884
return err
885885
}
886+
887+
// GetFormControls retrieves all form controls in a worksheet by a given
888+
// worksheet name. Note that, this function does not support getting the width,
889+
// height, text, rich text, and format currently.
890+
func (f *File) GetFormControls(sheet string) ([]FormControl, error) {
891+
var formControls []FormControl
892+
// Read sheet data
893+
ws, err := f.workSheetReader(sheet)
894+
if err != nil {
895+
return formControls, err
896+
}
897+
if ws.LegacyDrawing == nil {
898+
return formControls, err
899+
}
900+
target := f.getSheetRelationshipsTargetByID(sheet, ws.LegacyDrawing.RID)
901+
drawingVML := strings.ReplaceAll(target, "..", "xl")
902+
vml := f.VMLDrawing[drawingVML]
903+
if vml == nil {
904+
// Load exist VML shapes from xl/drawings/vmlDrawing%d.vml
905+
d, err := f.decodeVMLDrawingReader(drawingVML)
906+
if err != nil {
907+
return formControls, err
908+
}
909+
for _, sp := range d.Shape {
910+
if sp.Type != "#_x0000_t201" {
911+
continue
912+
}
913+
formControl, err := extractFormControl(sp.Val)
914+
if err != nil {
915+
return formControls, err
916+
}
917+
if formControl.Type == FormControlNote || formControl.Cell == "" {
918+
continue
919+
}
920+
formControls = append(formControls, formControl)
921+
}
922+
return formControls, err
923+
}
924+
for _, sp := range vml.Shape {
925+
if sp.Type != "#_x0000_t201" {
926+
continue
927+
}
928+
formControl, err := extractFormControl(sp.Val)
929+
if err != nil {
930+
return formControls, err
931+
}
932+
if formControl.Type == FormControlNote || formControl.Cell == "" {
933+
continue
934+
}
935+
formControls = append(formControls, formControl)
936+
}
937+
return formControls, err
938+
}
939+
940+
// extractFormControl provides a function to extract form controls for a
941+
// worksheets by given client data.
942+
func extractFormControl(clientData string) (FormControl, error) {
943+
var (
944+
err error
945+
formControl FormControl
946+
shapeVal decodeShapeVal
947+
)
948+
if err = xml.Unmarshal([]byte(fmt.Sprintf("<shape>%s</shape>", clientData)), &shapeVal); err != nil {
949+
return formControl, err
950+
}
951+
for formCtrlType, preset := range formCtrlPresets {
952+
if shapeVal.ClientData.ObjectType == preset.objectType {
953+
formControl.Type = formCtrlType
954+
if formControl.Cell, err = CoordinatesToCellName(shapeVal.ClientData.Column+1, shapeVal.ClientData.Row+1); err != nil {
955+
return formControl, err
956+
}
957+
formControl.Macro = shapeVal.ClientData.FmlaMacro
958+
formControl.Checked = shapeVal.ClientData.Checked != 0
959+
formControl.CellLink = shapeVal.ClientData.FmlaLink
960+
formControl.CurrentVal = shapeVal.ClientData.Val
961+
formControl.MinVal = shapeVal.ClientData.Min
962+
formControl.MaxVal = shapeVal.ClientData.Max
963+
formControl.IncChange = shapeVal.ClientData.Inc
964+
formControl.PageChange = shapeVal.ClientData.Page
965+
formControl.Horizontally = shapeVal.ClientData.Horiz != nil
966+
}
967+
}
968+
return formControl, err
969+
}

vmlDrawing.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,17 @@ type decodeShapeVal struct {
192192
// element in the file xl/drawings/vmlDrawing%d.vml.
193193
type decodeVMLClientData struct {
194194
ObjectType string `xml:"ObjectType,attr"`
195+
FmlaMacro string
195196
Column int
196197
Row int
198+
Checked int
199+
FmlaLink string
200+
Val uint
201+
Min uint
202+
Max uint
203+
Inc uint
204+
Page uint
205+
Horiz *string
197206
}
198207

199208
// encodeShape defines the structure used to re-serialization shape element.

0 commit comments

Comments
 (0)