From 4e4a5b9b3e052d1694442515492792fb1aa74c5a Mon Sep 17 00:00:00 2001 From: xuri Date: Mon, 23 Dec 2019 00:07:40 +0800 Subject: [PATCH] Improve compatibility, fix workbook's rels ID calc error --- adjust.go | 2 +- cell.go | 2 +- cellmerged.go | 2 - chart.go | 219 +++++++++++++++++++++++------------------------ comment.go | 15 ++-- excelize.go | 10 ++- excelize_test.go | 36 ++++---- lib.go | 9 ++ shape.go | 9 +- sheet.go | 22 +++-- stream_test.go | 2 +- styles.go | 16 ++-- styles_test.go | 2 +- table.go | 3 +- xmlChart.go | 8 +- xmlPivotTable.go | 2 +- xmlWorksheet.go | 26 +++++- 17 files changed, 213 insertions(+), 172 deletions(-) diff --git a/adjust.go b/adjust.go index bb583f1..c15d4b4 100644 --- a/adjust.go +++ b/adjust.go @@ -53,7 +53,7 @@ func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int) return err } checkSheet(xlsx) - checkRow(xlsx) + _ = checkRow(xlsx) if xlsx.MergeCells != nil && len(xlsx.MergeCells.Cells) == 0 { xlsx.MergeCells = nil diff --git a/cell.go b/cell.go index ad4bcdb..e59a659 100644 --- a/cell.go +++ b/cell.go @@ -395,7 +395,7 @@ func (f *File) SetCellHyperLink(sheet, axis, link, linkType string) error { linkData = xlsxHyperlink{ Ref: axis, } - sheetPath, _ := f.sheetMap[trimSheetName(sheet)] + sheetPath := f.sheetMap[trimSheetName(sheet)] sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels" rID := f.addRels(sheetRels, SourceRelationshipHyperLink, link, linkType) linkData.RID = "rId" + strconv.Itoa(rID) diff --git a/cellmerged.go b/cellmerged.go index 968a28a..5bea0bc 100644 --- a/cellmerged.go +++ b/cellmerged.go @@ -129,8 +129,6 @@ func (f *File) UnmergeCell(sheet string, hcell, vcell string) error { if rect1[3] < rect1[1] { rect1[1], rect1[3] = rect1[3], rect1[1] } - hcell, _ = CoordinatesToCellName(rect1[0], rect1[1]) - vcell, _ = CoordinatesToCellName(rect1[2], rect1[3]) // return nil since no MergeCells in the sheet if xlsx.MergeCells == nil { diff --git a/chart.go b/chart.go index bf8155a..5a42c5b 100644 --- a/chart.go +++ b/chart.go @@ -726,8 +726,7 @@ func (f *File) prepareDrawing(xlsx *xlsxWorksheet, drawingID int, sheet, drawing drawingXML = strings.Replace(sheetRelationshipsDrawingXML, "..", "xl", -1) } else { // Add first picture for given sheet. - sheetPath, _ := f.sheetMap[trimSheetName(sheet)] - sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels" + sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels" rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "") f.addSheetDrawing(sheet, rID) } @@ -743,9 +742,9 @@ func (f *File) addChart(formatSet *formatChart) { XMLNSa: NameSpaceDrawingML, XMLNSr: SourceRelationship, XMLNSc16r2: SourceRelationshipChart201506, - Date1904: &attrValBool{Val: false}, - Lang: &attrValString{Val: "en-US"}, - RoundedCorners: &attrValBool{Val: false}, + Date1904: &attrValBool{Val: boolPtr(false)}, + Lang: &attrValString{Val: stringPtr("en-US")}, + RoundedCorners: &attrValBool{Val: boolPtr(false)}, Chart: cChart{ Title: &cTitle{ Tx: cTx{ @@ -761,10 +760,10 @@ func (f *File) addChart(formatSet *formatChart) { SchemeClr: &aSchemeClr{ Val: "tx1", LumMod: &attrValInt{ - Val: 65000, + Val: intPtr(65000), }, LumOff: &attrValInt{ - Val: 35000, + Val: intPtr(35000), }, }, }, @@ -806,29 +805,29 @@ func (f *File) addChart(formatSet *formatChart) { }, }, View3D: &cView3D{ - RotX: &attrValInt{Val: chartView3DRotX[formatSet.Type]}, - RotY: &attrValInt{Val: chartView3DRotY[formatSet.Type]}, - Perspective: &attrValInt{Val: chartView3DPerspective[formatSet.Type]}, - RAngAx: &attrValInt{Val: chartView3DRAngAx[formatSet.Type]}, + RotX: &attrValInt{Val: intPtr(chartView3DRotX[formatSet.Type])}, + RotY: &attrValInt{Val: intPtr(chartView3DRotY[formatSet.Type])}, + Perspective: &attrValInt{Val: intPtr(chartView3DPerspective[formatSet.Type])}, + RAngAx: &attrValInt{Val: intPtr(chartView3DRAngAx[formatSet.Type])}, }, Floor: &cThicknessSpPr{ - Thickness: &attrValInt{Val: 0}, + Thickness: &attrValInt{Val: intPtr(0)}, }, SideWall: &cThicknessSpPr{ - Thickness: &attrValInt{Val: 0}, + Thickness: &attrValInt{Val: intPtr(0)}, }, BackWall: &cThicknessSpPr{ - Thickness: &attrValInt{Val: 0}, + Thickness: &attrValInt{Val: intPtr(0)}, }, PlotArea: &cPlotArea{}, Legend: &cLegend{ - LegendPos: &attrValString{Val: chartLegendPosition[formatSet.Legend.Position]}, - Overlay: &attrValBool{Val: false}, + LegendPos: &attrValString{Val: stringPtr(chartLegendPosition[formatSet.Legend.Position])}, + Overlay: &attrValBool{Val: boolPtr(false)}, }, - PlotVisOnly: &attrValBool{Val: false}, - DispBlanksAs: &attrValString{Val: formatSet.ShowBlanksAs}, - ShowDLblsOverMax: &attrValBool{Val: false}, + PlotVisOnly: &attrValBool{Val: boolPtr(false)}, + DispBlanksAs: &attrValString{Val: stringPtr(formatSet.ShowBlanksAs)}, + ShowDLblsOverMax: &attrValBool{Val: boolPtr(false)}, }, SpPr: &cSpPr{ SolidFill: &aSolidFill{ @@ -842,10 +841,10 @@ func (f *File) addChart(formatSet *formatChart) { SolidFill: &aSolidFill{ SchemeClr: &aSchemeClr{Val: "tx1", LumMod: &attrValInt{ - Val: 15000, + Val: intPtr(15000), }, LumOff: &attrValInt{ - Val: 85000, + Val: intPtr(85000), }, }, }, @@ -928,31 +927,31 @@ func (f *File) addChart(formatSet *formatChart) { func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea { c := cCharts{ BarDir: &attrValString{ - Val: "col", + Val: stringPtr("col"), }, Grouping: &attrValString{ - Val: "clustered", + Val: stringPtr("clustered"), }, VaryColors: &attrValBool{ - Val: true, + Val: boolPtr(true), }, Ser: f.drawChartSeries(formatSet), Shape: f.drawChartShape(formatSet), DLbls: f.drawChartDLbls(formatSet), AxID: []*attrValInt{ - {Val: 754001152}, - {Val: 753999904}, + {Val: intPtr(754001152)}, + {Val: intPtr(753999904)}, }, - Overlap: &attrValInt{Val: 100}, + Overlap: &attrValInt{Val: intPtr(100)}, } var ok bool - if c.BarDir.Val, ok = plotAreaChartBarDir[formatSet.Type]; !ok { + if *c.BarDir.Val, ok = plotAreaChartBarDir[formatSet.Type]; !ok { c.BarDir = nil } - if c.Grouping.Val, ok = plotAreaChartGrouping[formatSet.Type]; !ok { + if *c.Grouping.Val, ok = plotAreaChartGrouping[formatSet.Type]; !ok { c.Grouping = nil } - if c.Overlap.Val, ok = plotAreaChartOverlap[formatSet.Type]; !ok { + if *c.Overlap.Val, ok = plotAreaChartOverlap[formatSet.Type]; !ok { c.Overlap = nil } catAx := f.drawPlotAreaCatAx(formatSet) @@ -1178,10 +1177,10 @@ func (f *File) drawDoughnutChart(formatSet *formatChart) *cPlotArea { return &cPlotArea{ DoughnutChart: &cCharts{ VaryColors: &attrValBool{ - Val: true, + Val: boolPtr(true), }, Ser: f.drawChartSeries(formatSet), - HoleSize: &attrValInt{Val: 75}, + HoleSize: &attrValInt{Val: intPtr(75)}, }, } } @@ -1192,19 +1191,19 @@ func (f *File) drawLineChart(formatSet *formatChart) *cPlotArea { return &cPlotArea{ LineChart: &cCharts{ Grouping: &attrValString{ - Val: plotAreaChartGrouping[formatSet.Type], + Val: stringPtr(plotAreaChartGrouping[formatSet.Type]), }, VaryColors: &attrValBool{ - Val: false, + Val: boolPtr(false), }, Ser: f.drawChartSeries(formatSet), DLbls: f.drawChartDLbls(formatSet), Smooth: &attrValBool{ - Val: false, + Val: boolPtr(false), }, AxID: []*attrValInt{ - {Val: 754001152}, - {Val: 753999904}, + {Val: intPtr(754001152)}, + {Val: intPtr(753999904)}, }, }, CatAx: f.drawPlotAreaCatAx(formatSet), @@ -1218,7 +1217,7 @@ func (f *File) drawPieChart(formatSet *formatChart) *cPlotArea { return &cPlotArea{ PieChart: &cCharts{ VaryColors: &attrValBool{ - Val: true, + Val: boolPtr(true), }, Ser: f.drawChartSeries(formatSet), }, @@ -1231,7 +1230,7 @@ func (f *File) drawPie3DChart(formatSet *formatChart) *cPlotArea { return &cPlotArea{ Pie3DChart: &cCharts{ VaryColors: &attrValBool{ - Val: true, + Val: boolPtr(true), }, Ser: f.drawChartSeries(formatSet), }, @@ -1244,16 +1243,16 @@ func (f *File) drawRadarChart(formatSet *formatChart) *cPlotArea { return &cPlotArea{ RadarChart: &cCharts{ RadarStyle: &attrValString{ - Val: "marker", + Val: stringPtr("marker"), }, VaryColors: &attrValBool{ - Val: false, + Val: boolPtr(false), }, Ser: f.drawChartSeries(formatSet), DLbls: f.drawChartDLbls(formatSet), AxID: []*attrValInt{ - {Val: 754001152}, - {Val: 753999904}, + {Val: intPtr(754001152)}, + {Val: intPtr(753999904)}, }, }, CatAx: f.drawPlotAreaCatAx(formatSet), @@ -1267,16 +1266,16 @@ func (f *File) drawScatterChart(formatSet *formatChart) *cPlotArea { return &cPlotArea{ ScatterChart: &cCharts{ ScatterStyle: &attrValString{ - Val: "smoothMarker", // line,lineMarker,marker,none,smooth,smoothMarker + Val: stringPtr("smoothMarker"), // line,lineMarker,marker,none,smooth,smoothMarker }, VaryColors: &attrValBool{ - Val: false, + Val: boolPtr(false), }, Ser: f.drawChartSeries(formatSet), DLbls: f.drawChartDLbls(formatSet), AxID: []*attrValInt{ - {Val: 754001152}, - {Val: 753999904}, + {Val: intPtr(754001152)}, + {Val: intPtr(753999904)}, }, }, CatAx: f.drawPlotAreaCatAx(formatSet), @@ -1291,9 +1290,9 @@ func (f *File) drawSurface3DChart(formatSet *formatChart) *cPlotArea { Surface3DChart: &cCharts{ Ser: f.drawChartSeries(formatSet), AxID: []*attrValInt{ - {Val: 754001152}, - {Val: 753999904}, - {Val: 832256642}, + {Val: intPtr(754001152)}, + {Val: intPtr(753999904)}, + {Val: intPtr(832256642)}, }, }, CatAx: f.drawPlotAreaCatAx(formatSet), @@ -1301,7 +1300,7 @@ func (f *File) drawSurface3DChart(formatSet *formatChart) *cPlotArea { SerAx: f.drawPlotAreaSerAx(formatSet), } if formatSet.Type == WireframeSurface3D { - plotArea.Surface3DChart.Wireframe = &attrValBool{Val: true} + plotArea.Surface3DChart.Wireframe = &attrValBool{Val: boolPtr(true)} } return plotArea } @@ -1313,9 +1312,9 @@ func (f *File) drawSurfaceChart(formatSet *formatChart) *cPlotArea { SurfaceChart: &cCharts{ Ser: f.drawChartSeries(formatSet), AxID: []*attrValInt{ - {Val: 754001152}, - {Val: 753999904}, - {Val: 832256642}, + {Val: intPtr(754001152)}, + {Val: intPtr(753999904)}, + {Val: intPtr(832256642)}, }, }, CatAx: f.drawPlotAreaCatAx(formatSet), @@ -1323,7 +1322,7 @@ func (f *File) drawSurfaceChart(formatSet *formatChart) *cPlotArea { SerAx: f.drawPlotAreaSerAx(formatSet), } if formatSet.Type == WireframeContour { - plotArea.SurfaceChart.Wireframe = &attrValBool{Val: true} + plotArea.SurfaceChart.Wireframe = &attrValBool{Val: boolPtr(true)} } return plotArea } @@ -1355,7 +1354,7 @@ func (f *File) drawChartShape(formatSet *formatChart) *attrValString { Col3DCylinderPercentStacked: "cylinder", } if shape, ok := shapes[formatSet.Type]; ok { - return &attrValString{Val: shape} + return &attrValString{Val: stringPtr(shape)} } return nil } @@ -1366,8 +1365,8 @@ func (f *File) drawChartSeries(formatSet *formatChart) *[]cSer { ser := []cSer{} for k := range formatSet.Series { ser = append(ser, cSer{ - IDx: &attrValInt{Val: k}, - Order: &attrValInt{Val: k}, + IDx: &attrValInt{Val: intPtr(k)}, + Order: &attrValInt{Val: intPtr(k)}, Tx: &cTx{ StrRef: &cStrRef{ F: formatSet.Series[k].Name, @@ -1416,8 +1415,8 @@ func (f *File) drawChartSeriesSpPr(i int, formatSet *formatChart) *cSpPr { // data index and format sets. func (f *File) drawChartSeriesDPt(i int, formatSet *formatChart) []*cDPt { dpt := []*cDPt{{ - IDx: &attrValInt{Val: i}, - Bubble3D: &attrValBool{Val: false}, + IDx: &attrValInt{Val: intPtr(i)}, + Bubble3D: &attrValBool{Val: boolPtr(false)}, SpPr: &cSpPr{ SolidFill: &aSolidFill{ SchemeClr: &aSchemeClr{Val: "accent" + strconv.Itoa(i+1)}, @@ -1475,8 +1474,8 @@ func (f *File) drawChartSeriesVal(v formatChartSeries, formatSet *formatChart) * // given data index and format sets. func (f *File) drawChartSeriesMarker(i int, formatSet *formatChart) *cMarker { marker := &cMarker{ - Symbol: &attrValString{Val: "circle"}, - Size: &attrValInt{Val: 5}, + Symbol: &attrValString{Val: stringPtr("circle")}, + Size: &attrValInt{Val: intPtr(5)}, } if i < 6 { marker.SpPr = &cSpPr{ @@ -1542,20 +1541,20 @@ func (f *File) drawCharSeriesBubble3D(formatSet *formatChart) *attrValBool { if _, ok := map[string]bool{Bubble3D: true}[formatSet.Type]; !ok { return nil } - return &attrValBool{Val: true} + return &attrValBool{Val: boolPtr(true)} } // drawChartDLbls provides a function to draw the c:dLbls element by given // format sets. func (f *File) drawChartDLbls(formatSet *formatChart) *cDLbls { return &cDLbls{ - ShowLegendKey: &attrValBool{Val: formatSet.Legend.ShowLegendKey}, - ShowVal: &attrValBool{Val: formatSet.Plotarea.ShowVal}, - ShowCatName: &attrValBool{Val: formatSet.Plotarea.ShowCatName}, - ShowSerName: &attrValBool{Val: formatSet.Plotarea.ShowSerName}, - ShowBubbleSize: &attrValBool{Val: formatSet.Plotarea.ShowBubbleSize}, - ShowPercent: &attrValBool{Val: formatSet.Plotarea.ShowPercent}, - ShowLeaderLines: &attrValBool{Val: formatSet.Plotarea.ShowLeaderLines}, + ShowLegendKey: &attrValBool{Val: boolPtr(formatSet.Legend.ShowLegendKey)}, + ShowVal: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowVal)}, + ShowCatName: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowCatName)}, + ShowSerName: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowSerName)}, + ShowBubbleSize: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowBubbleSize)}, + ShowPercent: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowPercent)}, + ShowLeaderLines: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowLeaderLines)}, } } @@ -1572,8 +1571,8 @@ func (f *File) drawChartSeriesDLbls(formatSet *formatChart) *cDLbls { // drawPlotAreaCatAx provides a function to draw the c:catAx element. func (f *File) drawPlotAreaCatAx(formatSet *formatChart) []*cAxs { - min := &attrValFloat{Val: formatSet.XAxis.Minimum} - max := &attrValFloat{Val: formatSet.XAxis.Maximum} + min := &attrValFloat{Val: float64Ptr(formatSet.XAxis.Minimum)} + max := &attrValFloat{Val: float64Ptr(formatSet.XAxis.Maximum)} if formatSet.XAxis.Minimum == 0 { min = nil } @@ -1582,29 +1581,29 @@ func (f *File) drawPlotAreaCatAx(formatSet *formatChart) []*cAxs { } axs := []*cAxs{ { - AxID: &attrValInt{Val: 754001152}, + AxID: &attrValInt{Val: intPtr(754001152)}, Scaling: &cScaling{ - Orientation: &attrValString{Val: orientation[formatSet.XAxis.ReverseOrder]}, + Orientation: &attrValString{Val: stringPtr(orientation[formatSet.XAxis.ReverseOrder])}, Max: max, Min: min, }, - Delete: &attrValBool{Val: false}, - AxPos: &attrValString{Val: catAxPos[formatSet.XAxis.ReverseOrder]}, + Delete: &attrValBool{Val: boolPtr(false)}, + AxPos: &attrValString{Val: stringPtr(catAxPos[formatSet.XAxis.ReverseOrder])}, NumFmt: &cNumFmt{ FormatCode: "General", SourceLinked: true, }, - MajorTickMark: &attrValString{Val: "none"}, - MinorTickMark: &attrValString{Val: "none"}, - TickLblPos: &attrValString{Val: "nextTo"}, + MajorTickMark: &attrValString{Val: stringPtr("none")}, + MinorTickMark: &attrValString{Val: stringPtr("none")}, + TickLblPos: &attrValString{Val: stringPtr("nextTo")}, SpPr: f.drawPlotAreaSpPr(), TxPr: f.drawPlotAreaTxPr(), - CrossAx: &attrValInt{Val: 753999904}, - Crosses: &attrValString{Val: "autoZero"}, - Auto: &attrValBool{Val: true}, - LblAlgn: &attrValString{Val: "ctr"}, - LblOffset: &attrValInt{Val: 100}, - NoMultiLvlLbl: &attrValBool{Val: false}, + CrossAx: &attrValInt{Val: intPtr(753999904)}, + Crosses: &attrValString{Val: stringPtr("autoZero")}, + Auto: &attrValBool{Val: boolPtr(true)}, + LblAlgn: &attrValString{Val: stringPtr("ctr")}, + LblOffset: &attrValInt{Val: intPtr(100)}, + NoMultiLvlLbl: &attrValBool{Val: boolPtr(false)}, }, } if formatSet.XAxis.MajorGridlines { @@ -1618,8 +1617,8 @@ func (f *File) drawPlotAreaCatAx(formatSet *formatChart) []*cAxs { // drawPlotAreaValAx provides a function to draw the c:valAx element. func (f *File) drawPlotAreaValAx(formatSet *formatChart) []*cAxs { - min := &attrValFloat{Val: formatSet.YAxis.Minimum} - max := &attrValFloat{Val: formatSet.YAxis.Maximum} + min := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Minimum)} + max := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Maximum)} if formatSet.YAxis.Minimum == 0 { min = nil } @@ -1628,26 +1627,26 @@ func (f *File) drawPlotAreaValAx(formatSet *formatChart) []*cAxs { } axs := []*cAxs{ { - AxID: &attrValInt{Val: 753999904}, + AxID: &attrValInt{Val: intPtr(753999904)}, Scaling: &cScaling{ - Orientation: &attrValString{Val: orientation[formatSet.YAxis.ReverseOrder]}, + Orientation: &attrValString{Val: stringPtr(orientation[formatSet.YAxis.ReverseOrder])}, Max: max, Min: min, }, - Delete: &attrValBool{Val: false}, - AxPos: &attrValString{Val: valAxPos[formatSet.YAxis.ReverseOrder]}, + Delete: &attrValBool{Val: boolPtr(false)}, + AxPos: &attrValString{Val: stringPtr(valAxPos[formatSet.YAxis.ReverseOrder])}, NumFmt: &cNumFmt{ FormatCode: chartValAxNumFmtFormatCode[formatSet.Type], SourceLinked: true, }, - MajorTickMark: &attrValString{Val: "none"}, - MinorTickMark: &attrValString{Val: "none"}, - TickLblPos: &attrValString{Val: "nextTo"}, + MajorTickMark: &attrValString{Val: stringPtr("none")}, + MinorTickMark: &attrValString{Val: stringPtr("none")}, + TickLblPos: &attrValString{Val: stringPtr("nextTo")}, SpPr: f.drawPlotAreaSpPr(), TxPr: f.drawPlotAreaTxPr(), - CrossAx: &attrValInt{Val: 754001152}, - Crosses: &attrValString{Val: "autoZero"}, - CrossBetween: &attrValString{Val: chartValAxCrossBetween[formatSet.Type]}, + CrossAx: &attrValInt{Val: intPtr(754001152)}, + Crosses: &attrValString{Val: stringPtr("autoZero")}, + CrossBetween: &attrValString{Val: stringPtr(chartValAxCrossBetween[formatSet.Type])}, }, } if formatSet.YAxis.MajorGridlines { @@ -1657,15 +1656,15 @@ func (f *File) drawPlotAreaValAx(formatSet *formatChart) []*cAxs { axs[0].MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()} } if pos, ok := valTickLblPos[formatSet.Type]; ok { - axs[0].TickLblPos.Val = pos + axs[0].TickLblPos.Val = stringPtr(pos) } return axs } // drawPlotAreaSerAx provides a function to draw the c:serAx element. func (f *File) drawPlotAreaSerAx(formatSet *formatChart) []*cAxs { - min := &attrValFloat{Val: formatSet.YAxis.Minimum} - max := &attrValFloat{Val: formatSet.YAxis.Maximum} + min := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Minimum)} + max := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Maximum)} if formatSet.YAxis.Minimum == 0 { min = nil } @@ -1674,18 +1673,18 @@ func (f *File) drawPlotAreaSerAx(formatSet *formatChart) []*cAxs { } return []*cAxs{ { - AxID: &attrValInt{Val: 832256642}, + AxID: &attrValInt{Val: intPtr(832256642)}, Scaling: &cScaling{ - Orientation: &attrValString{Val: orientation[formatSet.YAxis.ReverseOrder]}, + Orientation: &attrValString{Val: stringPtr(orientation[formatSet.YAxis.ReverseOrder])}, Max: max, Min: min, }, - Delete: &attrValBool{Val: false}, - AxPos: &attrValString{Val: catAxPos[formatSet.XAxis.ReverseOrder]}, - TickLblPos: &attrValString{Val: "nextTo"}, + Delete: &attrValBool{Val: boolPtr(false)}, + AxPos: &attrValString{Val: stringPtr(catAxPos[formatSet.XAxis.ReverseOrder])}, + TickLblPos: &attrValString{Val: stringPtr("nextTo")}, SpPr: f.drawPlotAreaSpPr(), TxPr: f.drawPlotAreaTxPr(), - CrossAx: &attrValInt{Val: 753999904}, + CrossAx: &attrValInt{Val: intPtr(753999904)}, }, } } @@ -1701,8 +1700,8 @@ func (f *File) drawPlotAreaSpPr() *cSpPr { SolidFill: &aSolidFill{ SchemeClr: &aSchemeClr{ Val: "tx1", - LumMod: &attrValInt{Val: 15000}, - LumOff: &attrValInt{Val: 85000}, + LumMod: &attrValInt{Val: intPtr(15000)}, + LumOff: &attrValInt{Val: intPtr(85000)}, }, }, }, @@ -1734,8 +1733,8 @@ func (f *File) drawPlotAreaTxPr() *cTxPr { SolidFill: &aSolidFill{ SchemeClr: &aSchemeClr{ Val: "tx1", - LumMod: &attrValInt{Val: 15000}, - LumOff: &attrValInt{Val: 85000}, + LumMod: &attrValInt{Val: intPtr(15000)}, + LumOff: &attrValInt{Val: intPtr(85000)}, }, }, Latin: &aLatin{Typeface: "+mn-lt"}, diff --git a/comment.go b/comment.go index 99630c9..486a035 100644 --- a/comment.go +++ b/comment.go @@ -101,8 +101,7 @@ func (f *File) AddComment(sheet, cell, format string) error { drawingVML = strings.Replace(sheetRelationshipsDrawingVML, "..", "xl", -1) } else { // Add first comment for given sheet. - sheetPath, _ := f.sheetMap[trimSheetName(sheet)] - sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels" + sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels" rID := f.addRels(sheetRels, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "") f.addRels(sheetRels, SourceRelationshipComments, sheetRelationshipsComments, "") f.addSheetLegacyDrawing(sheet, rID) @@ -256,23 +255,23 @@ func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) { { RPr: &xlsxRPr{ B: " ", - Sz: &attrValFloat{Val: 9}, + Sz: &attrValFloat{Val: float64Ptr(9)}, Color: &xlsxColor{ Indexed: 81, }, - RFont: &attrValString{Val: defaultFont}, - Family: &attrValInt{Val: 2}, + RFont: &attrValString{Val: stringPtr(defaultFont)}, + Family: &attrValInt{Val: intPtr(2)}, }, T: a, }, { RPr: &xlsxRPr{ - Sz: &attrValFloat{Val: 9}, + Sz: &attrValFloat{Val: float64Ptr(9)}, Color: &xlsxColor{ Indexed: 81, }, - RFont: &attrValString{Val: defaultFont}, - Family: &attrValInt{Val: 2}, + RFont: &attrValString{Val: stringPtr(defaultFont)}, + Family: &attrValInt{Val: intPtr(2)}, }, T: t, }, diff --git a/excelize.go b/excelize.go index a2e20ff..135028c 100644 --- a/excelize.go +++ b/excelize.go @@ -203,11 +203,17 @@ func checkSheet(xlsx *xlsxWorksheet) { // relationship type, target and target mode. func (f *File) addRels(relPath, relType, target, targetMode string) int { rels := f.relsReader(relPath) - rID := 0 if rels == nil { rels = &xlsxRelationships{} } - rID = len(rels.Relationships) + 1 + var rID int + for _, rel := range rels.Relationships { + ID, _ := strconv.Atoi(strings.TrimPrefix(rel.ID, "rId")) + if ID > rID { + rID = ID + } + } + rID++ var ID bytes.Buffer ID.WriteString("rId") ID.WriteString(strconv.Itoa(rID)) diff --git a/excelize_test.go b/excelize_test.go index 6929a4f..1d6ed24 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -48,7 +48,7 @@ func TestOpenFile(t *testing.T) { assert.EqualError(t, f.SetCellDefault("Sheet2", "A", strconv.FormatFloat(float64(-100.1588), 'f', -1, 64)), `cannot convert cell "A" to coordinates: invalid cell name "A"`) - f.SetCellInt("Sheet2", "A1", 100) + assert.NoError(t, f.SetCellInt("Sheet2", "A1", 100)) // Test set cell integer value with illegal row number. assert.EqualError(t, f.SetCellInt("Sheet2", "A", 100), `cannot convert cell "A" to coordinates: invalid cell name "A"`) @@ -80,8 +80,10 @@ func TestOpenFile(t *testing.T) { _, err = f.GetCellFormula("Sheet1", "B") assert.EqualError(t, err, `cannot convert cell "B" to coordinates: invalid cell name "B"`) // Test get shared cell formula - f.GetCellFormula("Sheet2", "H11") - f.GetCellFormula("Sheet2", "I11") + _, err = f.GetCellFormula("Sheet2", "H11") + assert.NoError(t, err) + _, err = f.GetCellFormula("Sheet2", "I11") + assert.NoError(t, err) getSharedForumula(&xlsxWorksheet{}, "") // Test read cell value with given illegal rows number. @@ -91,10 +93,14 @@ func TestOpenFile(t *testing.T) { assert.EqualError(t, err, `cannot convert cell "A" to coordinates: invalid cell name "A"`) // Test read cell value with given lowercase column number. - f.GetCellValue("Sheet2", "a5") - f.GetCellValue("Sheet2", "C11") - f.GetCellValue("Sheet2", "D11") - f.GetCellValue("Sheet2", "D12") + _, err = f.GetCellValue("Sheet2", "a5") + assert.NoError(t, err) + _, err = f.GetCellValue("Sheet2", "C11") + assert.NoError(t, err) + _, err = f.GetCellValue("Sheet2", "D11") + assert.NoError(t, err) + _, err = f.GetCellValue("Sheet2", "D12") + assert.NoError(t, err) // Test SetCellValue function. assert.NoError(t, f.SetCellValue("Sheet2", "F1", " Hello")) assert.NoError(t, f.SetCellValue("Sheet2", "G1", []byte("World"))) @@ -147,7 +153,8 @@ func TestOpenFile(t *testing.T) { // Test completion column. f.SetCellValue("Sheet2", "M2", nil) // Test read cell value with given axis large than exists row. - f.GetCellValue("Sheet2", "E231") + _, err = f.GetCellValue("Sheet2", "E231") + assert.NoError(t, err) // Test get active worksheet of XLSX and get worksheet name of XLSX by given worksheet index. f.GetSheetName(f.GetActiveSheetIndex()) // Test get worksheet index of XLSX by given worksheet name. @@ -302,13 +309,10 @@ func TestSetCellHyperLink(t *testing.T) { assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellHyperLink.xlsx"))) - file := NewFile() - for row := 1; row <= 65530; row++ { - cell, err := CoordinatesToCellName(1, row) - assert.NoError(t, err) - assert.NoError(t, file.SetCellHyperLink("Sheet1", cell, "https://github.com/360EntSecGroup-Skylar/excelize", "External")) - } - assert.EqualError(t, file.SetCellHyperLink("Sheet1", "A65531", "https://github.com/360EntSecGroup-Skylar/excelize", "External"), "over maximum limit hyperlinks in a worksheet") + f = NewFile() + f.workSheetReader("Sheet1") + f.Sheet["xl/worksheets/sheet1.xml"].Hyperlinks = &xlsxHyperlinks{Hyperlink: make([]xlsxHyperlink, 65530)} + assert.EqualError(t, f.SetCellHyperLink("Sheet1", "A65531", "https://github.com/360EntSecGroup-Skylar/excelize", "External"), "over maximum limit hyperlinks in a worksheet") f = NewFile() f.workSheetReader("Sheet1") @@ -1013,6 +1017,8 @@ func TestSetActiveSheet(t *testing.T) { f.WorkBook.BookViews = &xlsxBookViews{WorkBookView: []xlsxWorkBookView{}} f.Sheet["xl/worksheets/sheet1.xml"].SheetViews = &xlsxSheetViews{SheetView: []xlsxSheetView{}} f.SetActiveSheet(1) + f.Sheet["xl/worksheets/sheet1.xml"].SheetViews = nil + f.SetActiveSheet(1) } func TestSetSheetVisible(t *testing.T) { diff --git a/lib.go b/lib.go index edac98a..86f8d16 100644 --- a/lib.go +++ b/lib.go @@ -198,6 +198,15 @@ func CoordinatesToCellName(col, row int) (string, error) { // boolPtr returns a pointer to a bool with the given value. func boolPtr(b bool) *bool { return &b } +// intPtr returns a pointer to a int with the given value. +func intPtr(i int) *int { return &i } + +// float64Ptr returns a pofloat64er to a float64 with the given value. +func float64Ptr(f float64) *float64 { return &f } + +// stringPtr returns a pointer to a string with the given value. +func stringPtr(s string) *string { return &s } + // defaultTrue returns true if b is nil, or the pointed value. func defaultTrue(b *bool) bool { if b == nil { diff --git a/shape.go b/shape.go index f284e43..2ea66ea 100644 --- a/shape.go +++ b/shape.go @@ -275,8 +275,7 @@ func (f *File) AddShape(sheet, cell, format string) error { drawingXML = strings.Replace(sheetRelationshipsDrawingXML, "..", "xl", -1) } else { // Add first shape for given sheet. - name, _ := f.sheetMap[trimSheetName(sheet)] - sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels" + sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels" rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "") f.addSheetDrawing(sheet, rID) } @@ -362,7 +361,7 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *format FontRef: &aFontRef{ Idx: "minor", SchemeClr: &attrValString{ - Val: "tx1", + Val: stringPtr("tx1"), }, }, }, @@ -422,7 +421,7 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *format if len(srgbClr) == 6 { paragraph.R.RPr.SolidFill = &aSolidFill{ SrgbClr: &attrValString{ - Val: srgbClr, + Val: stringPtr(srgbClr), }, } } @@ -454,7 +453,7 @@ func setShapeRef(color string, i int) *aRef { return &aRef{ Idx: i, SrgbClr: &attrValString{ - Val: strings.Replace(strings.ToUpper(color), "#", "", -1), + Val: stringPtr(strings.Replace(strings.ToUpper(color), "#", "", -1)), }, } } diff --git a/sheet.go b/sheet.go index 7412fce..954de5b 100644 --- a/sheet.go +++ b/sheet.go @@ -249,6 +249,11 @@ func (f *File) SetActiveSheet(index int) { } for idx, name := range f.GetSheetMap() { xlsx, _ := f.workSheetReader(name) + if xlsx.SheetViews == nil { + xlsx.SheetViews = &xlsxSheetViews{ + SheetView: []xlsxSheetView{{WorkbookViewID: 0}}, + } + } if len(xlsx.SheetViews.SheetView) > 0 { xlsx.SheetViews.SheetView[0].TabSelected = false } @@ -305,11 +310,15 @@ func (f *File) SetSheetName(oldName, newName string) { // string. func (f *File) GetSheetName(index int) string { wb := f.workbookReader() - realIdx := index - 1 // sheets are 1 based index, but we're checking against an array - if wb == nil || realIdx < 0 || realIdx >= len(wb.Sheets.Sheet) { + if wb == nil || index < 1 { return "" } - return wb.Sheets.Sheet[realIdx].Name + for _, sheet := range wb.Sheets.Sheet { + if index == sheet.SheetID { + return sheet.Name + } + } + return "" } // GetSheetIndex provides a function to get worksheet index of XLSX by given @@ -342,8 +351,8 @@ func (f *File) GetSheetMap() map[int]string { wb := f.workbookReader() sheetMap := map[int]string{} if wb != nil { - for i, sheet := range wb.Sheets.Sheet { - sheetMap[i+1] = sheet.Name + for _, sheet := range wb.Sheets.Sheet { + sheetMap[sheet.SheetID] = sheet.Name } } return sheetMap @@ -384,8 +393,7 @@ func (f *File) SetSheetBackground(sheet, picture string) error { } file, _ := ioutil.ReadFile(picture) name := f.addMedia(file, ext) - sheetPath, _ := f.sheetMap[trimSheetName(sheet)] - sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels" + sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels" rID := f.addRels(sheetRels, SourceRelationshipImage, strings.Replace(name, "xl", "..", 1), "") f.addSheetPicture(sheet, rID) f.setContentTypePartImageExtensions() diff --git a/stream_test.go b/stream_test.go index 8371a4e..4482bd1 100644 --- a/stream_test.go +++ b/stream_test.go @@ -42,7 +42,7 @@ func TestStreamWriter(t *testing.T) { assert.NoError(t, file.SaveAs(filepath.Join("test", "TestStreamWriter.xlsx"))) // Test error exceptions - streamWriter, err = file.NewStreamWriter("SheetN") + _, err = file.NewStreamWriter("SheetN") assert.EqualError(t, err, "sheet SheetN is not exist") } diff --git a/styles.go b/styles.go index fa0507e..56c7196 100644 --- a/styles.go +++ b/styles.go @@ -1957,13 +1957,13 @@ func (f *File) NewConditionalStyle(style string) (int, error) { // Documents generated by excelize start with Calibri. func (f *File) GetDefaultFont() string { font := f.readDefaultFont() - return font.Name.Val + return *font.Name.Val } // SetDefaultFont changes the default font in the workbook. func (f *File) SetDefaultFont(fontName string) { font := f.readDefaultFont() - font.Name.Val = fontName + font.Name.Val = stringPtr(fontName) s := f.stylesReader() s.Fonts.Font[0] = font custom := true @@ -1987,10 +1987,10 @@ func (f *File) setFont(formatStyle *formatStyle) *xlsxFont { formatStyle.Font.Color = "#000000" } fnt := xlsxFont{ - Sz: &attrValFloat{Val: formatStyle.Font.Size}, + Sz: &attrValFloat{Val: float64Ptr(formatStyle.Font.Size)}, Color: &xlsxColor{RGB: getPaletteColor(formatStyle.Font.Color)}, - Name: &attrValString{Val: formatStyle.Font.Family}, - Family: &attrValInt{Val: 2}, + Name: &attrValString{Val: stringPtr(formatStyle.Font.Family)}, + Family: &attrValInt{Val: intPtr(2)}, } if formatStyle.Font.Bold { fnt.B = &formatStyle.Font.Bold @@ -1998,8 +1998,8 @@ func (f *File) setFont(formatStyle *formatStyle) *xlsxFont { if formatStyle.Font.Italic { fnt.I = &formatStyle.Font.Italic } - if fnt.Name.Val == "" { - fnt.Name.Val = f.GetDefaultFont() + if *fnt.Name.Val == "" { + *fnt.Name.Val = f.GetDefaultFont() } if formatStyle.Font.Strike { strike := true @@ -2007,7 +2007,7 @@ func (f *File) setFont(formatStyle *formatStyle) *xlsxFont { } val, ok := fontUnderlineType[formatStyle.Font.Underline] if ok { - fnt.U = &attrValString{Val: val} + fnt.U = &attrValString{Val: stringPtr(val)} } return &fnt } diff --git a/styles_test.go b/styles_test.go index 36a78ed..e6faccb 100644 --- a/styles_test.go +++ b/styles_test.go @@ -175,7 +175,7 @@ func TestNewStyle(t *testing.T) { styles := f.stylesReader() fontID := styles.CellXfs.Xf[styleID].FontID font := styles.Fonts.Font[fontID] - assert.Contains(t, font.Name.Val, "Times New Roman", "Stored font should contain font name") + assert.Contains(t, *font.Name.Val, "Times New Roman", "Stored font should contain font name") assert.Equal(t, 2, styles.CellXfs.Count, "Should have 2 styles") } diff --git a/table.go b/table.go index d26f8fd..c5a704c 100644 --- a/table.go +++ b/table.go @@ -77,8 +77,7 @@ func (f *File) AddTable(sheet, hcell, vcell, format string) error { sheetRelationshipsTableXML := "../tables/table" + strconv.Itoa(tableID) + ".xml" tableXML := strings.Replace(sheetRelationshipsTableXML, "..", "xl", -1) // Add first table for given sheet. - sheetPath, _ := f.sheetMap[trimSheetName(sheet)] - sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels" + sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels" rID := f.addRels(sheetRels, SourceRelationshipTable, sheetRelationshipsTableXML, "") f.addSheetTable(sheet, rID) err = f.addTable(sheet, tableXML, hcol, hrow, vcol, vrow, tableID, formatSet) diff --git a/xmlChart.go b/xmlChart.go index a02da2a..84c1a3b 100644 --- a/xmlChart.go +++ b/xmlChart.go @@ -141,25 +141,25 @@ type aSchemeClr struct { // attrValInt directly maps the val element with integer data type as an // attribute。 type attrValInt struct { - Val int `xml:"val,attr"` + Val *int `xml:"val,attr"` } // attrValFloat directly maps the val element with float64 data type as an // attribute。 type attrValFloat struct { - Val float64 `xml:"val,attr"` + Val *float64 `xml:"val,attr"` } // attrValBool directly maps the val element with boolean data type as an // attribute。 type attrValBool struct { - Val bool `xml:"val,attr"` + Val *bool `xml:"val,attr"` } // attrValString directly maps the val element with string data type as an // attribute。 type attrValString struct { - Val string `xml:"val,attr"` + Val *string `xml:"val,attr"` } // aCs directly maps the a:cs element. diff --git a/xmlPivotTable.go b/xmlPivotTable.go index 0549c5e..6e1dfb8 100644 --- a/xmlPivotTable.go +++ b/xmlPivotTable.go @@ -187,7 +187,7 @@ type xlsxItem struct { F bool `xml:"f,attr,omitempty"` M bool `xml:"m,attr,omitempty"` C bool `xml:"c,attr,omitempty"` - X int `xml:"x,attr,omitempty,omitempty"` + X int `xml:"x,attr,omitempty"` D bool `xml:"d,attr,omitempty"` E bool `xml:"e,attr,omitempty"` } diff --git a/xmlWorksheet.go b/xmlWorksheet.go index 8f39adf..57fd43f 100644 --- a/xmlWorksheet.go +++ b/xmlWorksheet.go @@ -27,7 +27,7 @@ type xlsxWorksheet struct { ProtectedRanges *xlsxInnerXML `xml:"protectedRanges"` Scenarios *xlsxInnerXML `xml:"scenarios"` AutoFilter *xlsxAutoFilter `xml:"autoFilter"` - SortState *xlsxInnerXML `xml:"sortState"` + SortState *xlsxSortState `xml:"sortState"` DataConsolidate *xlsxInnerXML `xml:"dataConsolidate"` CustomSheetViews *xlsxCustomSheetViews `xml:"customSheetViews"` MergeCells *xlsxMergeCells `xml:"mergeCells"` @@ -47,7 +47,7 @@ type xlsxWorksheet struct { SmartTags *xlsxInnerXML `xml:"smartTags"` Drawing *xlsxDrawing `xml:"drawing"` LegacyDrawing *xlsxLegacyDrawing `xml:"legacyDrawing"` - LegacyDrawingHF *xlsxInnerXML `xml:"legacyDrawingHF"` + LegacyDrawingHF *xlsxLegacyDrawingHF `xml:"legacyDrawingHF"` DrawingHF *xlsxDrawingHF `xml:"drawingHF"` Picture *xlsxPicture `xml:"picture"` OleObjects *xlsxInnerXML `xml:"oleObjects"` @@ -328,6 +328,16 @@ type xlsxRow struct { C []xlsxC `xml:"c"` } +// xlsxSortState directly maps the sortState element. This collection +// preserves the AutoFilter sort state. +type xlsxSortState struct { + ColumnSort bool `xml:"columnSort,attr,omitempty"` + CaseSensitive bool `xml:"caseSensitive,attr,omitempty"` + SortMethod string `xml:"sortMethod,attr,omitempty"` + Ref string `xml:"ref,attr"` + Content string `xml:",innerxml"` +} + // xlsxCustomSheetViews directly maps the customSheetViews element. This is a // collection of custom sheet views. type xlsxCustomSheetViews struct { @@ -424,7 +434,7 @@ type DataValidation struct { ShowErrorMessage bool `xml:"showErrorMessage,attr,omitempty"` ShowInputMessage bool `xml:"showInputMessage,attr,omitempty"` Sqref string `xml:"sqref,attr"` - Type string `xml:"type,attr"` + Type string `xml:"type,attr,omitempty"` Formula1 string `xml:",innerxml"` Formula2 string `xml:",innerxml"` } @@ -448,7 +458,7 @@ type DataValidation struct { type xlsxC struct { XMLName xml.Name `xml:"c"` XMLSpace xml.Attr `xml:"space,attr,omitempty"` - R string `xml:"r,attr"` // Cell ID, e.g. A1 + R string `xml:"r,attr,omitempty"` // Cell ID, e.g. A1 S int `xml:"s,attr,omitempty"` // Style reference. // Str string `xml:"str,attr,omitempty"` // Style reference. T string `xml:"t,attr,omitempty"` // Type. @@ -665,6 +675,14 @@ type xlsxLegacyDrawing struct { RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"` } +// xlsxLegacyDrawingHF specifies the explicit relationship to the part +// containing the VML defining pictures rendered in the header / footer of the +// sheet. +type xlsxLegacyDrawingHF struct { + XMLName xml.Name `xml:"legacyDrawingHF"` + RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"` +} + type xlsxInnerXML struct { Content string `xml:",innerxml"` } -- GitLab