Skip to content

Commit 58ea132

Browse files
committed
WIP:Fixed date axis padding and other axis issues
1 parent 029e3a7 commit 58ea132

5 files changed

Lines changed: 277 additions & 248 deletions

File tree

src/EPPlus.Export.ImageRenderer.Test/Chart/LineChartToSvgTests.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public void GenerateSvgForLineCharts_sheet1()
2020
var ws = p.Workbook.Worksheets[0];
2121
var renderer = new EPPlusImageRenderer.ImageRenderer();
2222

23-
//var ix = 2;
23+
//var ix = 4;
2424
//var c = ws.Drawings[ix];
2525
//var svg = renderer.RenderDrawingToSvg(c);
2626
//SaveTextFileToWorkbook($"svg\\ChartForSvg_ind{ix++}.svg", svg);
@@ -82,16 +82,16 @@ public void GenerateSvgForLineCharts()
8282
{
8383
var ws = p.Workbook.Worksheets[0];
8484
var renderer = new EPPlusImageRenderer.ImageRenderer();
85-
//var ix = 1;
86-
//var c = ws.Drawings[ix];
87-
//var svg = renderer.RenderDrawingToSvg(c);
88-
//SaveTextFileToWorkbook($"svg\\LineChartForSvg_Single{ix++}.svg", svg);
8985
var ix = 1;
90-
foreach (ExcelChart c in ws.Drawings)
91-
{
92-
var svg = renderer.RenderDrawingToSvg(c);
93-
SaveTextFileToWorkbook($"svg\\LineChartForSvg{ix++}.svg", svg);
94-
}
86+
var c = ws.Drawings[ix];
87+
var svg = renderer.RenderDrawingToSvg(c);
88+
SaveTextFileToWorkbook($"svg\\LineChartForSvg_Single{ix++}.svg", svg);
89+
//var ix = 1;
90+
//foreach (ExcelChart c in ws.Drawings)
91+
//{
92+
// var svg = renderer.RenderDrawingToSvg(c);
93+
// SaveTextFileToWorkbook($"svg\\LineChartForSvg{ix++}.svg", svg);
94+
//}
9595
}
9696
}
9797

src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/DateAxisScaleCalculator.cs

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ private static DateTime CeilingToUnit(double value, eTimeUnit unit, int interval
298298
};
299299
}
300300

301-
internal static AxisScale CalculateByWidth(double min, double max, ITextMeasurer tm, AxisOptions options)
301+
internal static AxisScale CalculateByWidthAllowDiagonal(double min, double max, ITextMeasurer tm, AxisOptions options)
302302
{
303303
var ax = options.Axis;
304304
var plotAreaWidth = options.ChartSize.Bounds.Width;
@@ -337,7 +337,7 @@ internal static AxisScale CalculateByWidth(double min, double max, ITextMeasurer
337337
MajorDateUnit = unit,
338338
Min = min,
339339
Max = max,
340-
TextOrientation = eTextOrientation.Horizontal
340+
TextOrientation = eTextOrientation.Horizontal,
341341
};
342342
}
343343
else
@@ -350,7 +350,7 @@ internal static AxisScale CalculateByWidth(double min, double max, ITextMeasurer
350350
MajorDateUnit = unit,
351351
Min = min,
352352
Max = max,
353-
TextOrientation = eTextOrientation.Diagonal
353+
TextOrientation = eTextOrientation.Diagonal,
354354
};
355355
}
356356
}
@@ -365,7 +365,7 @@ internal static AxisScale CalculateByWidth(double min, double max, ITextMeasurer
365365
MajorDateUnit = unit,
366366
Min = min,
367367
Max = max,
368-
TextOrientation = ax.TextBody.VerticalText==OfficeOpenXml.Drawing.eTextVerticalType.Horizontal ? eTextOrientation.Horizontal : eTextOrientation.Vertical
368+
TextOrientation = ax.TextBody.VerticalText==OfficeOpenXml.Drawing.eTextVerticalType.Horizontal ? eTextOrientation.Horizontal : eTextOrientation.Vertical,
369369
};
370370
}
371371

@@ -487,10 +487,9 @@ private static bool FitAsVerticalDiagonalText(double min, double max, int interv
487487
}
488488
}
489489

490-
internal static AxisScale CalculateByHeight(double min, double max, ITextMeasurer tm, AxisOptions options)
490+
internal static AxisScale CalculateByWidthHeight(double widthOrHeight, double min, double max, ITextMeasurer tm, AxisOptions options)
491491
{
492492
var ax = options.Axis;
493-
var plotAreaHeight = options.ChartSize.Bounds.Height;
494493
var mf = ax.Font.GetMeasureFont();
495494
int interval;
496495
eTimeUnit unit;
@@ -505,11 +504,45 @@ internal static AxisScale CalculateByHeight(double min, double max, ITextMeasure
505504
{
506505
interval = 1;
507506
unit = eTimeUnit.Days;
507+
var range = max - min;
508+
509+
double axis_min, axis_max;
510+
if (options.AddPadding)
511+
{
512+
axis_min = options.LockedMin ?? Math.Floor((min - 0.5 * range) / interval) * interval;
513+
axis_max = options.LockedMax ?? Math.Ceiling((max + 0.2 * range) / interval) * interval;
514+
}
515+
else
516+
{
517+
axis_min = min;
518+
axis_max = max;
519+
}
508520
//Get interval for maximum width with vertical text.
509-
while(FitAsVerticalDiagonalText(min, max, interval, unit, res.Height, res.Height * 0.3, plotAreaHeight) == false)
521+
while (FitAsVerticalDiagonalText(axis_min, axis_max, interval, unit, res.Height, res.Height * 0.3, widthOrHeight) == false)
510522
{
511523
AddIntervall(ref interval, ref unit);
524+
double days;
525+
switch (unit)
526+
{
527+
case eTimeUnit.Months:
528+
days = 30 * interval;
529+
break;
530+
case eTimeUnit.Years:
531+
days = 365 * interval;
532+
break;
533+
default:
534+
days = interval;
535+
break;
536+
}
537+
538+
if (options.AddPadding)
539+
{
540+
axis_min = options.LockedMin ?? Math.Floor((min - 0.5 * range) / days) * days;
541+
axis_max = options.LockedMax ?? Math.Ceiling((max + 0.2 * range) / days) * days;
542+
}
512543
}
544+
min = axis_min;
545+
max = axis_max;
513546
}
514547

515548
return new AxisScale()

src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChartAxis.cs

Lines changed: 43 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ Date Author Change
2222
using OfficeOpenXml.Drawing;
2323
using OfficeOpenXml.Drawing.Chart;
2424
using OfficeOpenXml.Drawing.Chart.Style;
25+
using OfficeOpenXml.FormulaParsing.Excel.Functions.DateAndTime;
2526
using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical;
2627
using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions;
28+
using OfficeOpenXml.FormulaParsing.Utilities;
2729
using OfficeOpenXml.Style;
2830
using OfficeOpenXml.Style.XmlAccess;
2931
using OfficeOpenXml.Utils.String;
@@ -218,6 +220,12 @@ public List<object> Values
218220
public double MinorUnit { get; set; }
219221
public eTimeUnit? MajorDateUnit { get; set; }
220222
public eTextOrientation LabelOrientation { get; set; }
223+
public bool IsDateScale
224+
{
225+
get;
226+
private set;
227+
} = false;
228+
221229
internal override void AppendRenderItems(List<RenderItem> renderItems)
222230
{
223231
Title?.AppendRenderItems(renderItems);
@@ -277,15 +285,11 @@ internal void AddTickmarksAndValues(List<RenderItem> DefItems)
277285
if(Axis.HasMajorGridlines)
278286
{
279287
MajorGridlinePositions = AddGridlines(MajorUnit, double.NaN, Axis.MajorGridlines, Chart.StyleManager.Style.GridlineMajor);
280-
//DefItems.Add(MajorGridlinePositions[0]);
281-
//MajorGridlinePositions.RemoveAt(0);
282288
}
283289

284290
if ((Axis.HasMinorGridlines))
285291
{
286292
MinorGridlinePositions = AddGridlines(MinorUnit, MajorUnit, Axis.MinorGridlines, Chart.StyleManager.Style.GridlineMinor);
287-
//DefItems.Add(MinorGridlinePositions[0]);
288-
//MinorGridlinePositions.RemoveAt(0);
289293
}
290294

291295
if (Axis.CrossBetween == eCrossBetween.MidCat)
@@ -497,42 +501,22 @@ private double GetAxisItemLeft(int i, OfficeOpenXml.Interfaces.Drawing.Text.Text
497501
{
498502
return Rectangle.Left;
499503
}
500-
//else if (Axis.AxisPosition == eAxisPosition.Right)
501-
//{
502-
503-
// return Rectangle.Left;
504-
//}
505504
else
506505
{
507-
//if ((Axis.AxisType == eAxisType.Cat || Axis.IsVertical==false) && LabelOrientation==eTextOrientation.Horizontal)
508-
//{
509-
// //Between tickmarks
510-
// var majorWidth = Rectangle.Width / (AxisValues.Count);
511-
// var majorTickStartingPosition = Rectangle.Left + majorWidth * i;
512-
// //var middleOfBounds = majorTickStartingPosition + (majorWidth / 2);
513-
// return majorTickStartingPosition;
514-
//}
515-
//else
516-
//{
517-
if(Axis.AxisType == eAxisType.Cat || Axis.AxisType==eAxisType.Date)
518-
{
519-
var majorWidth = Rectangle.Width / AxisValues.Count;
520-
var majorTickStartingPosition = Rectangle.Left + majorWidth * i;
521-
if(Axis.AxisType == eAxisType.Date)
522-
{
523-
return majorTickStartingPosition + majorWidth / 2 - m.Width / 2;
524-
}
525-
//var middleOfBounds = majorTickStartingPosition + (majorWidth / 2);
526-
return majorTickStartingPosition;
527-
}
528-
else
529-
{
530-
var min = ConvertUtil.GetValueDouble(Values[0]);
531-
var max = ConvertUtil.GetValueDouble(Values.Last());
532-
var v = ConvertUtil.GetValueDouble(Values[i]);
533-
var majorWidth = Rectangle.Width * (v - Min) / (Max - Min);
534-
return Rectangle.Left + majorWidth;
535-
}
506+
if (Axis.AxisType == eAxisType.Cat || (Axis.AxisType == eAxisType.Date && IsDateScale == false))
507+
{
508+
var majorWidth = Rectangle.Width / AxisValues.Count;
509+
var majorTickStartingPosition = Rectangle.Left + majorWidth * i;
510+
return majorTickStartingPosition + majorWidth / 2 - m.Width / 2;
511+
}
512+
else
513+
{
514+
var min = ConvertUtil.GetValueDouble(Values[0]);
515+
var max = ConvertUtil.GetValueDouble(Values.Last());
516+
var v = ConvertUtil.GetValueDouble(Values[i]);
517+
var majorWidth = Rectangle.Width * (v - Min) / (Max - Min);
518+
return Rectangle.Left + majorWidth;
519+
}
536520
//}
537521
}
538522
}
@@ -865,7 +849,7 @@ internal double GetPositionInPlotarea(double val, bool startValue=false)
865849
else
866850
{
867851
if (val < Min || val > Max) return double.NaN;
868-
var diff = Max - Min + 1;
852+
var diff = Max - Min;
869853
return (Max - val) / diff * SvgChart.Plotarea.Rectangle.Height;
870854
}
871855
}
@@ -886,28 +870,28 @@ internal double GetPositionInPlotarea(double val, bool startValue=false)
886870
else
887871
{
888872
if (val < Min || val > Max) return double.NaN;
889-
var diff = Max - Min + 1;
873+
var diff = Max - Min;
890874
return (((val - Min) / diff * SvgChart.Plotarea.Rectangle.Width));
891875
}
892876
}
893877
}
894878
protected List<object> GetAxisValue(ExcelChartAxisStandard ax, RenderItem rect, out double? min, out double? max, out double? majorUnit, out eTimeUnit? dateUnit, out eTextOrientation orientation)
895879
{
896880
var values = ax.GetAxisValues(out bool isCount);
897-
881+
var isNumeric = values.Any(x => x == null || x.IsNumeric());
898882
var options = new AxisOptions
899883
{
900884
LockedMin = ax.MinValue,
901885
LockedMax = ax.MaxValue,
902886
LockedInterval = ax.MajorUnit,
903887
LockedIntervalUnit = ax.MajorTimeUnit,
904-
AddPadding = ax.AxisType==eAxisType.Val,//(ax.AxisPosition == eAxisPosition.Left || ax.AxisPosition == eAxisPosition.Right),
888+
AddPadding = ShouldHavePadding(),
905889
Axis = ax,
906890
IsStacked100 = Chart.IsTypePercentStacked(),
907891
ChartSize = rect
908892
};
909893

910-
if (ax.AxisType == eAxisType.Cat &&
894+
if ((ax.AxisType == eAxisType.Cat || (ax.IsDate && isNumeric==false)) &&
911895
isCount == false)
912896
{
913897
//min = 0;
@@ -970,17 +954,25 @@ protected List<object> GetAxisValue(ExcelChartAxisStandard ax, RenderItem rect,
970954
AxisScale res;
971955
if (ax.IsVertical)
972956
{
973-
//res = DateAxisScaleCalculator.Calculate(min ?? 0, max ?? 0, length, options);
974-
res = DateAxisScaleCalculator.CalculateByHeight(min ?? 0D, max ?? 0D, SvgChart.TextMeasurer, options);
957+
res = DateAxisScaleCalculator.CalculateByWidthHeight(options.ChartSize.Bounds.Height, min ?? 0D, max ?? 0D, SvgChart.TextMeasurer, options);
975958
}
976959
else
977960
{
978-
res = DateAxisScaleCalculator.CalculateByWidth(min ?? 0D, max ?? 0D, SvgChart.TextMeasurer, options);
961+
if (Chart.IsTypeBar())
962+
{
963+
res = DateAxisScaleCalculator.CalculateByWidthHeight(options.ChartSize.Bounds.Width, min ?? 0D, max ?? 0D, SvgChart.TextMeasurer, options);
964+
}
965+
else
966+
{
967+
res = DateAxisScaleCalculator.CalculateByWidthAllowDiagonal(min ?? 0D, max ?? 0D, SvgChart.TextMeasurer, options);
968+
}
979969
}
980970
orientation = res.TextOrientation;
981971
dateUnit = res.MajorDateUnit;
972+
majorUnit = res.MajorInterval;
982973
var dt = DateTime.FromOADate(res.Min);
983974
var maxDt = DateTime.FromOADate(res.Max);
975+
IsDateScale = (dateUnit != eTimeUnit.Days || majorUnit > 1) && values.Count > 31;
984976
while (dt <= maxDt)
985977
{
986978
l.Add(dt);
@@ -1000,7 +992,6 @@ protected List<object> GetAxisValue(ExcelChartAxisStandard ax, RenderItem rect,
1000992

1001993
min = res.Min;
1002994
max = res.Max;
1003-
majorUnit = res.MajorInterval;
1004995
}
1005996
else
1006997
{
@@ -1019,5 +1010,10 @@ protected List<object> GetAxisValue(ExcelChartAxisStandard ax, RenderItem rect,
10191010

10201011
return l;
10211012
}
1013+
1014+
private bool ShouldHavePadding()
1015+
{
1016+
return Axis.AxisType == eAxisType.Val || (Chart.IsTypeLine() && Axis.AxisType == eAxisType.Date);
1017+
}
10221018
}
10231019
}

0 commit comments

Comments
 (0)