Compare commits

...

4 Commits

Author SHA1 Message Date
xuri 8c0ef7f90d
This closes #1983, support create combo chart with same types
- Add new exported ChartLineUnset enumeration
- Fix set line type of line chart does not work
- Support set line type of scatter chart
2024-09-05 21:38:19 +08:00
Ben Smith aca04ecf57
This closes #1987, support absolute paths for pictures (#1988) 2024-09-04 19:47:02 +08:00
Zhang Zhipeng 0447cb22c8
This close #1985, fix incorrect result of cell value after apply general number format (#1986)
- Update unit tests
2024-09-02 09:19:50 +08:00
xuri 29366fd126
Add new fields for pivot table options and pivot field options
- Add new fields FieldPrintTitles and ItemPrintTitles in the PivotTableOptions data type
- Add new fields ShowAll and InsertBlankRow in the PivotTableField data type
- Export 4 constants ExtURIDataField, ExtURIPivotField, ExtURIPivotFilter and ExtURIPivotHierarchy
- Update unit tests
- Update dependencies modules
2024-09-01 12:10:01 +08:00
12 changed files with 284 additions and 205 deletions

View File

@ -85,7 +85,8 @@ type ChartLineType byte
// This section defines the currently supported chart line types enumeration.
const (
ChartLineSolid ChartLineType = iota
ChartLineUnset ChartLineType = iota
ChartLineSolid
ChartLineNone
ChartLineAutomatic
)

View File

@ -176,7 +176,12 @@ func (f *File) addChart(opts *Chart, comboCharts []*Chart) {
if field.IsNil() {
continue
}
immutable.FieldByName(mutable.Type().Field(i).Name).Set(field)
fld := immutable.FieldByName(mutable.Type().Field(i).Name)
if field.Kind() == reflect.Slice && i < 16 { // All []*cCharts type fields
fld.Set(reflect.Append(fld, field.Index(0)))
continue
}
fld.Set(field)
}
}
addChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[opts.Type](xlsxChartSpace.Chart.PlotArea, opts))
@ -194,239 +199,241 @@ func (f *File) addChart(opts *Chart, comboCharts []*Chart) {
// drawBaseChart provides a function to draw the c:plotArea element for bar,
// and column series charts by given format sets.
func (f *File) drawBaseChart(pa *cPlotArea, opts *Chart) *cPlotArea {
c := cCharts{
BarDir: &attrValString{
Val: stringPtr("col"),
c := []*cCharts{
{
BarDir: &attrValString{
Val: stringPtr("col"),
},
Grouping: &attrValString{
Val: stringPtr(plotAreaChartGrouping[opts.Type]),
},
VaryColors: &attrValBool{
Val: opts.VaryColors,
},
Ser: f.drawChartSeries(opts),
Shape: f.drawChartShape(opts),
DLbls: f.drawChartDLbls(opts),
AxID: f.genAxID(opts),
Overlap: &attrValInt{Val: intPtr(100)},
},
Grouping: &attrValString{
Val: stringPtr(plotAreaChartGrouping[opts.Type]),
},
VaryColors: &attrValBool{
Val: opts.VaryColors,
},
Ser: f.drawChartSeries(opts),
Shape: f.drawChartShape(opts),
DLbls: f.drawChartDLbls(opts),
AxID: f.genAxID(opts),
Overlap: &attrValInt{Val: intPtr(100)},
}
var ok bool
if *c.BarDir.Val, ok = plotAreaChartBarDir[opts.Type]; !ok {
c.BarDir = nil
if *c[0].BarDir.Val, ok = plotAreaChartBarDir[opts.Type]; !ok {
c[0].BarDir = nil
}
if *c.Overlap.Val, ok = plotAreaChartOverlap[opts.Type]; !ok {
c.Overlap = nil
if *c[0].Overlap.Val, ok = plotAreaChartOverlap[opts.Type]; !ok {
c[0].Overlap = nil
}
catAx := f.drawPlotAreaCatAx(pa, opts)
valAx := f.drawPlotAreaValAx(pa, opts)
charts := map[ChartType]*cPlotArea{
Area: {
AreaChart: &c,
AreaChart: c,
CatAx: catAx,
ValAx: valAx,
},
AreaStacked: {
AreaChart: &c,
AreaChart: c,
CatAx: catAx,
ValAx: valAx,
},
AreaPercentStacked: {
AreaChart: &c,
AreaChart: c,
CatAx: catAx,
ValAx: valAx,
},
Area3D: {
Area3DChart: &c,
Area3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Area3DStacked: {
Area3DChart: &c,
Area3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Area3DPercentStacked: {
Area3DChart: &c,
Area3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Bar: {
BarChart: &c,
BarChart: c,
CatAx: catAx,
ValAx: valAx,
},
BarStacked: {
BarChart: &c,
BarChart: c,
CatAx: catAx,
ValAx: valAx,
},
BarPercentStacked: {
BarChart: &c,
BarChart: c,
CatAx: catAx,
ValAx: valAx,
},
Bar3DClustered: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Bar3DStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Bar3DPercentStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Bar3DConeClustered: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Bar3DConeStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Bar3DConePercentStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Bar3DPyramidClustered: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Bar3DPyramidStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Bar3DPyramidPercentStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Bar3DCylinderClustered: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Bar3DCylinderStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Bar3DCylinderPercentStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col: {
BarChart: &c,
BarChart: c,
CatAx: catAx,
ValAx: valAx,
},
ColStacked: {
BarChart: &c,
BarChart: c,
CatAx: catAx,
ValAx: valAx,
},
ColPercentStacked: {
BarChart: &c,
BarChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3D: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3DClustered: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3DStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3DPercentStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3DCone: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3DConeClustered: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3DConeStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3DConePercentStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3DPyramid: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3DPyramidClustered: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3DPyramidStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3DPyramidPercentStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3DCylinder: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3DCylinderClustered: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3DCylinderStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Col3DCylinderPercentStacked: {
Bar3DChart: &c,
Bar3DChart: c,
CatAx: catAx,
ValAx: valAx,
},
Bubble: {
BubbleChart: &c,
BubbleChart: c,
CatAx: catAx,
ValAx: valAx,
},
Bubble3D: {
BubbleChart: &c,
BubbleChart: c,
CatAx: catAx,
ValAx: valAx,
},
@ -443,12 +450,14 @@ func (f *File) drawDoughnutChart(pa *cPlotArea, opts *Chart) *cPlotArea {
}
return &cPlotArea{
DoughnutChart: &cCharts{
VaryColors: &attrValBool{
Val: opts.VaryColors,
DoughnutChart: []*cCharts{
{
VaryColors: &attrValBool{
Val: opts.VaryColors,
},
Ser: f.drawChartSeries(opts),
HoleSize: &attrValInt{Val: intPtr(holeSize)},
},
Ser: f.drawChartSeries(opts),
HoleSize: &attrValInt{Val: intPtr(holeSize)},
},
}
}
@ -457,16 +466,18 @@ func (f *File) drawDoughnutChart(pa *cPlotArea, opts *Chart) *cPlotArea {
// chart by given format sets.
func (f *File) drawLineChart(pa *cPlotArea, opts *Chart) *cPlotArea {
return &cPlotArea{
LineChart: &cCharts{
Grouping: &attrValString{
Val: stringPtr(plotAreaChartGrouping[opts.Type]),
LineChart: []*cCharts{
{
Grouping: &attrValString{
Val: stringPtr(plotAreaChartGrouping[opts.Type]),
},
VaryColors: &attrValBool{
Val: boolPtr(false),
},
Ser: f.drawChartSeries(opts),
DLbls: f.drawChartDLbls(opts),
AxID: f.genAxID(opts),
},
VaryColors: &attrValBool{
Val: boolPtr(false),
},
Ser: f.drawChartSeries(opts),
DLbls: f.drawChartDLbls(opts),
AxID: f.genAxID(opts),
},
CatAx: f.drawPlotAreaCatAx(pa, opts),
ValAx: f.drawPlotAreaValAx(pa, opts),
@ -477,16 +488,18 @@ func (f *File) drawLineChart(pa *cPlotArea, opts *Chart) *cPlotArea {
// chart by given format sets.
func (f *File) drawLine3DChart(pa *cPlotArea, opts *Chart) *cPlotArea {
return &cPlotArea{
Line3DChart: &cCharts{
Grouping: &attrValString{
Val: stringPtr(plotAreaChartGrouping[opts.Type]),
Line3DChart: []*cCharts{
{
Grouping: &attrValString{
Val: stringPtr(plotAreaChartGrouping[opts.Type]),
},
VaryColors: &attrValBool{
Val: boolPtr(false),
},
Ser: f.drawChartSeries(opts),
DLbls: f.drawChartDLbls(opts),
AxID: f.genAxID(opts),
},
VaryColors: &attrValBool{
Val: boolPtr(false),
},
Ser: f.drawChartSeries(opts),
DLbls: f.drawChartDLbls(opts),
AxID: f.genAxID(opts),
},
CatAx: f.drawPlotAreaCatAx(pa, opts),
ValAx: f.drawPlotAreaValAx(pa, opts),
@ -497,11 +510,13 @@ func (f *File) drawLine3DChart(pa *cPlotArea, opts *Chart) *cPlotArea {
// chart by given format sets.
func (f *File) drawPieChart(pa *cPlotArea, opts *Chart) *cPlotArea {
return &cPlotArea{
PieChart: &cCharts{
VaryColors: &attrValBool{
Val: opts.VaryColors,
PieChart: []*cCharts{
{
VaryColors: &attrValBool{
Val: opts.VaryColors,
},
Ser: f.drawChartSeries(opts),
},
Ser: f.drawChartSeries(opts),
},
}
}
@ -510,11 +525,13 @@ func (f *File) drawPieChart(pa *cPlotArea, opts *Chart) *cPlotArea {
// pie chart by given format sets.
func (f *File) drawPie3DChart(pa *cPlotArea, opts *Chart) *cPlotArea {
return &cPlotArea{
Pie3DChart: &cCharts{
VaryColors: &attrValBool{
Val: opts.VaryColors,
Pie3DChart: []*cCharts{
{
VaryColors: &attrValBool{
Val: opts.VaryColors,
},
Ser: f.drawChartSeries(opts),
},
Ser: f.drawChartSeries(opts),
},
}
}
@ -527,16 +544,18 @@ func (f *File) drawPieOfPieChart(pa *cPlotArea, opts *Chart) *cPlotArea {
splitPos = &attrValInt{Val: intPtr(opts.PlotArea.SecondPlotValues)}
}
return &cPlotArea{
OfPieChart: &cCharts{
OfPieType: &attrValString{
Val: stringPtr("pie"),
OfPieChart: []*cCharts{
{
OfPieType: &attrValString{
Val: stringPtr("pie"),
},
VaryColors: &attrValBool{
Val: opts.VaryColors,
},
Ser: f.drawChartSeries(opts),
SplitPos: splitPos,
SerLines: &attrValString{},
},
VaryColors: &attrValBool{
Val: opts.VaryColors,
},
Ser: f.drawChartSeries(opts),
SplitPos: splitPos,
SerLines: &attrValString{},
},
}
}
@ -549,16 +568,18 @@ func (f *File) drawBarOfPieChart(pa *cPlotArea, opts *Chart) *cPlotArea {
splitPos = &attrValInt{Val: intPtr(opts.PlotArea.SecondPlotValues)}
}
return &cPlotArea{
OfPieChart: &cCharts{
OfPieType: &attrValString{
Val: stringPtr("bar"),
OfPieChart: []*cCharts{
{
OfPieType: &attrValString{
Val: stringPtr("bar"),
},
VaryColors: &attrValBool{
Val: opts.VaryColors,
},
SplitPos: splitPos,
Ser: f.drawChartSeries(opts),
SerLines: &attrValString{},
},
VaryColors: &attrValBool{
Val: opts.VaryColors,
},
SplitPos: splitPos,
Ser: f.drawChartSeries(opts),
SerLines: &attrValString{},
},
}
}
@ -567,16 +588,18 @@ func (f *File) drawBarOfPieChart(pa *cPlotArea, opts *Chart) *cPlotArea {
// chart by given format sets.
func (f *File) drawRadarChart(pa *cPlotArea, opts *Chart) *cPlotArea {
return &cPlotArea{
RadarChart: &cCharts{
RadarStyle: &attrValString{
Val: stringPtr("marker"),
RadarChart: []*cCharts{
{
RadarStyle: &attrValString{
Val: stringPtr("marker"),
},
VaryColors: &attrValBool{
Val: boolPtr(false),
},
Ser: f.drawChartSeries(opts),
DLbls: f.drawChartDLbls(opts),
AxID: f.genAxID(opts),
},
VaryColors: &attrValBool{
Val: boolPtr(false),
},
Ser: f.drawChartSeries(opts),
DLbls: f.drawChartDLbls(opts),
AxID: f.genAxID(opts),
},
CatAx: f.drawPlotAreaCatAx(pa, opts),
ValAx: f.drawPlotAreaValAx(pa, opts),
@ -587,16 +610,18 @@ func (f *File) drawRadarChart(pa *cPlotArea, opts *Chart) *cPlotArea {
// scatter chart by given format sets.
func (f *File) drawScatterChart(pa *cPlotArea, opts *Chart) *cPlotArea {
return &cPlotArea{
ScatterChart: &cCharts{
ScatterStyle: &attrValString{
Val: stringPtr("smoothMarker"), // line,lineMarker,marker,none,smooth,smoothMarker
ScatterChart: []*cCharts{
{
ScatterStyle: &attrValString{
Val: stringPtr("smoothMarker"), // line,lineMarker,marker,none,smooth,smoothMarker
},
VaryColors: &attrValBool{
Val: boolPtr(false),
},
Ser: f.drawChartSeries(opts),
DLbls: f.drawChartDLbls(opts),
AxID: f.genAxID(opts),
},
VaryColors: &attrValBool{
Val: boolPtr(false),
},
Ser: f.drawChartSeries(opts),
DLbls: f.drawChartDLbls(opts),
AxID: f.genAxID(opts),
},
ValAx: append(f.drawPlotAreaCatAx(pa, opts), f.drawPlotAreaValAx(pa, opts)...),
}
@ -606,12 +631,14 @@ func (f *File) drawScatterChart(pa *cPlotArea, opts *Chart) *cPlotArea {
// given format sets.
func (f *File) drawSurface3DChart(pa *cPlotArea, opts *Chart) *cPlotArea {
plotArea := &cPlotArea{
Surface3DChart: &cCharts{
Ser: f.drawChartSeries(opts),
AxID: []*attrValInt{
{Val: intPtr(100000000)},
{Val: intPtr(100000001)},
{Val: intPtr(100000005)},
Surface3DChart: []*cCharts{
{
Ser: f.drawChartSeries(opts),
AxID: []*attrValInt{
{Val: intPtr(100000000)},
{Val: intPtr(100000001)},
{Val: intPtr(100000005)},
},
},
},
CatAx: f.drawPlotAreaCatAx(pa, opts),
@ -619,7 +646,7 @@ func (f *File) drawSurface3DChart(pa *cPlotArea, opts *Chart) *cPlotArea {
SerAx: f.drawPlotAreaSerAx(opts),
}
if opts.Type == WireframeSurface3D {
plotArea.Surface3DChart.Wireframe = &attrValBool{Val: boolPtr(true)}
plotArea.Surface3DChart[0].Wireframe = &attrValBool{Val: boolPtr(true)}
}
return plotArea
}
@ -628,12 +655,14 @@ func (f *File) drawSurface3DChart(pa *cPlotArea, opts *Chart) *cPlotArea {
// given format sets.
func (f *File) drawSurfaceChart(pa *cPlotArea, opts *Chart) *cPlotArea {
plotArea := &cPlotArea{
SurfaceChart: &cCharts{
Ser: f.drawChartSeries(opts),
AxID: []*attrValInt{
{Val: intPtr(100000000)},
{Val: intPtr(100000001)},
{Val: intPtr(100000005)},
SurfaceChart: []*cCharts{
{
Ser: f.drawChartSeries(opts),
AxID: []*attrValInt{
{Val: intPtr(100000000)},
{Val: intPtr(100000001)},
{Val: intPtr(100000005)},
},
},
},
CatAx: f.drawPlotAreaCatAx(pa, opts),
@ -641,7 +670,7 @@ func (f *File) drawSurfaceChart(pa *cPlotArea, opts *Chart) *cPlotArea {
SerAx: f.drawPlotAreaSerAx(opts),
}
if opts.Type == WireframeContour {
plotArea.SurfaceChart.Wireframe = &attrValBool{Val: boolPtr(true)}
plotArea.SurfaceChart[0].Wireframe = &attrValBool{Val: boolPtr(true)}
}
return plotArea
}
@ -650,18 +679,20 @@ func (f *File) drawSurfaceChart(pa *cPlotArea, opts *Chart) *cPlotArea {
// given format sets.
func (f *File) drawBubbleChart(pa *cPlotArea, opts *Chart) *cPlotArea {
plotArea := &cPlotArea{
BubbleChart: &cCharts{
VaryColors: &attrValBool{
Val: opts.VaryColors,
BubbleChart: []*cCharts{
{
VaryColors: &attrValBool{
Val: opts.VaryColors,
},
Ser: f.drawChartSeries(opts),
DLbls: f.drawChartDLbls(opts),
AxID: f.genAxID(opts),
},
Ser: f.drawChartSeries(opts),
DLbls: f.drawChartDLbls(opts),
AxID: f.genAxID(opts),
},
ValAx: append(f.drawPlotAreaCatAx(pa, opts), f.drawPlotAreaValAx(pa, opts)...),
}
if opts.BubbleSize > 0 && opts.BubbleSize <= 300 {
plotArea.BubbleChart.BubbleScale = &attrValFloat{Val: float64Ptr(float64(opts.BubbleSize))}
plotArea.BubbleChart[0].BubbleScale = &attrValFloat{Val: float64Ptr(float64(opts.BubbleSize))}
}
return plotArea
}
@ -750,23 +781,19 @@ func (f *File) drawShapeFill(fill Fill, spPr *cSpPr) *cSpPr {
func (f *File) drawChartSeriesSpPr(i int, opts *Chart) *cSpPr {
spPr := &cSpPr{SolidFill: &aSolidFill{SchemeClr: &aSchemeClr{Val: "accent" + strconv.Itoa((opts.order+i)%6+1)}}}
spPr = f.drawShapeFill(opts.Series[i].Fill, spPr)
spPrScatter := &cSpPr{
Ln: &aLn{
W: 25400,
NoFill: &attrValString{},
},
}
spPrLine := &cSpPr{
solid := &cSpPr{
Ln: &aLn{
W: f.ptToEMUs(opts.Series[i].Line.Width),
Cap: "rnd", // rnd, sq, flat
SolidFill: spPr.SolidFill,
},
}
if chartSeriesSpPr, ok := map[ChartType]*cSpPr{
Line: spPrLine, Scatter: spPrScatter,
noLn := &cSpPr{Ln: &aLn{NoFill: &attrValString{}}}
if chartSeriesSpPr, ok := map[ChartType]map[ChartLineType]*cSpPr{
Line: {ChartLineUnset: solid, ChartLineSolid: solid, ChartLineNone: noLn, ChartLineAutomatic: solid},
Scatter: {ChartLineUnset: noLn, ChartLineSolid: solid, ChartLineNone: noLn, ChartLineAutomatic: noLn},
}[opts.Type]; ok {
return chartSeriesSpPr
return chartSeriesSpPr[opts.Series[i].Line.Type]
}
if spPr.SolidFill.SrgbClr != nil {
return spPr

6
go.mod
View File

@ -8,10 +8,10 @@ require (
github.com/stretchr/testify v1.8.4
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7
golang.org/x/crypto v0.25.0
golang.org/x/crypto v0.26.0
golang.org/x/image v0.18.0
golang.org/x/net v0.27.0
golang.org/x/text v0.16.0
golang.org/x/net v0.28.0
golang.org/x/text v0.17.0
)
require (

12
go.sum
View File

@ -15,14 +15,14 @@ github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d h1:llb0neMWDQe87IzJLS4Ci7
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A=
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@ -5136,7 +5136,10 @@ func (nf *numberFormat) positiveHandler() string {
var fmtNum bool
for _, token := range nf.section[nf.sectionIdx].Items {
if token.TType == nfp.TokenTypeGeneral {
return strconv.FormatFloat(nf.number, 'G', 10, 64)
if isNum, precision, _ := isNumeric(nf.value); isNum && precision > 11 {
return strconv.FormatFloat(nf.number, 'G', 10, 64)
}
return nf.value
}
if inStrSlice(supportedNumberTokenTypes, token.TType, true) != -1 {
fmtNum = true

View File

@ -11,6 +11,7 @@ func TestNumFmt(t *testing.T) {
for _, item := range [][]string{
{"123", "general", "123"},
{"-123", ";general", "-123"},
{"12345678901", "General", "12345678901"},
{"43543.5448726851", "General", "43543.54487"},
{"-43543.5448726851", "General", "-43543.54487"},
{"1234567890.12345", "General", "1234567890"},

View File

@ -497,8 +497,7 @@ func (f *File) GetPictures(sheet, cell string) ([]Picture, error) {
target := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)
drawingXML := strings.TrimPrefix(strings.ReplaceAll(target, "..", "xl"), "/")
drawingRelationships := strings.ReplaceAll(
strings.ReplaceAll(target, "../drawings", "xl/drawings/_rels"), ".xml", ".xml.rels")
strings.ReplaceAll(drawingXML, "xl/drawings", "xl/drawings/_rels"), ".xml", ".xml.rels")
imgs, err := f.getCellImages(sheet, cell)
if err != nil {
return nil, err
@ -526,7 +525,8 @@ func (f *File) GetPictureCells(sheet string) ([]string, error) {
target := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)
drawingXML := strings.TrimPrefix(strings.ReplaceAll(target, "..", "xl"), "/")
drawingRelationships := strings.ReplaceAll(
strings.ReplaceAll(target, "../drawings", "xl/drawings/_rels"), ".xml", ".xml.rels")
strings.ReplaceAll(drawingXML, "xl/drawings", "xl/drawings/_rels"), ".xml", ".xml.rels")
embeddedImageCells, err := f.getImageCells(sheet)
if err != nil {
return nil, err
@ -609,8 +609,15 @@ func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string)
}
}
cb2 := func(a *decodeCellAnchor, r *xlsxRelationship) {
pic := Picture{Extension: filepath.Ext(r.Target), Format: &GraphicOptions{}, InsertType: PictureInsertTypePlaceOverCells}
if buffer, _ := f.Pkg.Load(filepath.ToSlash(filepath.Clean("xl/drawings/" + r.Target))); buffer != nil {
var target string
if strings.HasPrefix(r.Target, "/") {
target = strings.TrimPrefix(r.Target, "/")
} else {
target = filepath.ToSlash(filepath.Clean("xl/drawings/" + r.Target))
}
pic := Picture{Extension: filepath.Ext(target), Format: &GraphicOptions{}, InsertType: PictureInsertTypePlaceOverCells}
if buffer, _ := f.Pkg.Load(target); buffer != nil {
pic.File = buffer.([]byte)
pic.Format.AltText = a.Pic.NvPicPr.CNvPr.Descr
pics = append(pics, pic)
@ -770,7 +777,14 @@ func (f *File) getPictureCells(drawingXML, drawingRelationships string) ([]strin
}
}
cb2 := func(a *decodeCellAnchor, r *xlsxRelationship) {
if _, ok := f.Pkg.Load(filepath.ToSlash(filepath.Clean("xl/drawings/" + r.Target))); ok {
var target string
if strings.HasPrefix(r.Target, "/") {
target = strings.TrimPrefix(r.Target, "/")
} else {
target = filepath.ToSlash(filepath.Clean("xl/drawings/" + r.Target))
}
if _, ok := f.Pkg.Load(target); ok {
if cell, err := CoordinatesToCellName(a.From.Col+1, a.From.Row+1); err == nil && inStrSlice(cells, cell, true) == -1 {
cells = append(cells, cell)
}

View File

@ -219,6 +219,19 @@ func TestGetPicture(t *testing.T) {
cells, err := f.GetPictureCells("Sheet2")
assert.NoError(t, err)
assert.Equal(t, []string{"K16"}, cells)
// Try to get picture cells with absolute target path in the drawing relationship
rels, err := f.relsReader("xl/drawings/_rels/drawing2.xml.rels")
assert.NoError(t, err)
rels.Relationships[0].Target = "/xl/media/image2.jpeg"
cells, err = f.GetPictureCells("Sheet2")
assert.NoError(t, err)
assert.Equal(t, []string{"K16"}, cells)
// Try to get pictures with absolute target path in the drawing relationship
pics, err = f.GetPictures("Sheet2", "K16")
assert.NoError(t, err)
assert.Len(t, pics, 1)
assert.NoError(t, f.Close())
// Test get picture from none drawing worksheet

View File

@ -58,6 +58,8 @@ type PivotTableOptions struct {
ShowRowStripes bool
ShowColStripes bool
ShowLastColumn bool
FieldPrintTitles bool
ItemPrintTitles bool
PivotTableStyleName string
}
@ -90,6 +92,8 @@ type PivotTableField struct {
Data string
Name string
Outline bool
ShowAll bool
InsertBlankRow bool
Subtotal string
DefaultSubtotal bool
NumFmt int
@ -349,6 +353,8 @@ func (f *File) addPivotTable(cacheID, pivotTableID int, opts *PivotTableOptions)
CreatedVersion: pivotTableVersion,
CompactData: &opts.CompactData,
ShowError: &opts.ShowError,
FieldPrintTitles: opts.FieldPrintTitles,
ItemPrintTitles: opts.ItemPrintTitles,
DataCaption: "Values",
Location: &xlsxLocation{
Ref: topLeftCell + ":" + bottomRightCell,
@ -555,6 +561,8 @@ func (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opts *PivotTableOpti
DataField: inPivotTableField(opts.Data, name) != -1,
Compact: &rowOptions.Compact,
Outline: &rowOptions.Outline,
ShowAll: rowOptions.ShowAll,
InsertBlankRow: rowOptions.InsertBlankRow,
DefaultSubtotal: &rowOptions.DefaultSubtotal,
Items: &xlsxItems{
Count: len(items),
@ -591,6 +599,8 @@ func (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opts *PivotTableOpti
DataField: inPivotTableField(opts.Data, name) != -1,
Compact: &columnOptions.Compact,
Outline: &columnOptions.Outline,
ShowAll: columnOptions.ShowAll,
InsertBlankRow: columnOptions.InsertBlankRow,
DefaultSubtotal: &columnOptions.DefaultSubtotal,
Items: &xlsxItems{
Count: len(items),
@ -831,12 +841,14 @@ func (f *File) getPivotTable(sheet, pivotTableXML, pivotCacheRels string) (Pivot
return opts, err
}
opts = PivotTableOptions{
pivotTableXML: pivotTableXML,
pivotCacheXML: pivotCacheXML,
pivotSheetName: sheet,
DataRange: fmt.Sprintf("%s!%s", pc.CacheSource.WorksheetSource.Sheet, pc.CacheSource.WorksheetSource.Ref),
PivotTableRange: fmt.Sprintf("%s!%s", sheet, pt.Location.Ref),
Name: pt.Name,
pivotTableXML: pivotTableXML,
pivotCacheXML: pivotCacheXML,
pivotSheetName: sheet,
DataRange: fmt.Sprintf("%s!%s", pc.CacheSource.WorksheetSource.Sheet, pc.CacheSource.WorksheetSource.Ref),
PivotTableRange: fmt.Sprintf("%s!%s", sheet, pt.Location.Ref),
Name: pt.Name,
FieldPrintTitles: pt.FieldPrintTitles,
ItemPrintTitles: pt.ItemPrintTitles,
}
if pc.CacheSource.WorksheetSource.Name != "" {
opts.DataRange = pc.CacheSource.WorksheetSource.Name
@ -924,7 +936,9 @@ func (f *File) extractPivotTableFields(order []string, pt *xlsxPivotTableDefinit
// settings by given pivot table fields.
func extractPivotTableField(data string, fld *xlsxPivotField) PivotTableField {
pivotTableField := PivotTableField{
Data: data,
Data: data,
ShowAll: fld.ShowAll,
InsertBlankRow: fld.InsertBlankRow,
}
fields := []string{"Compact", "Name", "Outline", "Subtotal", "DefaultSubtotal"}
immutable, mutable := reflect.ValueOf(*fld), reflect.ValueOf(&pivotTableField).Elem()

View File

@ -31,9 +31,9 @@ func TestPivotTable(t *testing.T) {
DataRange: "Sheet1!A1:E31",
PivotTableRange: "Sheet1!G2:M34",
Name: "PivotTable1",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
Rows: []PivotTableField{{Data: "Month", ShowAll: true, DefaultSubtotal: true}, {Data: "Year"}},
Filter: []PivotTableField{{Data: "Region"}},
Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}},
Columns: []PivotTableField{{Data: "Type", ShowAll: true, InsertBlankRow: true, DefaultSubtotal: true}},
Data: []PivotTableField{{Data: "Sales", Subtotal: "Sum", Name: "Summarize by Sum", NumFmt: 38}},
RowGrandTotals: true,
ColGrandTotals: true,
@ -42,6 +42,8 @@ func TestPivotTable(t *testing.T) {
ShowColHeaders: true,
ShowLastColumn: true,
ShowError: true,
ItemPrintTitles: true,
FieldPrintTitles: true,
PivotTableStyleName: "PivotStyleLight16",
}
assert.NoError(t, f.AddPivotTable(expected))

View File

@ -102,6 +102,7 @@ const (
ExtURICalcFeatures = "{B58B0392-4F1F-4190-BB64-5DF3571DCE5F}"
ExtURIConditionalFormattingRuleID = "{B025F937-C7B1-47D3-B67F-A62EFF666E3E}"
ExtURIConditionalFormattings = "{78C0D931-6437-407d-A8EE-F0AAD7539E65}"
ExtURIDataField = "{E15A36E0-9728-4E99-A89B-3F7291B0FE68}"
ExtURIDataModel = "{FCE2AD5D-F65C-4FA6-A056-5C36A1767C68}"
ExtURIDataValidations = "{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}"
ExtURIDrawingBlip = "{28A0092B-C50C-407E-A947-70E740481C1C}"
@ -112,6 +113,9 @@ const (
ExtURIPivotCacheDefinition = "{725AE2AE-9491-48be-B2B4-4EB974FC3084}"
ExtURIPivotCachesX14 = "{876F7934-8845-4945-9796-88D515C7AA90}"
ExtURIPivotCachesX15 = "{841E416B-1EF1-43b6-AB56-02D37102CBD5}"
ExtURIPivotField = "{2946ED86-A175-432a-8AC1-64E0C546D7DE}"
ExtURIPivotFilter = "{0605FD5F-26C8-4aeb-8148-2DB25E43C511}"
ExtURIPivotHierarchy = "{F1805F06-0CD304483-9156-8803C3D141DF}"
ExtURIPivotTableReferences = "{983426D0-5260-488c-9760-48F4B6AC55F4}"
ExtURIProtectedRanges = "{FC87AEE6-9EDD-4A0A-B7FB-166176984837}"
ExtURISlicerCacheDefinition = "{2F2917AC-EB37-4324-AD4E-5DD8C200BD13}"

View File

@ -300,26 +300,26 @@ type cView3D struct {
// cPlotArea directly maps the plotArea element. This element specifies the
// plot area of the chart.
type cPlotArea struct {
Layout *string `xml:"layout"`
AreaChart *cCharts `xml:"areaChart"`
Area3DChart *cCharts `xml:"area3DChart"`
BarChart *cCharts `xml:"barChart"`
Bar3DChart *cCharts `xml:"bar3DChart"`
BubbleChart *cCharts `xml:"bubbleChart"`
DoughnutChart *cCharts `xml:"doughnutChart"`
LineChart *cCharts `xml:"lineChart"`
Line3DChart *cCharts `xml:"line3DChart"`
PieChart *cCharts `xml:"pieChart"`
Pie3DChart *cCharts `xml:"pie3DChart"`
OfPieChart *cCharts `xml:"ofPieChart"`
RadarChart *cCharts `xml:"radarChart"`
ScatterChart *cCharts `xml:"scatterChart"`
Surface3DChart *cCharts `xml:"surface3DChart"`
SurfaceChart *cCharts `xml:"surfaceChart"`
CatAx []*cAxs `xml:"catAx"`
ValAx []*cAxs `xml:"valAx"`
SerAx []*cAxs `xml:"serAx"`
SpPr *cSpPr `xml:"spPr"`
Layout *string `xml:"layout"`
AreaChart []*cCharts `xml:"areaChart"`
Area3DChart []*cCharts `xml:"area3DChart"`
BarChart []*cCharts `xml:"barChart"`
Bar3DChart []*cCharts `xml:"bar3DChart"`
BubbleChart []*cCharts `xml:"bubbleChart"`
DoughnutChart []*cCharts `xml:"doughnutChart"`
LineChart []*cCharts `xml:"lineChart"`
Line3DChart []*cCharts `xml:"line3DChart"`
PieChart []*cCharts `xml:"pieChart"`
Pie3DChart []*cCharts `xml:"pie3DChart"`
OfPieChart []*cCharts `xml:"ofPieChart"`
RadarChart []*cCharts `xml:"radarChart"`
ScatterChart []*cCharts `xml:"scatterChart"`
Surface3DChart []*cCharts `xml:"surface3DChart"`
SurfaceChart []*cCharts `xml:"surfaceChart"`
CatAx []*cAxs `xml:"catAx"`
ValAx []*cAxs `xml:"valAx"`
SerAx []*cAxs `xml:"serAx"`
SpPr *cSpPr `xml:"spPr"`
}
// cCharts specifies the common element of the chart.