Compare commits
3 Commits
53b65150ce
...
9c278365f2
Author | SHA1 | Date |
---|---|---|
xuri | 9c278365f2 | |
wxy | 307e533061 | |
Patrick Wang | 431c31029e |
21
chart.go
21
chart.go
|
@ -90,6 +90,19 @@ const (
|
|||
ChartLineAutomatic
|
||||
)
|
||||
|
||||
// ChartTickLabelPositionType is the type of supported chart tick label position
|
||||
// types.
|
||||
type ChartTickLabelPositionType byte
|
||||
|
||||
// This section defines the supported chart tick label position types
|
||||
// enumeration.
|
||||
const (
|
||||
ChartTickLabelNextToAxis ChartTickLabelPositionType = iota
|
||||
ChartTickLabelHigh
|
||||
ChartTickLabelLow
|
||||
ChartTickLabelNone
|
||||
)
|
||||
|
||||
// This section defines the default value of chart properties.
|
||||
var (
|
||||
chartView3DRotX = map[ChartType]int{
|
||||
|
@ -484,7 +497,13 @@ var (
|
|||
true: "r",
|
||||
false: "l",
|
||||
}
|
||||
valTickLblPos = map[ChartType]string{
|
||||
tickLblPosVal = map[ChartTickLabelPositionType]string{
|
||||
ChartTickLabelNextToAxis: "nextTo",
|
||||
ChartTickLabelHigh: "high",
|
||||
ChartTickLabelLow: "low",
|
||||
ChartTickLabelNone: "none",
|
||||
}
|
||||
tickLblPosNone = map[ChartType]string{
|
||||
Contour: "none",
|
||||
WireframeContour: "none",
|
||||
}
|
||||
|
|
|
@ -237,7 +237,7 @@ func TestAddChart(t *testing.T) {
|
|||
{sheetName: "Sheet2", cell: "P1", opts: &Chart{Type: Line3D, Series: series2, Format: format, Legend: ChartLegend{Position: "top", ShowLegendKey: false}, Title: []RichTextRun{{Text: "3D Line Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, TickLabelSkip: 1, NumFmt: ChartNumFmt{CustomNumFmt: "General"}}, YAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, MajorUnit: 1, NumFmt: ChartNumFmt{CustomNumFmt: "General"}}}},
|
||||
{sheetName: "Sheet2", cell: "X1", opts: &Chart{Type: Scatter, Series: series, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Scatter Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||
{sheetName: "Sheet2", cell: "P16", opts: &Chart{Type: Doughnut, Series: series3, Format: format, Legend: ChartLegend{Position: "right", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Doughnut Chart"}}, PlotArea: ChartPlotArea{ShowBubbleSize: false, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: false, ShowVal: false}, ShowBlanksAs: "zero", HoleSize: 30}},
|
||||
{sheetName: "Sheet2", cell: "X16", opts: &Chart{Type: Line, Series: series2, Format: format, Legend: ChartLegend{Position: "top", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Line Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, TickLabelSkip: 1}, YAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, MajorUnit: 1}}},
|
||||
{sheetName: "Sheet2", cell: "X16", opts: &Chart{Type: Line, Series: series2, Format: format, Legend: ChartLegend{Position: "top", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Line Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, TickLabelSkip: 1, TickLabelPosition: ChartTickLabelLow}, YAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, MajorUnit: 1}}},
|
||||
{sheetName: "Sheet2", cell: "P32", opts: &Chart{Type: Pie3D, Series: series3, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: []RichTextRun{{Text: "3D Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
|
||||
{sheetName: "Sheet2", cell: "X32", opts: &Chart{Type: Pie, Series: series3, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Pie Chart"}}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: false, ShowVal: false, NumFmt: ChartNumFmt{CustomNumFmt: "0.00%;0;;"}}, ShowBlanksAs: "gap"}},
|
||||
// bar series chart
|
||||
|
|
|
@ -1001,7 +1001,7 @@ func (f *File) drawPlotAreaCatAx(pa *cPlotArea, opts *Chart) []*cAxs {
|
|||
MajorTickMark: &attrValString{Val: stringPtr("none")},
|
||||
MinorTickMark: &attrValString{Val: stringPtr("none")},
|
||||
Title: f.drawPlotAreaTitles(opts.XAxis.Title, ""),
|
||||
TickLblPos: &attrValString{Val: stringPtr("nextTo")},
|
||||
TickLblPos: &attrValString{Val: stringPtr(tickLblPosVal[opts.XAxis.TickLabelPosition])},
|
||||
SpPr: f.drawPlotAreaSpPr(),
|
||||
TxPr: f.drawPlotAreaTxPr(&opts.XAxis),
|
||||
CrossAx: &attrValInt{Val: intPtr(100000001)},
|
||||
|
@ -1063,7 +1063,7 @@ func (f *File) drawPlotAreaValAx(pa *cPlotArea, opts *Chart) []*cAxs {
|
|||
},
|
||||
MajorTickMark: &attrValString{Val: stringPtr("none")},
|
||||
MinorTickMark: &attrValString{Val: stringPtr("none")},
|
||||
TickLblPos: &attrValString{Val: stringPtr("nextTo")},
|
||||
TickLblPos: &attrValString{Val: stringPtr(tickLblPosVal[opts.YAxis.TickLabelPosition])},
|
||||
SpPr: f.drawPlotAreaSpPr(),
|
||||
TxPr: f.drawPlotAreaTxPr(&opts.YAxis),
|
||||
CrossAx: &attrValInt{Val: intPtr(100000000)},
|
||||
|
@ -1079,7 +1079,7 @@ func (f *File) drawPlotAreaValAx(pa *cPlotArea, opts *Chart) []*cAxs {
|
|||
if opts.YAxis.MinorGridLines {
|
||||
ax.MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
|
||||
}
|
||||
if pos, ok := valTickLblPos[opts.Type]; ok {
|
||||
if pos, ok := tickLblPosNone[opts.Type]; ok {
|
||||
ax.TickLblPos.Val = stringPtr(pos)
|
||||
}
|
||||
if opts.YAxis.MajorUnit != 0 {
|
||||
|
@ -1115,7 +1115,7 @@ func (f *File) drawPlotAreaSerAx(opts *Chart) []*cAxs {
|
|||
},
|
||||
Delete: &attrValBool{Val: boolPtr(opts.YAxis.None)},
|
||||
AxPos: &attrValString{Val: stringPtr(catAxPos[opts.XAxis.ReverseOrder])},
|
||||
TickLblPos: &attrValString{Val: stringPtr("nextTo")},
|
||||
TickLblPos: &attrValString{Val: stringPtr(tickLblPosVal[opts.YAxis.TickLabelPosition])},
|
||||
SpPr: f.drawPlotAreaSpPr(),
|
||||
TxPr: f.drawPlotAreaTxPr(nil),
|
||||
CrossAx: &attrValInt{Val: intPtr(100000001)},
|
||||
|
|
32
numfmt.go
32
numfmt.go
|
@ -4799,10 +4799,8 @@ func format(value, numFmt string, date1904 bool, cellType CellType, opts *Option
|
|||
switch section.Type {
|
||||
case nfp.TokenSectionPositive:
|
||||
return nf.alignmentHandler(nf.positiveHandler())
|
||||
case nfp.TokenSectionNegative:
|
||||
return nf.alignmentHandler(nf.negativeHandler())
|
||||
default:
|
||||
return nf.alignmentHandler(nf.zeroHandler())
|
||||
return nf.alignmentHandler(nf.negativeHandler())
|
||||
}
|
||||
}
|
||||
return nf.alignmentHandler(nf.textHandler())
|
||||
|
@ -7108,11 +7106,6 @@ func (nf *numberFormat) negativeHandler() (result string) {
|
|||
return nf.numberHandler()
|
||||
}
|
||||
|
||||
// zeroHandler will be handling zero selection for a number format expression.
|
||||
func (nf *numberFormat) zeroHandler() string {
|
||||
return nf.value
|
||||
}
|
||||
|
||||
// textHandler will be handling text selection for a number format expression.
|
||||
func (nf *numberFormat) textHandler() (result string) {
|
||||
for _, token := range nf.section[nf.sectionIdx].Items {
|
||||
|
@ -7137,21 +7130,18 @@ func (nf *numberFormat) getValueSectionType(value string) (float64, string) {
|
|||
return 0, nfp.TokenSectionText
|
||||
}
|
||||
number, _ := strconv.ParseFloat(value, 64)
|
||||
if number > 0 {
|
||||
if number >= 0 {
|
||||
return number, nfp.TokenSectionPositive
|
||||
}
|
||||
if number < 0 {
|
||||
var hasNeg bool
|
||||
for _, sec := range nf.section {
|
||||
if sec.Type == nfp.TokenSectionNegative {
|
||||
hasNeg = true
|
||||
}
|
||||
var hasNeg bool
|
||||
for _, sec := range nf.section {
|
||||
if sec.Type == nfp.TokenSectionNegative {
|
||||
hasNeg = true
|
||||
}
|
||||
if !hasNeg {
|
||||
nf.usePositive = true
|
||||
return number, nfp.TokenSectionPositive
|
||||
}
|
||||
return number, nfp.TokenSectionNegative
|
||||
}
|
||||
return number, nfp.TokenSectionZero
|
||||
if !hasNeg {
|
||||
nf.usePositive = true
|
||||
return number, nfp.TokenSectionPositive
|
||||
}
|
||||
return number, nfp.TokenSectionNegative
|
||||
}
|
||||
|
|
|
@ -69,7 +69,10 @@ func TestNumFmt(t *testing.T) {
|
|||
{"0.97952546296296295", "h:m", "23:30"},
|
||||
{"43528", "mmmm", "March"},
|
||||
{"43528", "dddd", "Monday"},
|
||||
{"0", ";;;", "0"},
|
||||
{"0", ";;;", ""},
|
||||
{"0", "0%", "0%"},
|
||||
{"0", "0.0%", "0.0%"},
|
||||
{"0", "0.00%", "0.00%"},
|
||||
{"43528", "[$-409]MM/DD/YYYY", "03/04/2019"},
|
||||
{"43528", "[$-409]MM/DD/YYYY am/pm", "03/04/2019 AM"},
|
||||
{"43528", "[$-111]MM/DD/YYYY", "43528"},
|
||||
|
|
|
@ -260,6 +260,9 @@ func (f *File) getTableFieldsOrder(opts *PivotTableOptions) ([]string, error) {
|
|||
if err != nil {
|
||||
return order, err
|
||||
}
|
||||
if name == "" {
|
||||
return order, ErrParameterInvalid
|
||||
}
|
||||
order = append(order, name)
|
||||
}
|
||||
return order, nil
|
||||
|
@ -272,8 +275,10 @@ func (f *File) addPivotCache(opts *PivotTableOptions) error {
|
|||
if err != nil {
|
||||
return newPivotTableDataRangeError(err.Error())
|
||||
}
|
||||
// data range has been checked
|
||||
order, _ := f.getTableFieldsOrder(opts)
|
||||
order, err := f.getTableFieldsOrder(opts)
|
||||
if err != nil {
|
||||
return newPivotTableDataRangeError(err.Error())
|
||||
}
|
||||
topLeftCell, _ := CoordinatesToCellName(coordinates[0], coordinates[1])
|
||||
bottomRightCell, _ := CoordinatesToCellName(coordinates[2], coordinates[3])
|
||||
pc := xlsxPivotCacheDefinition{
|
||||
|
|
|
@ -300,6 +300,7 @@ func TestPivotTable(t *testing.T) {
|
|||
assert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`)
|
||||
// Test add pivot table with unsupported charset content types.
|
||||
f = NewFile()
|
||||
assert.NoError(t, f.SetSheetRow("Sheet1", "A1", &[]string{"Month", "Year", "Type", "Sales", "Region"}))
|
||||
f.ContentTypes = nil
|
||||
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||
assert.EqualError(t, f.AddPivotTable(&PivotTableOptions{
|
||||
|
@ -393,6 +394,25 @@ func TestPivotTableDataRange(t *testing.T) {
|
|||
f.Relationships.Delete("xl/worksheets/_rels/sheet1.xml.rels")
|
||||
f.Pkg.Delete("xl/worksheets/_rels/sheet1.xml.rels")
|
||||
assert.EqualError(t, f.DeletePivotTable("Sheet1", "PivotTable1"), "table PivotTable1 does not exist")
|
||||
|
||||
t.Run("data_range_with_empty_column", func(t *testing.T) {
|
||||
// Test add pivot table with data range doesn't organized as a list with labeled columns
|
||||
f := NewFile()
|
||||
// Create some data in a sheet
|
||||
month := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
|
||||
types := []string{"Meat", "Dairy", "Beverages", "Produce"}
|
||||
assert.NoError(t, f.SetSheetRow("Sheet1", "A1", &[]string{"Month", "", "Type"}))
|
||||
for row := 2; row < 32; row++ {
|
||||
assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("A%d", row), month[rand.Intn(12)]))
|
||||
assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("C%d", row), types[rand.Intn(4)]))
|
||||
}
|
||||
assert.Equal(t, newPivotTableDataRangeError("parameter is invalid"), f.AddPivotTable(&PivotTableOptions{
|
||||
DataRange: "Sheet1!A1:E31",
|
||||
PivotTableRange: "Sheet1!G2:M34",
|
||||
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}},
|
||||
Data: []PivotTableField{{Data: "Type"}},
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseFormatPivotTableSet(t *testing.T) {
|
||||
|
|
29
xmlChart.go
29
xmlChart.go
|
@ -530,20 +530,21 @@ type ChartNumFmt struct {
|
|||
|
||||
// ChartAxis directly maps the format settings of the chart axis.
|
||||
type ChartAxis struct {
|
||||
None bool
|
||||
MajorGridLines bool
|
||||
MinorGridLines bool
|
||||
MajorUnit float64
|
||||
TickLabelSkip int
|
||||
ReverseOrder bool
|
||||
Secondary bool
|
||||
Maximum *float64
|
||||
Minimum *float64
|
||||
Font Font
|
||||
LogBase float64
|
||||
NumFmt ChartNumFmt
|
||||
Title []RichTextRun
|
||||
axID int
|
||||
None bool
|
||||
MajorGridLines bool
|
||||
MinorGridLines bool
|
||||
MajorUnit float64
|
||||
TickLabelPosition ChartTickLabelPositionType
|
||||
TickLabelSkip int
|
||||
ReverseOrder bool
|
||||
Secondary bool
|
||||
Maximum *float64
|
||||
Minimum *float64
|
||||
Font Font
|
||||
LogBase float64
|
||||
NumFmt ChartNumFmt
|
||||
Title []RichTextRun
|
||||
axID int
|
||||
}
|
||||
|
||||
// ChartDimension directly maps the dimension of the chart.
|
||||
|
|
Loading…
Reference in New Issue