This closes #320, support custom chart axis font style
This commit is contained in:
parent
7363c1e333
commit
3d02726ad4
15
chart.go
15
chart.go
|
@ -750,16 +750,18 @@ func parseChartOptions(opts string) (*chartOptions, error) {
|
|||
// reverse_order
|
||||
// maximum
|
||||
// minimum
|
||||
// number_font
|
||||
//
|
||||
// The properties of y_axis that can be set are:
|
||||
//
|
||||
// none
|
||||
// major_grid_lines
|
||||
// minor_grid_lines
|
||||
// major_unit
|
||||
// tick_label_skip
|
||||
// reverse_order
|
||||
// maximum
|
||||
// minimum
|
||||
// number_font
|
||||
//
|
||||
// none: Disable axes.
|
||||
//
|
||||
|
@ -777,6 +779,17 @@ func parseChartOptions(opts string) (*chartOptions, error) {
|
|||
//
|
||||
// minimum: Specifies that the fixed minimum, 0 is auto. The minimum property is optional. The default value is auto.
|
||||
//
|
||||
// number_font: Specifies that the font of the horizontal and vertical axis. The properties of number_font that can be set are:
|
||||
//
|
||||
// bold
|
||||
// italic
|
||||
// underline
|
||||
// family
|
||||
// size
|
||||
// strike
|
||||
// color
|
||||
// vertAlign
|
||||
//
|
||||
// Set chart size by dimension property. The dimension property is optional. The default width is 480, and height is 290.
|
||||
//
|
||||
// combo: Specifies the create a chart that combines two or more chart types
|
||||
|
|
|
@ -116,7 +116,7 @@ func TestAddChart(t *testing.T) {
|
|||
// Test add chart on not exists worksheet.
|
||||
assert.EqualError(t, f.AddChart("SheetN", "P1", "{}"), "sheet SheetN does not exist")
|
||||
|
||||
assert.NoError(t, f.AddChart("Sheet1", "P1", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"none":true,"show_legend_key":true},"title":{"name":"2D Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
||||
assert.NoError(t, f.AddChart("Sheet1", "P1", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"none":true,"show_legend_key":true},"title":{"name":"2D Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"number_font":{"bold":true,"italic":true,"underline":"dbl","color":"#000000"}},"y_axis":{"number_font":{"bold":false,"italic":false,"underline":"sng","color":"#777777"}}}`))
|
||||
assert.NoError(t, f.AddChart("Sheet1", "X1", `{"type":"colStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D Stacked Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
||||
assert.NoError(t, f.AddChart("Sheet1", "P16", `{"type":"colPercentStacked","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"100% Stacked Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
||||
assert.NoError(t, f.AddChart("Sheet1", "X16", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"bottom","show_legend_key":false},"title":{"name":"3D Clustered Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
|
||||
|
|
22
drawing.go
22
drawing.go
|
@ -1017,7 +1017,7 @@ func (f *File) drawPlotAreaCatAx(opts *chartOptions) []*cAxs {
|
|||
MinorTickMark: &attrValString{Val: stringPtr("none")},
|
||||
TickLblPos: &attrValString{Val: stringPtr("nextTo")},
|
||||
SpPr: f.drawPlotAreaSpPr(),
|
||||
TxPr: f.drawPlotAreaTxPr(),
|
||||
TxPr: f.drawPlotAreaTxPr(&opts.YAxis),
|
||||
CrossAx: &attrValInt{Val: intPtr(753999904)},
|
||||
Crosses: &attrValString{Val: stringPtr("autoZero")},
|
||||
Auto: &attrValBool{Val: boolPtr(true)},
|
||||
|
@ -1071,7 +1071,7 @@ func (f *File) drawPlotAreaValAx(opts *chartOptions) []*cAxs {
|
|||
MinorTickMark: &attrValString{Val: stringPtr("none")},
|
||||
TickLblPos: &attrValString{Val: stringPtr("nextTo")},
|
||||
SpPr: f.drawPlotAreaSpPr(),
|
||||
TxPr: f.drawPlotAreaTxPr(),
|
||||
TxPr: f.drawPlotAreaTxPr(&opts.XAxis),
|
||||
CrossAx: &attrValInt{Val: intPtr(754001152)},
|
||||
Crosses: &attrValString{Val: stringPtr("autoZero")},
|
||||
CrossBetween: &attrValString{Val: stringPtr(chartValAxCrossBetween[opts.Type])},
|
||||
|
@ -1114,7 +1114,7 @@ func (f *File) drawPlotAreaSerAx(opts *chartOptions) []*cAxs {
|
|||
AxPos: &attrValString{Val: stringPtr(catAxPos[opts.XAxis.ReverseOrder])},
|
||||
TickLblPos: &attrValString{Val: stringPtr("nextTo")},
|
||||
SpPr: f.drawPlotAreaSpPr(),
|
||||
TxPr: f.drawPlotAreaTxPr(),
|
||||
TxPr: f.drawPlotAreaTxPr(nil),
|
||||
CrossAx: &attrValInt{Val: intPtr(753999904)},
|
||||
},
|
||||
}
|
||||
|
@ -1140,8 +1140,8 @@ func (f *File) drawPlotAreaSpPr() *cSpPr {
|
|||
}
|
||||
|
||||
// drawPlotAreaTxPr provides a function to draw the c:txPr element.
|
||||
func (f *File) drawPlotAreaTxPr() *cTxPr {
|
||||
return &cTxPr{
|
||||
func (f *File) drawPlotAreaTxPr(opts *chartAxisOptions) *cTxPr {
|
||||
cTxPr := &cTxPr{
|
||||
BodyPr: aBodyPr{
|
||||
Rot: -60000000,
|
||||
SpcFirstLastPara: true,
|
||||
|
@ -1176,6 +1176,18 @@ func (f *File) drawPlotAreaTxPr() *cTxPr {
|
|||
EndParaRPr: &aEndParaRPr{Lang: "en-US"},
|
||||
},
|
||||
}
|
||||
if opts != nil {
|
||||
cTxPr.P.PPr.DefRPr.B = opts.NumFont.Bold
|
||||
cTxPr.P.PPr.DefRPr.I = opts.NumFont.Italic
|
||||
if idx := inStrSlice(supportedDrawingUnderlineTypes, opts.NumFont.Underline, true); idx != -1 {
|
||||
cTxPr.P.PPr.DefRPr.U = supportedDrawingUnderlineTypes[idx]
|
||||
}
|
||||
if opts.NumFont.Color != "" {
|
||||
cTxPr.P.PPr.DefRPr.SolidFill.SchemeClr = nil
|
||||
cTxPr.P.PPr.DefRPr.SolidFill.SrgbClr = &attrValString{Val: stringPtr(strings.ReplaceAll(strings.ToUpper(opts.NumFont.Color), "#", ""))}
|
||||
}
|
||||
}
|
||||
return cTxPr
|
||||
}
|
||||
|
||||
// drawingParser provides a function to parse drawingXML. In order to solve
|
||||
|
|
4
file.go
4
file.go
|
@ -72,7 +72,7 @@ func (f *File) SaveAs(name string, opts ...Options) error {
|
|||
return ErrMaxFilePathLength
|
||||
}
|
||||
f.Path = name
|
||||
if _, ok := supportedContentType[filepath.Ext(f.Path)]; !ok {
|
||||
if _, ok := supportedContentTypes[filepath.Ext(f.Path)]; !ok {
|
||||
return ErrWorkbookFileFormat
|
||||
}
|
||||
file, err := os.OpenFile(filepath.Clean(name), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm)
|
||||
|
@ -112,7 +112,7 @@ func (f *File) WriteTo(w io.Writer, opts ...Options) (int64, error) {
|
|||
f.options = &opts[i]
|
||||
}
|
||||
if len(f.Path) != 0 {
|
||||
contentType, ok := supportedContentType[filepath.Ext(f.Path)]
|
||||
contentType, ok := supportedContentTypes[filepath.Ext(f.Path)]
|
||||
if !ok {
|
||||
return 0, ErrWorkbookFileFormat
|
||||
}
|
||||
|
|
28
shape.go
28
shape.go
|
@ -323,27 +323,6 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *shapeOption
|
|||
colIdx := fromCol - 1
|
||||
rowIdx := fromRow - 1
|
||||
|
||||
textUnderlineType := map[string]bool{
|
||||
"none": true,
|
||||
"words": true,
|
||||
"sng": true,
|
||||
"dbl": true,
|
||||
"heavy": true,
|
||||
"dotted": true,
|
||||
"dottedHeavy": true,
|
||||
"dash": true,
|
||||
"dashHeavy": true,
|
||||
"dashLong": true,
|
||||
"dashLongHeavy": true,
|
||||
"dotDash": true,
|
||||
"dotDashHeavy": true,
|
||||
"dotDotDash": true,
|
||||
"dotDotDashHeavy": true,
|
||||
"wavy": true,
|
||||
"wavyHeavy": true,
|
||||
"wavyDbl": true,
|
||||
}
|
||||
|
||||
width := int(float64(opts.Width) * opts.Format.XScale)
|
||||
height := int(float64(opts.Height) * opts.Format.YScale)
|
||||
|
||||
|
@ -422,10 +401,9 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, opts *shapeOption
|
|||
}
|
||||
}
|
||||
for _, p := range opts.Paragraph {
|
||||
u := p.Font.Underline
|
||||
_, ok := textUnderlineType[u]
|
||||
if !ok {
|
||||
u = "none"
|
||||
u := "none"
|
||||
if idx := inStrSlice(supportedDrawingUnderlineTypes, p.Font.Underline, true); idx != -1 {
|
||||
u = supportedDrawingUnderlineTypes[idx]
|
||||
}
|
||||
text := p.Text
|
||||
if text == "" {
|
||||
|
|
18
styles.go
18
styles.go
|
@ -2087,7 +2087,6 @@ func (f *File) getFontID(styleSheet *xlsxStyleSheet, style *Style) (fontID int)
|
|||
// newFont provides a function to add font style by given cell format
|
||||
// settings.
|
||||
func (f *File) newFont(style *Style) *xlsxFont {
|
||||
fontUnderlineType := map[string]string{"single": "single", "double": "double"}
|
||||
if style.Font.Size < MinFontSize {
|
||||
style.Font.Size = 11
|
||||
}
|
||||
|
@ -2112,9 +2111,8 @@ func (f *File) newFont(style *Style) *xlsxFont {
|
|||
if style.Font.Strike {
|
||||
fnt.Strike = &attrValBool{Val: &style.Font.Strike}
|
||||
}
|
||||
val, ok := fontUnderlineType[style.Font.Underline]
|
||||
if ok {
|
||||
fnt.U = &attrValString{Val: stringPtr(val)}
|
||||
if idx := inStrSlice(supportedUnderlineTypes, style.Font.Underline, true); idx != -1 {
|
||||
fnt.U = &attrValString{Val: stringPtr(supportedUnderlineTypes[idx])}
|
||||
}
|
||||
return &fnt
|
||||
}
|
||||
|
@ -3100,13 +3098,10 @@ func drawCondFmtCellIs(p int, ct string, format *conditionalOptions) *xlsxCfRule
|
|||
DxfID: &format.Format,
|
||||
}
|
||||
// "between" and "not between" criteria require 2 values.
|
||||
_, ok := map[string]bool{"between": true, "notBetween": true}[ct]
|
||||
if ok {
|
||||
c.Formula = append(c.Formula, format.Minimum)
|
||||
c.Formula = append(c.Formula, format.Maximum)
|
||||
if ct == "between" || ct == "notBetween" {
|
||||
c.Formula = append(c.Formula, []string{format.Minimum, format.Maximum}...)
|
||||
}
|
||||
_, ok = map[string]bool{"equal": true, "notEqual": true, "greaterThan": true, "lessThan": true, "greaterThanOrEqual": true, "lessThanOrEqual": true, "containsText": true, "notContains": true, "beginsWith": true, "endsWith": true}[ct]
|
||||
if ok {
|
||||
if idx := inStrSlice([]string{"equal", "notEqual", "greaterThan", "lessThan", "greaterThanOrEqual", "lessThanOrEqual", "containsText", "notContains", "beginsWith", "endsWith"}, ct, true); idx != -1 {
|
||||
c.Formula = append(c.Formula, format.Value)
|
||||
}
|
||||
return c
|
||||
|
@ -3124,8 +3119,7 @@ func drawCondFmtTop10(p int, ct string, format *conditionalOptions) *xlsxCfRule
|
|||
DxfID: &format.Format,
|
||||
Percent: format.Percent,
|
||||
}
|
||||
rank, err := strconv.Atoi(format.Value)
|
||||
if err == nil {
|
||||
if rank, err := strconv.Atoi(format.Value); err == nil {
|
||||
c.Rank = rank
|
||||
}
|
||||
return c
|
||||
|
|
2
table.go
2
table.go
|
@ -516,7 +516,7 @@ func (f *File) parseFilterTokens(expression string, tokens []string) ([]int, str
|
|||
}
|
||||
}
|
||||
}
|
||||
// if the string token contains an Excel match character then change the
|
||||
// If the string token contains an Excel match character then change the
|
||||
// operator type to indicate a non "simple" equality.
|
||||
re, _ = regexp.Match("[*?]", []byte(token))
|
||||
if operator == 2 && re {
|
||||
|
|
|
@ -537,13 +537,8 @@ type chartAxisOptions struct {
|
|||
ReverseOrder bool `json:"reverse_order"`
|
||||
Maximum *float64 `json:"maximum"`
|
||||
Minimum *float64 `json:"minimum"`
|
||||
NumFormat string `json:"num_format"`
|
||||
NumFont struct {
|
||||
Color string `json:"color"`
|
||||
Bold bool `json:"bold"`
|
||||
Italic bool `json:"italic"`
|
||||
Underline bool `json:"underline"`
|
||||
} `json:"num_font"`
|
||||
NumFormat string `json:"number_format"`
|
||||
NumFont Font `json:"number_font"`
|
||||
LogBase float64 `json:"logbase"`
|
||||
NameLayout layoutOptions `json:"name_layout"`
|
||||
}
|
||||
|
|
|
@ -144,8 +144,8 @@ const (
|
|||
// supportedImageTypes defined supported image types.
|
||||
var supportedImageTypes = map[string]string{".gif": ".gif", ".jpg": ".jpeg", ".jpeg": ".jpeg", ".png": ".png", ".tif": ".tiff", ".tiff": ".tiff", ".emf": ".emf", ".wmf": ".wmf", ".emz": ".emz", ".wmz": ".wmz"}
|
||||
|
||||
// supportedContentType defined supported file format types.
|
||||
var supportedContentType = map[string]string{
|
||||
// supportedContentTypes defined supported file format types.
|
||||
var supportedContentTypes = map[string]string{
|
||||
".xlam": ContentTypeAddinMacro,
|
||||
".xlsm": ContentTypeMacro,
|
||||
".xlsx": ContentTypeSheetML,
|
||||
|
@ -153,6 +153,16 @@ var supportedContentType = map[string]string{
|
|||
".xltx": ContentTypeTemplate,
|
||||
}
|
||||
|
||||
// supportedUnderlineTypes defined supported underline types.
|
||||
var supportedUnderlineTypes = []string{"none", "single", "double"}
|
||||
|
||||
// supportedDrawingUnderlineTypes defined supported underline types in drawing
|
||||
// markup language.
|
||||
var supportedDrawingUnderlineTypes = []string{
|
||||
"none", "words", "sng", "dbl", "heavy", "dotted", "dottedHeavy", "dash", "dashHeavy", "dashLong", "dashLongHeavy", "dotDash", "dotDashHeavy", "dotDotDash", "dotDotDashHeavy", "wavy", "wavyHeavy",
|
||||
"wavyDbl",
|
||||
}
|
||||
|
||||
// xlsxCNvPr directly maps the cNvPr (Non-Visual Drawing Properties). This
|
||||
// element specifies non-visual canvas properties. This allows for additional
|
||||
// information that does not affect the appearance of the picture to be stored.
|
||||
|
|
Loading…
Reference in New Issue