Skip to content

Commit 74afba2

Browse files
authored
Merge pull request #2251 from EPPlusSoftware/feature/chart_to_svg
Feature/chart to svg
2 parents e6cd073 + ffce4c4 commit 74afba2

55 files changed

Lines changed: 2008 additions & 564 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/EPPlus.Export.ImageRenderer.Test/SvgPathTests.cs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ public void CustomPath()
346346
var ws = p.Workbook.Worksheets[0];
347347
//var d = ws.Drawings[0].As.Shape;
348348
//Assert.AreEqual(1, d.CustomGeom.DrawingPaths.Count);
349-
//d.Text = "Rectangle Rectangle Rectangle Rectangle";
349+
//d.Textbox = "Rectangle Rectangle Rectangle Rectangle";
350350
//d.TextAlignment = OfficeOpenXml.Drawing.eTextAlignment.Left;
351351
//d.TextAnchoring = OfficeOpenXml.Drawing.eTextAnchoringType.Bottom;
352352
var renderer = new EPPlusImageRenderer.ImageRenderer();
@@ -511,17 +511,39 @@ public void GenerateSvgForCharts()
511511
{
512512
var ws = p.Workbook.Worksheets[0];
513513
var renderer = new EPPlusImageRenderer.ImageRenderer();
514-
//var svg = renderer.RenderDrawingToSvg(ws.Drawings[1]);
515-
//File.WriteAllText($"c:\\temp\\ChartForSvg{1}.svg", svg);
516-
int ix = 1;
517-
foreach (ExcelChart d in ws.Drawings)
514+
//var ix = 5;
515+
//var c = ws.Drawings[ix];
516+
//var svg = renderer.RenderDrawingToSvg(c);
517+
//SaveTextFileToWorkbook($"svg\\ChartForSvg{ix++}.svg", svg);
518+
var ix = 1;
519+
foreach (ExcelChart c in ws.Drawings)
518520
{
519-
var svg = renderer.RenderDrawingToSvg(d);
520-
SaveTextFileToWorkbook($"svg\\ChartForSvg{ix}.svg", svg);
521-
ix++;
521+
var svg = renderer.RenderDrawingToSvg(c);
522+
SaveTextFileToWorkbook($"svg\\ChartForSvg{ix++}.svg", svg);
522523
}
523524
}
524525
}
526+
[TestMethod]
527+
public void GenerateSvgForCharts_sheet2()
528+
{
529+
ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project");
530+
using (var p = OpenTemplatePackage("ChartForSvg.xlsx"))
531+
{
532+
var ws = p.Workbook.Worksheets[1];
533+
var renderer = new EPPlusImageRenderer.ImageRenderer();
534+
var ix = 1;
535+
var c = ws.Drawings[ix];
536+
var svg = renderer.RenderDrawingToSvg(c);
537+
SaveTextFileToWorkbook($"svg\\ChartForSvg_sheet2_{ix++}.svg", svg);
538+
//var ix = 1;
539+
//foreach (ExcelChart c in ws.Drawings)
540+
//{
541+
// var svg = renderer.RenderDrawingToSvg(c);
542+
// SaveTextFileToWorkbook($"svg\\ChartForSvg{ix++}.svg", svg);
543+
//}
544+
}
545+
}
546+
525547
[TestMethod]
526548
public void CreateChartsWithDifferentSize()
527549
{
@@ -553,7 +575,6 @@ public void CreateChartsWithDifferentSize()
553575
chart3.SetSize(800, 400);
554576

555577
SaveAndCleanup(p);
556-
557578
}
558579
}
559580

src/EPPlus.Export.ImageRenderer.Test/TestBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ protected static FileInfo GetTemplateFile(string name)
258258
}
259259
else
260260
{
261-
t = new FileInfo(_testInputPathOptional + name);
261+
t = new FileInfo(_testInputPathOptional + name);
262262
if (t.Exists)
263263
{
264264
return t;

src/EPPlus.Export.ImageRenderer/DrawingBase.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Date Author Change
1212
*************************************************************************************************/
1313

1414
using EPPlus.Export.ImageRenderer;
15+
using EPPlus.Graphics;
1516
using EPPlusImageRenderer.RenderItems;
1617
using OfficeOpenXml;
1718
using OfficeOpenXml.Drawing;
@@ -22,11 +23,13 @@ Date Author Change
2223

2324
namespace EPPlusImageRenderer
2425
{
25-
internal abstract class DrawingBase : RenderItem
26+
internal abstract class DrawingBase
2627
{
2728
protected ExcelWorkbook _wb;
29+
protected ExcelDrawing _drawing;
30+
protected ExcelTheme _theme;
2831

29-
internal DrawingBase(ExcelDrawing drawing) : base(drawing)
32+
internal DrawingBase(ExcelDrawing drawing)
3033
{
3134
drawing.GetSizeInPixels(out int width, out int height);
3235
Drawing = drawing;
@@ -40,5 +43,6 @@ internal DrawingBase(ExcelDrawing drawing) : base(drawing)
4043
internal ITextMeasurer TextMeasurer { get; }
4144
public List<RenderItem> RenderItems { get; } = new List<RenderItem>();
4245
public DrawingSize Size { get; internal set; }
46+
internal BoundingBox Bounds = new BoundingBox();
4347
}
4448
}

src/EPPlus.Export.ImageRenderer/DrawingChart.cs

Lines changed: 117 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ Date Author Change
1313

1414
using EPPlusImageRenderer.RenderItems;
1515
using OfficeOpenXml.Drawing.Chart;
16+
using OfficeOpenXml.FormulaParsing.Excel.Functions.Finance;
17+
using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions;
1618
using OfficeOpenXml.Utils.TypeConversion;
19+
using System;
1720
using System.Collections.Generic;
1821
using System.Linq;
1922

@@ -27,17 +30,20 @@ public DrawingChart(ExcelChart chart) : base(chart)
2730
}
2831
public ExcelChart Chart { get; set; }
2932

30-
protected List<object> GetAxisValue(ExcelChartAxisStandard ax, RenderItem rect)
33+
protected List<object> GetAxisValue(ExcelChartAxisStandard ax, RenderItem rect, out double? min, out double? max, out double? majorUnit)
3134
{
32-
rect.GetBounds(out double x, out double y, out double w, out double h);
33-
var values = ax.GetAxisValues();
34-
if(ax.AxisType==eAxisType.Cat)
35+
var values = ax.GetAxisValues(out bool isCount);
36+
if(ax.AxisType == eAxisType.Cat &&
37+
isCount == false)
3538
{
39+
min = 0;
40+
max = values.Length;
41+
majorUnit = 1;
3642
return values.ToList();
3743
}
3844
var l = new List<object>();
39-
var min = double.MaxValue;
40-
var max = double.MinValue;
45+
min = double.MaxValue;
46+
max = double.MinValue;
4147
foreach (var v in values)
4248
{
4349
var d = ConvertUtil.GetValueDouble(v, false, true);
@@ -54,26 +60,122 @@ protected List<object> GetAxisValue(ExcelChartAxisStandard ax, RenderItem rect)
5460
max = d;
5561
}
5662
}
57-
var majorUnit = ax.MajorUnit ?? GetAutoUnit(min, max);
58-
63+
var maxMajorTickmarks = 10; //TODO: Calculate based on rect size
64+
GetAutoMinMaxValue(ax, maxMajorTickmarks, isCount, ref min, ref max, out majorUnit);
65+
for(var v=min; v<=max;v+=majorUnit)
66+
{
67+
l.Add(v);
68+
}
5969
return l;
70+
}
71+
72+
private void GetAutoMinMaxValue(ExcelChartAxisStandard ax, int maxMajorTickmarks, bool isCount, ref double? min, ref double? max, out double? majorUnit)
73+
{
74+
if(ax.MinValue.HasValue)
75+
{
76+
min = ax.MinValue;
77+
}
78+
else
79+
{
80+
if (isCount)
81+
{
82+
min = 1;
83+
}
84+
else
85+
{
86+
var diffFromZero = (max - min) / max;
87+
if (diffFromZero > 0.091)
88+
{
89+
min = 0;
90+
}
91+
}
92+
}
93+
94+
if(isCount)
95+
{
96+
majorUnit = 1;
97+
}
98+
else
99+
{
100+
if (ax.MaxValue.HasValue)
101+
{
102+
max = ax.MaxValue;
103+
majorUnit = ax.MajorUnit ?? GetAutoUnit(min.Value, max.Value);
104+
if (ax.MinValue.HasValue == false)
105+
{
106+
var newMin = max - majorUnit;
107+
while (newMin > min)
108+
{
109+
newMin -= majorUnit.Value;
110+
}
111+
min = newMin;
112+
}
113+
}
114+
else
115+
{
116+
majorUnit = ax.MajorUnit ?? GetAutoUnit(min.Value, max.Value);
117+
if (isCount == false)
118+
{
119+
var diff = max.Value - min.Value;
120+
var newMax = min.Value + majorUnit;
121+
while ((newMax - min) < (diff * 1.05))
122+
{
123+
newMax += majorUnit.Value;
124+
}
125+
max = newMax;
126+
}
127+
if(min != 0 && max-min<9)
128+
{
129+
min -= 2;
130+
}
131+
}
132+
var newUnit = majorUnit;
133+
while (newUnit >= 2 && (max - min) / newUnit > maxMajorTickmarks)
134+
{
135+
newUnit /= 2;
136+
}
137+
}
60138
}
61139

62140
private double GetAutoUnit(double min, double max)
63141
{
64142
var diff = max - min;
65-
66-
var unit = diff / 10;
67-
var rest = unit % 10d;
68-
if (rest < 5)
143+
if (diff < 8)
69144
{
70-
unit -= rest;
145+
return 1;
71146
}
72147
else
73148
{
74-
unit += 10 - rest;
149+
var rawMajorUnit = diff;
150+
var exponent = Math.Floor(Math.Log10(rawMajorUnit));
151+
var fraction = rawMajorUnit / (Math.Pow(10, exponent));
152+
double unit;
153+
if (fraction <= 1)
154+
{
155+
unit = 1D;
156+
}
157+
else if (fraction <= 2)
158+
{
159+
unit = 2;
160+
}
161+
else if (fraction <= 2.5)
162+
{
163+
unit = 2.5;
164+
}
165+
else if (fraction <= 5)
166+
{
167+
unit = 5;
168+
}
169+
else
170+
{
171+
unit = 10;
172+
}
173+
174+
var axMax = unit * Math.Pow(10, exponent);
175+
var axMin = Math.Floor(min / axMax) * axMax;
176+
axMax = Math.Ceiling(max / axMax) * axMax;
177+
return axMax / 10;
75178
}
76-
return unit;
77179
}
78180
}
79181
}

src/EPPlus.Export.ImageRenderer/RenderItems/RenderItem.cs

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ internal RenderItem(ExcelDrawing drawing)
4040
_drawing = drawing;
4141
_theme = drawing._drawings.Worksheet.Workbook.ThemeManager.GetOrCreateTheme();
4242
}
43+
internal bool IsEndOfGroup { get; set; } = false;
4344
public string FillColor { get; set; }
4445
public string FilterName { get; set; }
4546
public DrawGradientFill GradientFill { get; set; }
@@ -52,6 +53,7 @@ internal RenderItem(ExcelDrawing drawing)
5253
public double? BorderWidth { get; set; }
5354
public double[] BorderDashArray { get; set; }
5455
public double? BorderDashOffset { get; set; }
56+
public eLineCap LineCap { get; set; } = eLineCap.Flat;
5557
public SvgLineJoin LineJoin { get; set; } = SvgLineJoin.Miter;
5658
public double? BorderOpacity { get; set; }
5759
public PathFillMode FillColorSource { get; set; } = PathFillMode.Norm;
@@ -67,10 +69,27 @@ protected void CloneBase(RenderItem item)
6769
item.BorderDashOffset = BorderDashOffset;
6870
item.BorderOpacity = BorderOpacity;
6971
item.LineJoin = LineJoin;
72+
item.LineCap = LineCap;
7073
item.FillColorSource = FillColorSource;
7174
}
7275

7376
internal virtual void SetDrawingPropertiesFill(ExcelDrawingFill fill, ExcelDrawingColorManager color)
77+
{
78+
switch (fill.Style)
79+
{
80+
81+
case eFillStyle.PatternFill:
82+
PatternFill = fill.PatternFill;
83+
break;
84+
case eFillStyle.BlipFill:
85+
BlipFill = fill.BlipFill;
86+
break;
87+
default:
88+
SetDrawingPropertiesFill((ExcelDrawingFillBasic)fill, color);
89+
break;
90+
}
91+
}
92+
internal virtual void SetDrawingPropertiesFill(ExcelDrawingFillBasic fill, ExcelDrawingColorManager color)
7493
{
7594
switch (fill.Style)
7695
{
@@ -91,12 +110,6 @@ internal virtual void SetDrawingPropertiesFill(ExcelDrawingFill fill, ExcelDrawi
91110
GradientFill = new DrawGradientFill(_theme, fill.GradientFill);
92111
FillColor = null;
93112
break;
94-
case eFillStyle.PatternFill:
95-
PatternFill = fill.PatternFill;
96-
break;
97-
case eFillStyle.BlipFill:
98-
BlipFill = fill.BlipFill;
99-
break;
100113
}
101114
}
102115
internal virtual void SetDrawingPropertiesBorder(ExcelDrawingBorder border, ExcelChartStyleColorManager color, bool hasBorder, double defaultWidth=1.5)
@@ -200,23 +213,6 @@ private string GetFillColor(ExcelDrawingFillBasic fill, ExcelDrawingColorManager
200213
internal BoundingBox Bounds = new BoundingBox();
201214
internal abstract void GetBounds(out double il, out double it, out double ir, out double ib);
202215

203-
internal string RenderSvgElement(SvgElement element)
204-
{
205-
string retStr = string.Empty;
206-
207-
using (var ms = EPPlusMemoryManager.GetStream())
208-
{
209-
SvgWriter writer = new SvgWriter(ms, Encoding.UTF8);
210-
writer.RenderSvgElement(element, true);
211-
ms.Position = 0;
212-
using (var sr = new StreamReader(ms))
213-
{
214-
retStr = sr.ReadToEnd();
215-
return retStr;
216-
}
217-
}
218-
}
219-
220216
internal void SetTheme(ExcelTheme theme)
221217
{
222218
_theme = theme;
@@ -229,6 +225,5 @@ internal abstract class RenderItemBase
229225
{
230226
public abstract RenderItemType Type { get; }
231227
public abstract void Render(StringBuilder sb);
232-
233228
}
234229
}

src/EPPlus.Export.ImageRenderer/RenderItems/Shared/DrawingBaseItem.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,6 @@ internal void ImportEpplusDrawing(ExcelShapeBase drawing)
2222
Bounds.Top = drawing.GetPixelTop();
2323
Bounds.Width = drawing.GetPixelWidth();
2424
Bounds.Height = drawing.GetPixelHeight();
25-
26-
SetDrawingPropertiesFill(drawing.Fill, null);
27-
SetDrawingPropertiesBorder(drawing.Border, null, drawing.Border != null);
28-
}
29-
30-
public override RenderItemType Type => RenderItemType.Group;
31-
32-
internal override void GetBounds(out double il, out double it, out double ir, out double ib)
33-
{
34-
il = Bounds.Left; it = Bounds.Top; ir = Bounds.Right; ib = Bounds.Bottom;
3525
}
3626
}
3727
}

0 commit comments

Comments
 (0)