Skip to content

Commit f8aa3ad

Browse files
committed
This closes qax-os#1553, the AddChart function support set primary titles
- Update unit tests and documentation - Lint issues fixed
1 parent 9bc3fd7 commit f8aa3ad

6 files changed

+92
-43
lines changed

chart.go

+16-2
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,8 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
794794
// Maximum
795795
// Minimum
796796
// Font
797+
// NumFmt
798+
// Title
797799
//
798800
// The properties of 'YAxis' that can be set are:
799801
//
@@ -805,6 +807,9 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
805807
// Maximum
806808
// Minimum
807809
// Font
810+
// LogBase
811+
// NumFmt
812+
// Title
808813
//
809814
// None: Disable axes.
810815
//
@@ -813,14 +818,14 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
813818
// MinorGridLines: Specifies minor grid lines.
814819
//
815820
// MajorUnit: Specifies the distance between major ticks. Shall contain a
816-
// positive floating-point number. The MajorUnit property is optional. The
821+
// positive floating-point number. The 'MajorUnit' property is optional. The
817822
// default value is auto.
818823
//
819824
// TickLabelSkip: Specifies how many tick labels to skip between label that is
820825
// drawn. The 'TickLabelSkip' property is optional. The default value is auto.
821826
//
822827
// ReverseOrder: Specifies that the categories or values on reverse order
823-
// (orientation of the chart). The ReverseOrder property is optional. The
828+
// (orientation of the chart). The 'ReverseOrder' property is optional. The
824829
// default value is false.
825830
//
826831
// Maximum: Specifies that the fixed maximum, 0 is auto. The 'Maximum' property
@@ -841,6 +846,15 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
841846
// Color
842847
// VertAlign
843848
//
849+
// LogBase: Specifies logarithmic scale for the YAxis.
850+
//
851+
// NumFmt: Specifies that if linked to source and set custom number format code
852+
// for axis. The 'NumFmt' property is optional. The default format code is
853+
// 'General'.
854+
//
855+
// Title: Specifies that the primary horizontal or vertical axis title. The
856+
// 'Title' property is optional.
857+
//
844858
// Set chart size by 'Dimension' property. The 'Dimension' property is optional.
845859
// The default width is 480, and height is 290.
846860
//

chart_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ func TestAddChart(t *testing.T) {
206206
sheetName, cell string
207207
opts *Chart
208208
}{
209-
{sheetName: "Sheet1", cell: "P1", opts: &Chart{Type: Col, Series: series, Format: format, Legend: ChartLegend{Position: "none", ShowLegendKey: true}, Title: ChartTitle{Name: "2D Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{Font: Font{Bold: true, Italic: true, Underline: "dbl", Color: "000000"}}, YAxis: ChartAxis{Font: Font{Bold: false, Italic: false, Underline: "sng", Color: "777777"}}}},
209+
{sheetName: "Sheet1", cell: "P1", opts: &Chart{Type: Col, Series: series, Format: format, Legend: ChartLegend{Position: "none", ShowLegendKey: true}, Title: ChartTitle{Name: "2D Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{Font: Font{Bold: true, Italic: true, Underline: "dbl", Color: "000000"}, Title: []RichTextRun{{Text: "Primary Horizontal Axis Title"}}}, YAxis: ChartAxis{Font: Font{Bold: false, Italic: false, Underline: "sng", Color: "777777"}, Title: []RichTextRun{{Text: "Primary Vertical Axis Title", Font: &Font{Color: "777777", Bold: true, Italic: true, Size: 12}}}}}},
210210
{sheetName: "Sheet1", cell: "X1", opts: &Chart{Type: ColStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "2D Stacked Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
211211
{sheetName: "Sheet1", cell: "P16", opts: &Chart{Type: ColPercentStacked, Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "100% Stacked Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
212212
{sheetName: "Sheet1", cell: "X16", opts: &Chart{Type: Col3DClustered, Series: series, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: ChartTitle{Name: "3D Clustered Column Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},

drawing.go

+63-30
Original file line numberDiff line numberDiff line change
@@ -66,41 +66,43 @@ func (f *File) addChart(opts *Chart, comboCharts []*Chart) {
6666
Title: &cTitle{
6767
Tx: cTx{
6868
Rich: &cRich{
69-
P: aP{
70-
PPr: &aPPr{
71-
DefRPr: aRPr{
72-
Kern: 1200,
73-
Strike: "noStrike",
74-
U: "none",
75-
Sz: 1400,
76-
SolidFill: &aSolidFill{
77-
SchemeClr: &aSchemeClr{
78-
Val: "tx1",
79-
LumMod: &attrValInt{
80-
Val: intPtr(65000),
81-
},
82-
LumOff: &attrValInt{
83-
Val: intPtr(35000),
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+
},
8486
},
8587
},
86-
},
87-
Ea: &aEa{
88-
Typeface: "+mn-ea",
89-
},
90-
Cs: &aCs{
91-
Typeface: "+mn-cs",
92-
},
93-
Latin: &xlsxCTTextFont{
94-
Typeface: "+mn-lt",
88+
Ea: &aEa{
89+
Typeface: "+mn-ea",
90+
},
91+
Cs: &aCs{
92+
Typeface: "+mn-cs",
93+
},
94+
Latin: &xlsxCTTextFont{
95+
Typeface: "+mn-lt",
96+
},
9597
},
9698
},
97-
},
98-
R: &aR{
99-
RPr: aRPr{
100-
Lang: "en-US",
101-
AltLang: "en-US",
99+
R: &aR{
100+
RPr: aRPr{
101+
Lang: "en-US",
102+
AltLang: "en-US",
103+
},
104+
T: opts.Title.Name,
102105
},
103-
T: opts.Title.Name,
104106
},
105107
},
106108
},
@@ -1059,6 +1061,7 @@ func (f *File) drawPlotAreaCatAx(opts *Chart) []*cAxs {
10591061
NumFmt: &cNumFmt{FormatCode: "General"},
10601062
MajorTickMark: &attrValString{Val: stringPtr("none")},
10611063
MinorTickMark: &attrValString{Val: stringPtr("none")},
1064+
Title: f.drawPlotAreaTitles(opts.XAxis.Title, ""),
10621065
TickLblPos: &attrValString{Val: stringPtr("nextTo")},
10631066
SpPr: f.drawPlotAreaSpPr(),
10641067
TxPr: f.drawPlotAreaTxPr(&opts.YAxis),
@@ -1110,6 +1113,7 @@ func (f *File) drawPlotAreaValAx(opts *Chart) []*cAxs {
11101113
},
11111114
Delete: &attrValBool{Val: boolPtr(opts.YAxis.None)},
11121115
AxPos: &attrValString{Val: stringPtr(valAxPos[opts.YAxis.ReverseOrder])},
1116+
Title: f.drawPlotAreaTitles(opts.YAxis.Title, "horz"),
11131117
NumFmt: &cNumFmt{
11141118
FormatCode: chartValAxNumFmtFormatCode[opts.Type],
11151119
},
@@ -1169,6 +1173,35 @@ func (f *File) drawPlotAreaSerAx(opts *Chart) []*cAxs {
11691173
}
11701174
}
11711175

1176+
// drawPlotAreaTitles provides a function to draw the c:title element.
1177+
func (f *File) drawPlotAreaTitles(runs []RichTextRun, vert string) *cTitle {
1178+
if len(runs) == 0 {
1179+
return nil
1180+
}
1181+
title := &cTitle{Tx: cTx{Rich: &cRich{}}, Overlay: &attrValBool{Val: boolPtr(false)}}
1182+
for _, run := range runs {
1183+
r := &aR{T: run.Text}
1184+
if run.Font != nil {
1185+
r.RPr.B, r.RPr.I = run.Font.Bold, run.Font.Italic
1186+
if run.Font.Color != "" {
1187+
r.RPr.SolidFill = &aSolidFill{SrgbClr: &attrValString{Val: stringPtr(run.Font.Color)}}
1188+
}
1189+
if run.Font.Size > 0 {
1190+
r.RPr.Sz = run.Font.Size * 100
1191+
}
1192+
}
1193+
title.Tx.Rich.P = append(title.Tx.Rich.P, aP{
1194+
PPr: &aPPr{DefRPr: aRPr{}},
1195+
R: r,
1196+
EndParaRPr: &aEndParaRPr{Lang: "en-US", AltLang: "en-US"},
1197+
})
1198+
}
1199+
if vert == "horz" {
1200+
title.Tx.Rich.BodyPr = aBodyPr{Rot: -5400000, Vert: vert}
1201+
}
1202+
return title
1203+
}
1204+
11721205
// drawPlotAreaSpPr provides a function to draw the c:spPr element.
11731206
func (f *File) drawPlotAreaSpPr() *cSpPr {
11741207
return &cSpPr{

numfmt.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -1190,7 +1190,7 @@ func (nf *numberFormat) printNumberLiteral(text string) string {
11901190
}
11911191
for _, token := range nf.section[nf.sectionIdx].Items {
11921192
if token.TType == nfp.TokenTypeCurrencyLanguage {
1193-
if err, changeNumFmtCode := nf.currencyLanguageHandler(token); err != nil || changeNumFmtCode {
1193+
if changeNumFmtCode, err := nf.currencyLanguageHandler(token); err != nil || changeNumFmtCode {
11941194
return nf.value
11951195
}
11961196
result += nf.currencyString
@@ -1326,7 +1326,7 @@ func (nf *numberFormat) dateTimeHandler() string {
13261326
nf.t, nf.hours, nf.seconds = timeFromExcelTime(nf.number, nf.date1904), false, false
13271327
for i, token := range nf.section[nf.sectionIdx].Items {
13281328
if token.TType == nfp.TokenTypeCurrencyLanguage {
1329-
if err, changeNumFmtCode := nf.currencyLanguageHandler(token); err != nil || changeNumFmtCode {
1329+
if changeNumFmtCode, err := nf.currencyLanguageHandler(token); err != nil || changeNumFmtCode {
13301330
return nf.value
13311331
}
13321332
nf.result += nf.currencyString
@@ -1397,36 +1397,36 @@ func (nf *numberFormat) positiveHandler() string {
13971397

13981398
// currencyLanguageHandler will be handling currency and language types tokens
13991399
// for a number format expression.
1400-
func (nf *numberFormat) currencyLanguageHandler(token nfp.Token) (error, bool) {
1400+
func (nf *numberFormat) currencyLanguageHandler(token nfp.Token) (bool, error) {
14011401
for _, part := range token.Parts {
14021402
if inStrSlice(supportedTokenTypes, part.Token.TType, true) == -1 {
1403-
return ErrUnsupportedNumberFormat, false
1403+
return false, ErrUnsupportedNumberFormat
14041404
}
14051405
if part.Token.TType == nfp.TokenSubTypeLanguageInfo {
14061406
if strings.EqualFold(part.Token.TValue, "F800") { // [$-x-sysdate]
14071407
if nf.opts != nil && nf.opts.LongDatePattern != "" {
14081408
nf.value = format(nf.value, nf.opts.LongDatePattern, nf.date1904, nf.cellType, nf.opts)
1409-
return nil, true
1409+
return true, nil
14101410
}
14111411
part.Token.TValue = "409"
14121412
}
14131413
if strings.EqualFold(part.Token.TValue, "F400") { // [$-x-systime]
14141414
if nf.opts != nil && nf.opts.LongTimePattern != "" {
14151415
nf.value = format(nf.value, nf.opts.LongTimePattern, nf.date1904, nf.cellType, nf.opts)
1416-
return nil, true
1416+
return true, nil
14171417
}
14181418
part.Token.TValue = "409"
14191419
}
14201420
if _, ok := supportedLanguageInfo[strings.ToUpper(part.Token.TValue)]; !ok {
1421-
return ErrUnsupportedNumberFormat, false
1421+
return false, ErrUnsupportedNumberFormat
14221422
}
14231423
nf.localCode = strings.ToUpper(part.Token.TValue)
14241424
}
14251425
if part.Token.TType == nfp.TokenSubTypeCurrencyString {
14261426
nf.currencyString = part.Token.TValue
14271427
}
14281428
}
1429-
return nil, false
1429+
return false, nil
14301430
}
14311431

14321432
// localAmPm return AM/PM name by supported language ID.

numfmt_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1093,7 +1093,7 @@ func TestNumFmt(t *testing.T) {
10931093
}
10941094
}
10951095
nf := numberFormat{}
1096-
err, changeNumFmtCode := nf.currencyLanguageHandler(nfp.Token{Parts: []nfp.Part{{}}})
1096+
changeNumFmtCode, err := nf.currencyLanguageHandler(nfp.Token{Parts: []nfp.Part{{}}})
10971097
assert.Equal(t, ErrUnsupportedNumberFormat, err)
10981098
assert.False(t, changeNumFmtCode)
10991099
}

xmlChart.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ type cTx struct {
7474
type cRich struct {
7575
BodyPr aBodyPr `xml:"a:bodyPr,omitempty"`
7676
LstStyle string `xml:"a:lstStyle,omitempty"`
77-
P aP `xml:"a:p"`
77+
P []aP `xml:"a:p"`
7878
}
7979

8080
// aBodyPr (Body Properties) directly maps the a:bodyPr element. This element
@@ -351,6 +351,7 @@ type cAxs struct {
351351
AxPos *attrValString `xml:"axPos"`
352352
MajorGridlines *cChartLines `xml:"majorGridlines"`
353353
MinorGridlines *cChartLines `xml:"minorGridlines"`
354+
Title *cTitle `xml:"title"`
354355
NumFmt *cNumFmt `xml:"numFmt"`
355356
MajorTickMark *attrValString `xml:"majorTickMark"`
356357
MinorTickMark *attrValString `xml:"minorTickMark"`
@@ -539,6 +540,7 @@ type ChartAxis struct {
539540
Font Font
540541
LogBase float64
541542
NumFmt ChartNumFmt
543+
Title []RichTextRun
542544
}
543545

544546
// ChartDimension directly maps the dimension of the chart.

0 commit comments

Comments
 (0)