feat: support table redner
This commit is contained in:
parent
2fb0ebcbf7
commit
b3a3018ea2
8 changed files with 433 additions and 116 deletions
68
README.md
68
README.md
|
|
@ -15,6 +15,9 @@ Screenshot of common charts, the left part is light theme, the right part is gra
|
||||||
<img src="./assets/go-charts.png" alt="go-charts">
|
<img src="./assets/go-charts.png" alt="go-charts">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="./assets/go-table.png" alt="go-table">
|
||||||
|
</p>
|
||||||
## Chart Type
|
## Chart Type
|
||||||
|
|
||||||
These chart types are supported: `line`, `bar`, `pie`, `radar` or `funnel`.
|
These chart types are supported: `line`, `bar`, `pie`, `radar` or `funnel`.
|
||||||
|
|
@ -374,6 +377,71 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Table
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vicanso/go-charts/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
header := []string{
|
||||||
|
"Name",
|
||||||
|
"Age",
|
||||||
|
"Address",
|
||||||
|
"Tag",
|
||||||
|
"Action",
|
||||||
|
}
|
||||||
|
data := [][]string{
|
||||||
|
{
|
||||||
|
"John Brown",
|
||||||
|
"32",
|
||||||
|
"New York No. 1 Lake Park",
|
||||||
|
"nice, developer",
|
||||||
|
"Send Mail",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Jim Green ",
|
||||||
|
"42",
|
||||||
|
"London No. 1 Lake Park",
|
||||||
|
"wow",
|
||||||
|
"Send Mail",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Joe Black ",
|
||||||
|
"32",
|
||||||
|
"Sidney No. 1 Lake Park",
|
||||||
|
"cool, teacher",
|
||||||
|
"Send Mail",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
spans := map[int]int{
|
||||||
|
0: 2,
|
||||||
|
1: 1,
|
||||||
|
// 设置第三列的span
|
||||||
|
2: 3,
|
||||||
|
3: 2,
|
||||||
|
4: 2,
|
||||||
|
}
|
||||||
|
p, err := charts.TableRender(
|
||||||
|
header,
|
||||||
|
data,
|
||||||
|
spans,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := p.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// snip...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### ECharts Render
|
### ECharts Render
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
|
|
||||||
64
README_zh.md
64
README_zh.md
|
|
@ -373,6 +373,70 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Table
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vicanso/go-charts/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
header := []string{
|
||||||
|
"Name",
|
||||||
|
"Age",
|
||||||
|
"Address",
|
||||||
|
"Tag",
|
||||||
|
"Action",
|
||||||
|
}
|
||||||
|
data := [][]string{
|
||||||
|
{
|
||||||
|
"John Brown",
|
||||||
|
"32",
|
||||||
|
"New York No. 1 Lake Park",
|
||||||
|
"nice, developer",
|
||||||
|
"Send Mail",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Jim Green ",
|
||||||
|
"42",
|
||||||
|
"London No. 1 Lake Park",
|
||||||
|
"wow",
|
||||||
|
"Send Mail",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Joe Black ",
|
||||||
|
"32",
|
||||||
|
"Sidney No. 1 Lake Park",
|
||||||
|
"cool, teacher",
|
||||||
|
"Send Mail",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
spans := map[int]int{
|
||||||
|
0: 2,
|
||||||
|
1: 1,
|
||||||
|
// 设置第三列的span
|
||||||
|
2: 3,
|
||||||
|
3: 2,
|
||||||
|
4: 2,
|
||||||
|
}
|
||||||
|
p, err := charts.TableRender(
|
||||||
|
header,
|
||||||
|
data,
|
||||||
|
spans,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := p.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// snip...
|
||||||
|
}
|
||||||
|
```
|
||||||
### ECharts Render
|
### ECharts Render
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@
|
||||||
package charts
|
package charts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
|
|
@ -338,16 +337,35 @@ func FunnelRender(values []float64, opts ...OptionFunc) (*Painter, error) {
|
||||||
}, opts...)
|
}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TableRender(opt TableChartOption) (*Painter, error) {
|
// TableRender table chart render
|
||||||
|
func TableRender(header []string, data [][]string, spanMaps ...map[int]int) (*Painter, error) {
|
||||||
|
opt := TableChartOption{
|
||||||
|
Header: header,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
if len(spanMaps) != 0 {
|
||||||
|
spanMap := spanMaps[0]
|
||||||
|
spans := make([]int, len(opt.Header))
|
||||||
|
for index := range spans {
|
||||||
|
v, ok := spanMap[index]
|
||||||
|
if !ok {
|
||||||
|
v = 1
|
||||||
|
}
|
||||||
|
spans[index] = v
|
||||||
|
}
|
||||||
|
opt.Spans = spans
|
||||||
|
}
|
||||||
|
return TableOptionRender(opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableOptionRender table render with option
|
||||||
|
func TableOptionRender(opt TableChartOption) (*Painter, error) {
|
||||||
if opt.Type == "" {
|
if opt.Type == "" {
|
||||||
opt.Type = ChartOutputPNG
|
opt.Type = ChartOutputPNG
|
||||||
}
|
}
|
||||||
if opt.Width <= 0 {
|
if opt.Width <= 0 {
|
||||||
opt.Width = defaultChartWidth
|
opt.Width = defaultChartWidth
|
||||||
}
|
}
|
||||||
if opt.Height <= 0 {
|
|
||||||
opt.Height = defaultChartHeight
|
|
||||||
}
|
|
||||||
if opt.Font == nil {
|
if opt.Font == nil {
|
||||||
opt.Font, _ = chart.GetDefaultFont()
|
opt.Font, _ = chart.GetDefaultFont()
|
||||||
}
|
}
|
||||||
|
|
@ -355,7 +373,8 @@ func TableRender(opt TableChartOption) (*Painter, error) {
|
||||||
p, err := NewPainter(PainterOptions{
|
p, err := NewPainter(PainterOptions{
|
||||||
Type: opt.Type,
|
Type: opt.Type,
|
||||||
Width: opt.Width,
|
Width: opt.Width,
|
||||||
Height: opt.Height,
|
// 仅用于计算表格高度,因此随便设置即可
|
||||||
|
Height: 100,
|
||||||
Font: opt.Font,
|
Font: opt.Font,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -365,8 +384,6 @@ func TableRender(opt TableChartOption) (*Painter, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
fmt.Println(*info)
|
|
||||||
fmt.Println(info.Height)
|
|
||||||
|
|
||||||
p, err = NewPainter(PainterOptions{
|
p, err = NewPainter(PainterOptions{
|
||||||
Type: opt.Type,
|
Type: opt.Type,
|
||||||
|
|
@ -377,31 +394,9 @@ func TableRender(opt TableChartOption) (*Painter, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, err = NewTableChart(p, opt).Render()
|
_, err = NewTableChart(p, opt).renderWithInfo(info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// opt := ChartOption{}
|
|
||||||
// for _, fn := range opts {
|
|
||||||
// fn(&opt)
|
|
||||||
// }
|
|
||||||
// opt.fillDefault()
|
|
||||||
// p, err := NewPainter(PainterOptions{
|
|
||||||
// Type: opt.Type,
|
|
||||||
// Width: opt.Width,
|
|
||||||
// Height: opt.Height,
|
|
||||||
// Font: opt.font,
|
|
||||||
// })
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// _, err = NewTableChart(p, TableChartOption{
|
|
||||||
// Header: header,
|
|
||||||
// Data: data,
|
|
||||||
// }).Render()
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,48 @@ func handler(w http.ResponseWriter, req *http.Request, chartOptions []charts.Cha
|
||||||
bytesList = append(bytesList, buf)
|
bytesList = append(bytesList, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p, err := charts.TableOptionRender(charts.TableChartOption{
|
||||||
|
Type: charts.ChartOutputSVG,
|
||||||
|
Header: []string{
|
||||||
|
"Name",
|
||||||
|
"Age",
|
||||||
|
"Address",
|
||||||
|
"Tag",
|
||||||
|
"Action",
|
||||||
|
},
|
||||||
|
Data: [][]string{
|
||||||
|
{
|
||||||
|
"John Brown",
|
||||||
|
"32",
|
||||||
|
"New York No. 1 Lake Park",
|
||||||
|
"nice, developer",
|
||||||
|
"Send Mail",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Jim Green ",
|
||||||
|
"42",
|
||||||
|
"London No. 1 Lake Park",
|
||||||
|
"wow",
|
||||||
|
"Send Mail",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Joe Black ",
|
||||||
|
"32",
|
||||||
|
"Sidney No. 1 Lake Park",
|
||||||
|
"cool, teacher",
|
||||||
|
"Send Mail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
buf, err := p.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
bytesList = append(bytesList, buf)
|
||||||
|
|
||||||
data := bytes.ReplaceAll([]byte(html), []byte("{{body}}"), bytes.Join(bytesList, []byte("")))
|
data := bytes.ReplaceAll([]byte(html), []byte("{{body}}"), bytes.Join(bytesList, []byte("")))
|
||||||
w.Header().Set("Content-Type", "text/html")
|
w.Header().Set("Content-Type", "text/html")
|
||||||
w.Write(data)
|
w.Write(data)
|
||||||
|
|
|
||||||
|
|
@ -24,22 +24,15 @@ func writeFile(buf []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
p, err := charts.TableRender(charts.TableChartOption{
|
charts.SetDefaultWidth(810)
|
||||||
Header: []string{
|
header := []string{
|
||||||
"Name",
|
"Name",
|
||||||
"Age",
|
"Age",
|
||||||
"Address",
|
"Address",
|
||||||
"Tag",
|
"Tag",
|
||||||
"Action",
|
"Action",
|
||||||
},
|
}
|
||||||
Spans: []int{
|
data := [][]string{
|
||||||
1,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
},
|
|
||||||
Data: [][]string{
|
|
||||||
{
|
{
|
||||||
"John Brown",
|
"John Brown",
|
||||||
"32",
|
"32",
|
||||||
|
|
@ -61,8 +54,19 @@ func main() {
|
||||||
"cool, teacher",
|
"cool, teacher",
|
||||||
"Send Mail",
|
"Send Mail",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
},
|
spans := map[int]int{
|
||||||
|
0: 2,
|
||||||
|
1: 1,
|
||||||
|
// 设置第三列的span
|
||||||
|
2: 3,
|
||||||
|
3: 2,
|
||||||
|
4: 2,
|
||||||
|
}
|
||||||
|
p, err := charts.TableRender(
|
||||||
|
header,
|
||||||
|
data,
|
||||||
|
spans,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ type Painter struct {
|
||||||
parent *Painter
|
parent *Painter
|
||||||
style Style
|
style Style
|
||||||
theme ColorPalette
|
theme ColorPalette
|
||||||
|
// 类型
|
||||||
|
outputType string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PainterOptions struct {
|
type PainterOptions struct {
|
||||||
|
|
@ -169,6 +171,8 @@ func NewPainter(opts PainterOptions, opt ...PainterOption) (*Painter, error) {
|
||||||
Bottom: opts.Height,
|
Bottom: opts.Height,
|
||||||
},
|
},
|
||||||
font: font,
|
font: font,
|
||||||
|
// 类型
|
||||||
|
outputType: opts.Type,
|
||||||
}
|
}
|
||||||
p.setOptions(opt...)
|
p.setOptions(opt...)
|
||||||
if p.theme == nil {
|
if p.theme == nil {
|
||||||
|
|
|
||||||
180
table.go
180
table.go
|
|
@ -35,6 +35,7 @@ type tableChart struct {
|
||||||
opt *TableChartOption
|
opt *TableChartOption
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewTableChart returns a table chart render
|
||||||
func NewTableChart(p *Painter, opt TableChartOption) *tableChart {
|
func NewTableChart(p *Painter, opt TableChartOption) *tableChart {
|
||||||
if opt.Theme == nil {
|
if opt.Theme == nil {
|
||||||
opt.Theme = defaultTheme
|
opt.Theme = defaultTheme
|
||||||
|
|
@ -50,8 +51,6 @@ type TableChartOption struct {
|
||||||
Type string
|
Type string
|
||||||
// The width of table
|
// The width of table
|
||||||
Width int
|
Width int
|
||||||
// The height of table
|
|
||||||
Height int
|
|
||||||
// The theme
|
// The theme
|
||||||
Theme ColorPalette
|
Theme ColorPalette
|
||||||
// The padding of table cell
|
// The padding of table cell
|
||||||
|
|
@ -64,6 +63,8 @@ type TableChartOption struct {
|
||||||
Spans []int
|
Spans []int
|
||||||
// The font size of table
|
// The font size of table
|
||||||
FontSize float64
|
FontSize float64
|
||||||
|
// The font family, which should be installed first
|
||||||
|
FontFamily string
|
||||||
Font *truetype.Font
|
Font *truetype.Font
|
||||||
// The font color of table
|
// The font color of table
|
||||||
FontColor Color
|
FontColor Color
|
||||||
|
|
@ -77,26 +78,101 @@ type TableChartOption struct {
|
||||||
BackgroundColor Color
|
BackgroundColor Color
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultTableHeaderColor = Color{
|
type TableSetting struct {
|
||||||
R: 34,
|
// The color of header
|
||||||
G: 34,
|
HeaderColor Color
|
||||||
B: 34,
|
// The color of heder text
|
||||||
A: 255,
|
HeaderFontColor Color
|
||||||
|
// The color of table text
|
||||||
|
FontColor Color
|
||||||
|
// The color list of row
|
||||||
|
RowColors []Color
|
||||||
|
// The padding of cell
|
||||||
|
Padding Box
|
||||||
}
|
}
|
||||||
var defaultTableRowColors = []Color{
|
|
||||||
drawing.ColorWhite,
|
var TableLightThemeSetting = TableSetting{
|
||||||
{
|
HeaderColor: Color{
|
||||||
R: 242,
|
R: 240,
|
||||||
G: 242,
|
G: 240,
|
||||||
B: 242,
|
B: 240,
|
||||||
A: 255,
|
A: 255,
|
||||||
},
|
},
|
||||||
}
|
HeaderFontColor: Color{
|
||||||
var defaultTablePadding = Box{
|
R: 98,
|
||||||
|
G: 105,
|
||||||
|
B: 118,
|
||||||
|
A: 255,
|
||||||
|
},
|
||||||
|
FontColor: Color{
|
||||||
|
R: 70,
|
||||||
|
G: 70,
|
||||||
|
B: 70,
|
||||||
|
A: 255,
|
||||||
|
},
|
||||||
|
RowColors: []Color{
|
||||||
|
drawing.ColorWhite,
|
||||||
|
{
|
||||||
|
R: 247,
|
||||||
|
G: 247,
|
||||||
|
B: 247,
|
||||||
|
A: 255,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Padding: Box{
|
||||||
Left: 10,
|
Left: 10,
|
||||||
Top: 10,
|
Top: 10,
|
||||||
Right: 10,
|
Right: 10,
|
||||||
Bottom: 10,
|
Bottom: 10,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var TableDarkThemeSetting = TableSetting{
|
||||||
|
HeaderColor: Color{
|
||||||
|
R: 38,
|
||||||
|
G: 38,
|
||||||
|
B: 42,
|
||||||
|
A: 255,
|
||||||
|
},
|
||||||
|
HeaderFontColor: Color{
|
||||||
|
R: 216,
|
||||||
|
G: 217,
|
||||||
|
B: 218,
|
||||||
|
A: 255,
|
||||||
|
},
|
||||||
|
FontColor: Color{
|
||||||
|
R: 216,
|
||||||
|
G: 217,
|
||||||
|
B: 218,
|
||||||
|
A: 255,
|
||||||
|
},
|
||||||
|
RowColors: []Color{
|
||||||
|
{
|
||||||
|
R: 24,
|
||||||
|
G: 24,
|
||||||
|
B: 28,
|
||||||
|
A: 255,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
R: 38,
|
||||||
|
G: 38,
|
||||||
|
B: 42,
|
||||||
|
A: 255,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Padding: Box{
|
||||||
|
Left: 10,
|
||||||
|
Top: 10,
|
||||||
|
Right: 10,
|
||||||
|
Bottom: 10,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var tableDefaultSetting = TableLightThemeSetting
|
||||||
|
|
||||||
|
// SetDefaultTableSetting sets the default setting for table
|
||||||
|
func SetDefaultTableSetting(setting TableSetting) {
|
||||||
|
tableDefaultSetting = setting
|
||||||
}
|
}
|
||||||
|
|
||||||
type renderInfo struct {
|
type renderInfo struct {
|
||||||
|
|
@ -106,12 +182,12 @@ type renderInfo struct {
|
||||||
RowHeights []int
|
RowHeights []int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tableChart) render() (*renderInfo, error) {
|
func (t *tableChart) render() (*renderInfo, error) {
|
||||||
info := renderInfo{
|
info := renderInfo{
|
||||||
RowHeights: make([]int, 0),
|
RowHeights: make([]int, 0),
|
||||||
}
|
}
|
||||||
p := c.p
|
p := t.p
|
||||||
opt := c.opt
|
opt := t.opt
|
||||||
if len(opt.Header) == 0 {
|
if len(opt.Header) == 0 {
|
||||||
return nil, errors.New("header can not be nil")
|
return nil, errors.New("header can not be nil")
|
||||||
}
|
}
|
||||||
|
|
@ -125,7 +201,7 @@ func (c *tableChart) render() (*renderInfo, error) {
|
||||||
}
|
}
|
||||||
fontColor := opt.FontColor
|
fontColor := opt.FontColor
|
||||||
if fontColor.IsZero() {
|
if fontColor.IsZero() {
|
||||||
fontColor = theme.GetTextColor()
|
fontColor = tableDefaultSetting.FontColor
|
||||||
}
|
}
|
||||||
font := opt.Font
|
font := opt.Font
|
||||||
if font == nil {
|
if font == nil {
|
||||||
|
|
@ -133,14 +209,14 @@ func (c *tableChart) render() (*renderInfo, error) {
|
||||||
}
|
}
|
||||||
headerFontColor := opt.HeaderFontColor
|
headerFontColor := opt.HeaderFontColor
|
||||||
if opt.HeaderFontColor.IsZero() {
|
if opt.HeaderFontColor.IsZero() {
|
||||||
headerFontColor = drawing.ColorWhite
|
headerFontColor = tableDefaultSetting.HeaderFontColor
|
||||||
}
|
}
|
||||||
|
|
||||||
spans := opt.Spans
|
spans := opt.Spans
|
||||||
if len(spans) != 0 && len(spans) != len(opt.Header) {
|
if len(spans) != len(opt.Header) {
|
||||||
newSpans := make([]int, len(opt.Header))
|
newSpans := make([]int, len(opt.Header))
|
||||||
for index := range opt.Header {
|
for index := range opt.Header {
|
||||||
if len(spans) < index {
|
if index >= len(spans) {
|
||||||
newSpans[index] = 1
|
newSpans[index] = 1
|
||||||
} else {
|
} else {
|
||||||
newSpans[index] = spans[index]
|
newSpans[index] = spans[index]
|
||||||
|
|
@ -149,7 +225,8 @@ func (c *tableChart) render() (*renderInfo, error) {
|
||||||
spans = newSpans
|
spans = newSpans
|
||||||
}
|
}
|
||||||
|
|
||||||
values := autoDivideSpans(p.Width(), len(opt.Header)+1, spans)
|
sum := sumInt(spans)
|
||||||
|
values := autoDivideSpans(p.Width(), sum, spans)
|
||||||
height := 0
|
height := 0
|
||||||
textStyle := Style{
|
textStyle := Style{
|
||||||
FontSize: fontSize,
|
FontSize: fontSize,
|
||||||
|
|
@ -162,7 +239,7 @@ func (c *tableChart) render() (*renderInfo, error) {
|
||||||
headerHeight := 0
|
headerHeight := 0
|
||||||
padding := opt.Padding
|
padding := opt.Padding
|
||||||
if padding.IsZero() {
|
if padding.IsZero() {
|
||||||
padding = defaultTablePadding
|
padding = tableDefaultSetting.Padding
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTableCells := func(textList []string, currentHeight int, cellPadding Box) int {
|
renderTableCells := func(textList []string, currentHeight int, cellPadding Box) int {
|
||||||
|
|
@ -201,34 +278,23 @@ func (c *tableChart) render() (*renderInfo, error) {
|
||||||
return &info, nil
|
return &info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tableChart) Render() (Box, error) {
|
func (t *tableChart) renderWithInfo(info *renderInfo) (Box, error) {
|
||||||
p := c.p
|
p := t.p
|
||||||
opt := c.opt
|
opt := t.opt
|
||||||
if !opt.BackgroundColor.IsZero() {
|
if !opt.BackgroundColor.IsZero() {
|
||||||
p.SetBackground(p.Width(), p.Height(), opt.BackgroundColor)
|
p.SetBackground(p.Width(), p.Height(), opt.BackgroundColor)
|
||||||
}
|
}
|
||||||
headerBGColor := opt.HeaderBackgroundColor
|
headerBGColor := opt.HeaderBackgroundColor
|
||||||
if headerBGColor.IsZero() {
|
if headerBGColor.IsZero() {
|
||||||
headerBGColor = defaultTableHeaderColor
|
headerBGColor = tableDefaultSetting.HeaderColor
|
||||||
}
|
}
|
||||||
|
|
||||||
r := p.render
|
|
||||||
newRender, err := chart.SVG(p.Width(), 100)
|
|
||||||
if err != nil {
|
|
||||||
return BoxZero, err
|
|
||||||
}
|
|
||||||
p.render = newRender
|
|
||||||
info, err := c.render()
|
|
||||||
if err != nil {
|
|
||||||
return BoxZero, err
|
|
||||||
}
|
|
||||||
p.render = r
|
|
||||||
// 如果设置表头背景色
|
// 如果设置表头背景色
|
||||||
p.SetBackground(info.Width, info.HeaderHeight, headerBGColor, true)
|
p.SetBackground(info.Width, info.HeaderHeight, headerBGColor, true)
|
||||||
currentHeight := info.HeaderHeight
|
currentHeight := info.HeaderHeight
|
||||||
rowColors := opt.RowBackgroundColors
|
rowColors := opt.RowBackgroundColors
|
||||||
if len(rowColors) == 0 {
|
if len(rowColors) == 0 {
|
||||||
rowColors = defaultTableRowColors
|
rowColors = tableDefaultSetting.RowColors
|
||||||
}
|
}
|
||||||
for index, h := range info.RowHeights {
|
for index, h := range info.RowHeights {
|
||||||
color := rowColors[index%len(rowColors)]
|
color := rowColors[index%len(rowColors)]
|
||||||
|
|
@ -238,7 +304,7 @@ func (c *tableChart) Render() (Box, error) {
|
||||||
child.SetBackground(p.Width(), h, color, true)
|
child.SetBackground(p.Width(), h, color, true)
|
||||||
currentHeight += h
|
currentHeight += h
|
||||||
}
|
}
|
||||||
_, err = c.render()
|
_, err := t.render()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return BoxZero, err
|
return BoxZero, err
|
||||||
}
|
}
|
||||||
|
|
@ -248,3 +314,35 @@ func (c *tableChart) Render() (Box, error) {
|
||||||
Bottom: info.Height,
|
Bottom: info.Height,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tableChart) Render() (Box, error) {
|
||||||
|
p := t.p
|
||||||
|
opt := t.opt
|
||||||
|
if !opt.BackgroundColor.IsZero() {
|
||||||
|
p.SetBackground(p.Width(), p.Height(), opt.BackgroundColor)
|
||||||
|
}
|
||||||
|
headerBGColor := opt.HeaderBackgroundColor
|
||||||
|
if headerBGColor.IsZero() {
|
||||||
|
headerBGColor = tableDefaultSetting.HeaderColor
|
||||||
|
}
|
||||||
|
if opt.Font == nil && opt.FontFamily != "" {
|
||||||
|
opt.Font, _ = GetFont(opt.FontFamily)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := p.render
|
||||||
|
fn := chart.PNG
|
||||||
|
if p.outputType == ChartOutputSVG {
|
||||||
|
fn = chart.SVG
|
||||||
|
}
|
||||||
|
newRender, err := fn(p.Width(), 100)
|
||||||
|
if err != nil {
|
||||||
|
return BoxZero, err
|
||||||
|
}
|
||||||
|
p.render = newRender
|
||||||
|
info, err := t.render()
|
||||||
|
if err != nil {
|
||||||
|
return BoxZero, err
|
||||||
|
}
|
||||||
|
p.render = r
|
||||||
|
return t.renderWithInfo(info)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,8 @@ func TestTableChart(t *testing.T) {
|
||||||
1,
|
1,
|
||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
1,
|
// span和header不匹配,最后自动设置为1
|
||||||
|
// 1,
|
||||||
},
|
},
|
||||||
Data: [][]string{
|
Data: [][]string{
|
||||||
{
|
{
|
||||||
|
|
@ -82,6 +83,48 @@ func TestTableChart(t *testing.T) {
|
||||||
}
|
}
|
||||||
return p.Bytes()
|
return p.Bytes()
|
||||||
},
|
},
|
||||||
|
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"600\" height=\"400\">\\n<path d=\"M 0 0\nL 600 0\nL 600 35\nL 0 35\nL 0 0\" style=\"stroke-width:0;stroke:none;fill:rgba(240,240,240,1.0)\"/><path d=\"M 0 35\nL 600 35\nL 600 90\nL 0 90\nL 0 35\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path d=\"M 0 90\nL 600 90\nL 600 125\nL 0 125\nL 0 90\" style=\"stroke-width:0;stroke:none;fill:rgba(247,247,247,1.0)\"/><path d=\"M 0 125\nL 600 125\nL 600 180\nL 0 180\nL 0 125\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><text x=\"10\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(98,105,118,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Name</text><text x=\"110\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(98,105,118,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Age</text><text x=\"210\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(98,105,118,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Address</text><text x=\"410\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(98,105,118,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Tag</text><text x=\"510\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(98,105,118,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Action</text><text x=\"10\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">John</text><text x=\"10\" y=\"77\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Brown</text><text x=\"110\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">32</text><text x=\"210\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">New York No. 1 Lake Park</text><text x=\"410\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">nice,</text><text x=\"410\" y=\"77\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">developer</text><text x=\"510\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text><text x=\"10\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Jim Green</text><text x=\"110\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">42</text><text x=\"210\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">London No. 1 Lake Park</text><text x=\"410\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">wow</text><text x=\"510\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text><text x=\"10\" y=\"147\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Joe Black</text><text x=\"110\" y=\"147\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">32</text><text x=\"210\" y=\"147\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Sidney No. 1 Lake Park</text><text x=\"410\" y=\"147\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">cool,</text><text x=\"410\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">teacher</text><text x=\"510\" y=\"147\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text></svg>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
render: func(p *Painter) ([]byte, error) {
|
||||||
|
_, err := NewTableChart(p, TableChartOption{
|
||||||
|
Header: []string{
|
||||||
|
"Name",
|
||||||
|
"Age",
|
||||||
|
"Address",
|
||||||
|
"Tag",
|
||||||
|
"Action",
|
||||||
|
},
|
||||||
|
Data: [][]string{
|
||||||
|
{
|
||||||
|
"John Brown",
|
||||||
|
"32",
|
||||||
|
"New York No. 1 Lake Park",
|
||||||
|
"nice, developer",
|
||||||
|
"Send Mail",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Jim Green ",
|
||||||
|
"42",
|
||||||
|
"London No. 1 Lake Park",
|
||||||
|
"wow",
|
||||||
|
"Send Mail",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Joe Black ",
|
||||||
|
"32",
|
||||||
|
"Sidney No. 1 Lake Park",
|
||||||
|
"cool, teacher",
|
||||||
|
"Send Mail",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).Render()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return p.Bytes()
|
||||||
|
},
|
||||||
|
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"600\" height=\"400\">\\n<path d=\"M 0 0\nL 600 0\nL 600 35\nL 0 35\nL 0 0\" style=\"stroke-width:0;stroke:none;fill:rgba(240,240,240,1.0)\"/><path d=\"M 0 35\nL 600 35\nL 600 90\nL 0 90\nL 0 35\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><path d=\"M 0 90\nL 600 90\nL 600 145\nL 0 145\nL 0 90\" style=\"stroke-width:0;stroke:none;fill:rgba(247,247,247,1.0)\"/><path d=\"M 0 145\nL 600 145\nL 600 200\nL 0 200\nL 0 145\" style=\"stroke-width:0;stroke:none;fill:rgba(255,255,255,1.0)\"/><text x=\"10\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(98,105,118,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Name</text><text x=\"130\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(98,105,118,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Age</text><text x=\"250\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(98,105,118,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Address</text><text x=\"370\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(98,105,118,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Tag</text><text x=\"490\" y=\"22\" style=\"stroke-width:0;stroke:none;fill:rgba(98,105,118,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Action</text><text x=\"10\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">John Brown</text><text x=\"130\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">32</text><text x=\"250\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">New York No.</text><text x=\"250\" y=\"77\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">1 Lake Park</text><text x=\"370\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">nice,</text><text x=\"370\" y=\"77\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">developer</text><text x=\"490\" y=\"57\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text><text x=\"10\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Jim Green</text><text x=\"130\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">42</text><text x=\"250\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">London No. 1</text><text x=\"250\" y=\"132\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Lake Park</text><text x=\"370\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">wow</text><text x=\"490\" y=\"112\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text><text x=\"10\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Joe Black</text><text x=\"130\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">32</text><text x=\"250\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Sidney No. 1</text><text x=\"250\" y=\"187\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Lake Park</text><text x=\"370\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">cool, teacher</text><text x=\"490\" y=\"167\" style=\"stroke-width:0;stroke:none;fill:rgba(70,70,70,1.0);font-size:15.3px;font-family:'Roboto Medium',sans-serif\">Send Mail</text></svg>",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
@ -96,5 +139,4 @@ func TestTableChart(t *testing.T) {
|
||||||
fmt.Println(string(data))
|
fmt.Println(string(data))
|
||||||
assert.Equal(tt.result, string(data))
|
assert.Equal(tt.result, string(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue