diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 04f80c67f3..6ccdb3125f 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -4,7 +4,6 @@
-
diff --git a/src/EPPlus.Compression/EPPlus.Compression.csproj b/src/EPPlus.Compression/EPPlus.Compression.csproj
index de594e155c..c7093d41c9 100644
--- a/src/EPPlus.Compression/EPPlus.Compression.csproj
+++ b/src/EPPlus.Compression/EPPlus.Compression.csproj
@@ -6,5 +6,6 @@
OfficeOpenXml.Packaging.Ionic
true
EPPlus.Compression.snk
+ false
\ No newline at end of file
diff --git a/src/EPPlus.DrawingRenderer.Tests/Chart/BarChartTests.cs b/src/EPPlus.DrawingRenderer.Tests/Chart/BarChartTests.cs
index 364ea2ef7e..f992657e70 100644
--- a/src/EPPlus.DrawingRenderer.Tests/Chart/BarChartTests.cs
+++ b/src/EPPlus.DrawingRenderer.Tests/Chart/BarChartTests.cs
@@ -51,7 +51,7 @@ public void DatalabelBarCharts()
{
var ws = p.Workbook.Worksheets[0];
var drawings = ws.Drawings;
- var ix = 1;
+ var ix = 0;
for (int i = ix; i < drawings.Count; i++)
{
diff --git a/src/EPPlus.DrawingRenderer.Tests/Chart/ChartStyleFallbackTest.cs b/src/EPPlus.DrawingRenderer.Tests/Chart/ChartStyleFallbackTest.cs
index 110a010eb7..89d91fc863 100644
--- a/src/EPPlus.DrawingRenderer.Tests/Chart/ChartStyleFallbackTest.cs
+++ b/src/EPPlus.DrawingRenderer.Tests/Chart/ChartStyleFallbackTest.cs
@@ -5,347 +5,537 @@
using System.Collections.Generic;
using System.Drawing;
using tc = OfficeOpenXml.Utils.TypeConversion;
+using System.Globalization;
+using OfficeOpenXml.Drawing.Style.Coloring;
+using OfficeOpenXml.Drawing.Theme;
namespace EPPlus.DrawingRenderer.Tests.Chart
{
[TestClass]
public class ChartStyleFallbackTest : TestBase
{
- [TestMethod]
- public void ReadExcelFile()
+
+ [AssemblyInitialize]
+ public static async Task AssemblyInit(TestContext context)
{
ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project");
-
CreatePathIfNotExists("StyleExamples\\");
- using (var p = OpenTemplatePackage("StyleExamples\\ExcelUnchangedEmptyChart.xlsx"))
+ }
+
+ private void CreateStyleExampleAndExportIt(string fileName, Func, bool> assertIfTestSuccessful)
+ {
+ bool testSucceded = false;
+
+ using (var p = OpenTemplatePackage($"StyleExamples\\{fileName}.xlsx"))
{
var ws = p.Workbook.Worksheets[0];
- foreach (ExcelChart c in ws.Drawings)
- {
- var borderRef = c.StyleManager.Style.ChartArea.BorderReference;
- var borderSetting = c.Border;
- var borderDirectColor = borderSetting.Fill.Color;
+ List outputSvgs = new List();
- var svg = c.ToSvg();
- SaveTextFileToWorkbook($"svg\\ExcelDefault{ws.Name}_{c.Name}.svg", svg);
+ foreach (var d in ws.Drawings)
+ {
+ if (d is ExcelChart c)
+ {
+ var svg = c.ToSvg();
+ outputSvgs.Add(svg);
+ SaveTextFileToWorkbook($"svg\\{fileName}_{ws.Name}_{c.Name}.svg", svg);
+ }
}
- var fi = GetOutputFile("StyleExamples", "ExcelUnchangedEmptyChart_out.xlsx");
+
+ testSucceded = assertIfTestSuccessful(outputSvgs);
+
+ var fi = GetOutputFile("StyleExamples", $"{fileName}_Out.xlsx");
p.SaveAs(fi);
}
+ Assert.IsTrue(testSucceded);
}
[TestMethod]
- public void EpplusGeneratedChart()
+ public void ReadExcelFile()
{
- ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project");
+ CreateStyleExampleAndExportIt("ExcelUnchangedEmptyChart", (List outputSvgs) =>
+ {
+ //Create expected color
+ var col = Color.FromArgb(217, 217, 217);
+ var expectedStr = ColorTranslator.ToHtml(col).ToLower();
- CreatePathIfNotExists("StyleExamples\\");
+ var svgSplitOnSpace = outputSvgs[0].Split(' ');
+
+ //Get the first stroke and extract the hexCode for the expected color
+ var firstStroke = svgSplitOnSpace.First(s => s.StartsWith("stroke"));
+ var colorResult = firstStroke.Substring(8, 7).ToLower();
+
+ //Get the resulting width
+ var strokeWidth = svgSplitOnSpace.First(s => s.StartsWith("stroke-width"));
+ var widthStr = strokeWidth.Substring(14, strokeWidth.Length - 14 -1).ToLower();
+ var widthResult = double.Parse(widthStr, CultureInfo.InvariantCulture);
- using (var p = OpenPackage("StyleExamples\\epplusDefaultTest.xlsx",true))
+ //Assert
+ Assert.AreEqual(expectedStr, colorResult);
+ Assert.AreEqual(1d, widthResult);
+
+ return expectedStr == colorResult && 1d == widthResult;
+ });
+ }
+
+ [TestMethod]
+ public void ReadEmptyDefaultChartStyle()
+ {
+ CreateStyleExampleAndExportIt("emptyDefault", (List outputSvgs) =>
{
- var ws = p.Workbook.Worksheets.Add("EpplusGeneratedChart");
+ //Create expected color
+ var col = Color.FromArgb(217, 217, 217);
+ var expectedStr = ColorTranslator.ToHtml(col).ToLower();
- //ws.Workbook.ThemeManager.GetOrCreateTheme();
+ var svgSplitOnSpace = outputSvgs[0].Split(' ');
+ //Get the first stroke and extract the hexCode for the expected color
+ var firstStroke = svgSplitOnSpace.First(s => s.StartsWith("stroke"));
+ var colorResult = firstStroke.Substring(8, 7).ToLower();
- ws.Cells["A1:A3"].Formula = "ROW()+COLUMN()";
+ //Get the resulting width
+ var strokeWidth = svgSplitOnSpace.First(s => s.StartsWith("stroke-width"));
+ var widthStr = strokeWidth.Substring(14, strokeWidth.Length - 14 - 1).ToLower();
+ var widthResult = double.Parse(widthStr, CultureInfo.InvariantCulture);
- ws.Calculate();
+ //Assert
+ Assert.AreEqual(expectedStr, colorResult);
+ Assert.AreEqual(13.3333d, widthResult, 0.003);
- var emptyLines = ws.Drawings.AddLineChart("EmptyLineChart", eLineChartType.Line);
- var generatedBar = ws.Drawings.AddBarChart("EpplusBarChart", eBarChartType.ColumnClustered);
+ return expectedStr == colorResult && 13.3333d == Math.Round(widthResult,4);
+ });
+ }
- generatedBar.SetPosition(1, 1000);
- var defaultRect = ws.Drawings.AddShape("MyDefaultShape", OfficeOpenXml.Drawing.eShapeStyle.Round1Rect);
- var gradientRect = ws.Drawings.AddShape("GradRect", OfficeOpenXml.Drawing.eShapeStyle.Round1Rect);
+ [TestMethod]
+ public void ReadExcelEditedRemovedStyles()
+ {
+ string fileName = "emptyManuallyRemovedLnStyles";
- defaultRect.SetPosition(300, 1);
- gradientRect.SetPosition(300, 1000);
+ CreateStyleExampleAndExportIt(fileName, (List outputSvgs) =>
+ {
+ //Create expected color
+ var col = Color.FromArgb(137, 137, 137);
+ var expectedStr = ColorTranslator.ToHtml(col).ToLower();
- defaultRect.Fill.Style = OfficeOpenXml.Drawing.eFillStyle.SolidFill;
- gradientRect.Fill.Style = OfficeOpenXml.Drawing.eFillStyle.GradientFill;
- generatedBar.Series.Add(ws.Cells["A1:A3"]);
+ var svgSplitOnSpace = outputSvgs[0].Split(' ');
- //foreach (ExcelChart c in ws.Drawings)
- //{
- // var borderRef = c.StyleManager.Style.ChartArea.BorderReference;
- // var borderSetting = c.Border;
- // var borderDirectColor = borderSetting.Fill.Color;
+ //Get the first stroke and extract the hexCode for the expected color
+ var firstStroke = svgSplitOnSpace.First(s => s.StartsWith("stroke"));
+ var colorResult = firstStroke.Substring(8, 7).ToLower();
- // var svg = c.ToSvg();
- // SaveTextFileToWorkbook($"svg\\epplusDefault{ws.Name}_{c.Name}.svg", svg);
- //}
- //GetOutputFile("StyleExamples", "");
- SaveAndCleanup(p);
- }
+ //Get the resulting width
+ var strokeWidth = svgSplitOnSpace.First(s => s.StartsWith("stroke-width"));
+ var widthStr = strokeWidth.Substring(14, strokeWidth.Length - 14 - 1).ToLower();
+ var widthResult = double.Parse(widthStr, CultureInfo.InvariantCulture);
+
+ //Assert
+ Assert.AreEqual(expectedStr, colorResult);
+ Assert.AreEqual(13.3333d, widthResult, 0.003);
+
+ return expectedStr == colorResult && 13.3333d == Math.Round(widthResult, 4);
+ });
}
[TestMethod]
- public void ReadEmptyDefaultChartStyle()
+ public void EditedTheme()
{
- ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project");
-
- CreatePathIfNotExists("StyleExamples\\");
+ string fileName = "ExcelThemeEdited";
- using (var p = OpenTemplatePackage("StyleExamples\\emptyDefault.xlsx"))
+ CreateStyleExampleAndExportIt(fileName, (List outputSvgs) =>
{
- var ws = p.Workbook.Worksheets[0];
+ //Create expected color
+ var col = Color.FromArgb(255, 255, 199, 199);
+ var expectedStr = ColorTranslator.ToHtml(col).ToLower();
- foreach (ExcelChart c in ws.Drawings)
- {
- var borderRef = c.StyleManager.Style.ChartArea.BorderReference;
- var borderSetting = c.Border;
- var borderDirectColor = borderSetting.Fill.Color;
+ var svgSplitOnSpace = outputSvgs[0].Split(' ');
- var svg = c.ToSvg();
- SaveTextFileToWorkbook($"svg\\emptyDefaultStyle{ws.Name}_{c.Name}.svg", svg);
- }
- var fi = GetOutputFile("StyleExamples", "emptyDefault_out.xlsx");
- p.SaveAs(fi);
- }
+ //Get the first stroke and extract the hexCode for the expected color
+ var firstStroke = svgSplitOnSpace.First(s => s.StartsWith("stroke"));
+ var colorResult = firstStroke.Substring(8, 7).ToLower();
+
+ //Get the resulting width
+ var strokeWidth = svgSplitOnSpace.First(s => s.StartsWith("stroke-width"));
+ var widthStr = strokeWidth.Substring(14, strokeWidth.Length - 14 - 1).ToLower();
+ var widthResult = double.Parse(widthStr, CultureInfo.InvariantCulture);
+
+ //Assert
+ Assert.AreEqual(expectedStr, colorResult);
+ Assert.AreEqual(13.3333d, widthResult, 0.003);
+
+ return expectedStr == colorResult && 13.3333d == Math.Round(widthResult, 4);
+ });
}
+
+
[TestMethod]
- public void GenerateSimpleChart()
+ public void ManualSystemText()
{
- ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project");
-
- string fileName = "EpplusSimpleChart";
+ string fileName = "ExcelThemeManualSystemText";
- using (var p = OpenPackage($"{fileName}.xlsx",true))
+ CreateStyleExampleAndExportIt(fileName, (List outputSvgs) =>
{
- var ws = p.Workbook.Worksheets.Add("s1");
- ws.Drawings.AddBarChart("simpleChart", eBarChartType.ColumnClustered);
+ //Create expected color
+ var col = Color.FromArgb(255, 0, 0, 0);
+ var expectedStr = ColorTranslator.ToHtml(col).ToLower();
- SaveAndCleanup(p);
- }
+ var svgSplitOnSpace = outputSvgs[0].Split(' ');
+
+ //Get the first stroke and extract the hexCode for the expected color
+ var firstStroke = svgSplitOnSpace.First(s => s.StartsWith("stroke"));
+ var colorResult = firstStroke.Substring(8, 7).ToLower();
+
+ //Get the resulting width
+ var strokeWidth = svgSplitOnSpace.First(s => s.StartsWith("stroke-width"));
+ var widthStr = strokeWidth.Substring(14, strokeWidth.Length - 14 - 1).ToLower();
+ var widthResult = double.Parse(widthStr, CultureInfo.InvariantCulture);
+
+ //Assert
+ Assert.AreEqual(expectedStr, colorResult);
+ Assert.AreEqual(13.3333d, widthResult, 0.003);
+
+ return expectedStr == colorResult && 13.3333d == Math.Round(widthResult, 4);
+ });
}
+
[TestMethod]
- public void ReadChartBorderThemeTint()
+ public void ExcelThemeLnDeleted()
{
- ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project");
-
- var fileName = "ChartBorderThemeTint";
- CreatePathIfNotExists("StyleExamples\\");
+ string fileName = "ExcelThemeLnDeleted";
- using (var p = OpenTemplatePackage($"StyleExamples\\{fileName}.xlsx"))
+ CreateStyleExampleAndExportIt(fileName, (List outputSvgs) =>
{
- var ws = p.Workbook.Worksheets[0];
- var lChart = ws.Drawings[0].As.Chart.LineChart;
+ //Create expected color
+ var col = Color.Transparent;
+ var expectedStr = ColorTranslator.ToHtml(col).ToLower();
- lChart.StyleManager.Style.ChartArea.Border.Fill.SolidFill.Color.SetSchemeColor(OfficeOpenXml.Drawing.eSchemeColor.Accent1);
-
- //100 - input is what excel seems to apply
- //lChart.StyleManager.Style.ChartArea.BorderReference.Color.Transforms.AddTint(13);
+ var svgSplitOnSpace = outputSvgs[0].Split(' ');
- //Adding Less Tint makes the object Lighter. Which is the inverse of how excel does it.
- lChart.StyleManager.Style.ChartArea.Border.Fill.SolidFill.Color.Transforms.AddTint(60);
- lChart.StyleManager.Style.ChartArea.Border.Width = 10d;
- lChart.StyleManager.ApplyStyles();
+ //Get the first stroke and extract the hexCode for the expected color
+ var firstStroke = svgSplitOnSpace.First(s => s.StartsWith("stroke"));
+ var colorResult = firstStroke.Substring(8, 4).ToLower();
- var fi = GetOutputFile("StyleExamples", $"{fileName}_Out.xlsx");
- p.SaveAs(fi);
- }
+ Assert.AreEqual("none", colorResult);
+
+ return "none" == colorResult;
+ });
}
+
[TestMethod]
- public void RemovedStyles()
+ public void PureExcelTheme()
{
- ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project");
+ string fileName = "PureExcelTheme";
- string fileName = "emptyManuallyRemovedLnStyles";
- CreatePathIfNotExists("StyleExamples\\");
+ //Border and Fill for chartArea expected colors
+ List ExpectedColors = new List() { "#ffcaca", "#bbd7f9" };
- using (var p = OpenTemplatePackage($"StyleExamples\\{fileName}.xlsx"))
+ //Test un-edited excel theme with custom colors set in excel
+ CreateStyleExampleAndExportIt(fileName, (List outputSvgs) =>
{
- var ws = p.Workbook.Worksheets[0];
-
- foreach (var d in ws.Drawings)
+ foreach(var svg in outputSvgs)
{
- if (d is ExcelChart c)
- {
- //var borderSetting = c.Border;
- //var borderDirectColor = borderSetting.Fill.Color;
- //var theme = p.Workbook.ThemeManager.GetOrCreateTheme();
+ var svgSplitOnSpace = svg.Split(' ');
- //var defaultColorFromTheme = theme.ColorScheme.Dark1;
+ //Get the first stroke and extract the hexCode for the expected color
+ var firstStroke = svgSplitOnSpace.First(s => s.StartsWith("stroke"));
+ var borderResult = firstStroke.Substring(8, 7).ToLower();
- var svg = c.ToSvg();
- SaveTextFileToWorkbook($"svg\\{fileName}_{ws.Name}_{c.Name}.svg", svg);
- }
+ //Get the first stroke and extract the hexCode for the expected color
+ var firstFill = svgSplitOnSpace.First(s => s.StartsWith("fill"));
+ var fillResult = firstFill.Substring(6, 7).ToLower();
+
+ Assert.AreEqual(ExpectedColors[0], borderResult);
+ Assert.AreEqual(ExpectedColors[1], fillResult);
}
- var fi = GetOutputFile("StyleExamples", $"{fileName}_Out.xlsx");
- p.SaveAs(fi);
- }
+
+ return true;
+ });
}
+
[TestMethod]
- public void EditedTheme()
+ public void ChartWithChartStyle()
{
- ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project");
+ string fileName = "ChartWithChartStyleMEdit";
- CreatePathIfNotExists("StyleExamples\\");
+ //Read test for if chart style is applied appropriately
+ CreateStyleExampleAndExportIt(fileName, (List outputSvgs) =>
+ {
+ //Create expected color
+ var col = Color.FromArgb(255, 217, 217, 217);
+ var expectedStr = ColorTranslator.ToHtml(col).ToLower();
- string fileName = "ExcelThemeEdited";
+ var svgSplitOnSpace = outputSvgs[0].Split(' ');
- using (var p = OpenTemplatePackage($"StyleExamples\\{fileName}.xlsx"))
+ //Get the first stroke and extract the hexCode for the expected color
+ var firstStroke = svgSplitOnSpace.First(s => s.StartsWith("stroke"));
+ var colorResult = firstStroke.Substring(8, 7).ToLower();
+
+ //Get the resulting width
+ var strokeWidth = svgSplitOnSpace.First(s => s.StartsWith("stroke-width"));
+ var widthStr = strokeWidth.Substring(14, strokeWidth.Length - 14 - 1).ToLower();
+ var widthResult = double.Parse(widthStr, CultureInfo.InvariantCulture);
+
+ //Assert
+ Assert.AreEqual(expectedStr, colorResult);
+ Assert.AreEqual(1d, widthResult, 0.003);
+
+ return expectedStr == colorResult && 1d == Math.Round(widthResult, 1);
+ });
+ }
+
+ [TestMethod]
+ public void ReadChartBorderThemeTint()
+ {
+ var fileName = "ChartBorderThemeTint";
+
+ //Read test for if chart style is applied appropriately
+ CreateStyleExampleAndExportIt(fileName, (List outputSvgs) =>
{
- var ws = p.Workbook.Worksheets[0];
+ //Create expected color
+ var col = Color.FromArgb(255, 217, 217, 217);
+ var expectedStr = ColorTranslator.ToHtml(col).ToLower();
- foreach (var d in ws.Drawings)
- {
- if (d is ExcelChart c)
- {
- //var borderSetting = c.Border;
- //var borderDirectColor = borderSetting.Fill.Color;
- //var theme = p.Workbook.ThemeManager.GetOrCreateTheme();
+ var svgSplitOnSpace = outputSvgs[0].Split(' ');
- //var defaultColorFromTheme = theme.ColorScheme.Dark1;
+ //Get the first stroke and extract the hexCode for the expected color
+ var firstStroke = svgSplitOnSpace.First(s => s.StartsWith("stroke"));
+ var colorResult = firstStroke.Substring(8, 7).ToLower();
- var svg = c.ToSvg();
- SaveTextFileToWorkbook($"svg\\{fileName}_{ws.Name}_{c.Name}.svg", svg);
+ //Get the resulting width
+ var strokeWidth = svgSplitOnSpace.First(s => s.StartsWith("stroke-width"));
+ var widthStr = strokeWidth.Substring(14, strokeWidth.Length - 14 - 1).ToLower();
+ var widthResult = double.Parse(widthStr, CultureInfo.InvariantCulture);
- var theme = p.Workbook.ThemeManager.GetOrCreateTheme();
- var themeColor = tc.ColorConverter.GetThemeColor(theme, eThemeSchemeColor.Text1);
- var themedLine = theme.FormatScheme.BorderStyle[0];
- //themeColor = tc.ColorConverter.ApplyTransforms(themeColor, themedLine.Fill.SolidFill.Color.Transforms);
- themeColor = tc.ColorConverter.ApplyTintDrawing(themeColor, 0.55d);
- var ExpectedColor = Color.FromArgb(255, 255, 199, 199);
- Assert.AreEqual(ExpectedColor.ToArgb(), themeColor.ToArgb());
- }
- }
- var fi = GetOutputFile("StyleExamples", $"{fileName}_Out.xlsx");
- p.SaveAs(fi);
- }
+ //Assert
+ Assert.AreEqual(expectedStr, colorResult);
+ Assert.AreEqual(1d, widthResult, 0.003);
+
+ return expectedStr == colorResult && 1d == Math.Round(widthResult, 1);
+ });
+ //using (var p = OpenTemplatePackage($"StyleExamples\\{fileName}.xlsx"))
+ //{
+ // var ws = p.Workbook.Worksheets[0];
+ // var lChart = ws.Drawings[0].As.Chart.LineChart;
+
+ // lChart.StyleManager.Style.ChartArea.Border.Fill.SolidFill.Color.SetSchemeColor(OfficeOpenXml.Drawing.eSchemeColor.Accent1);
+
+ // //100 - input is what excel seems to apply
+ // //lChart.StyleManager.Style.ChartArea.BorderReference.Color.Transforms.AddTint(13);
+
+ // //Adding Less Tint makes the object Lighter. Which is the inverse of how excel does it.
+ // lChart.StyleManager.Style.ChartArea.Border.Fill.SolidFill.Color.Transforms.AddTint(60);
+ // lChart.StyleManager.Style.ChartArea.Border.Width = 10d;
+ // lChart.StyleManager.ApplyStyles();
+
+ // var fi = GetOutputFile("StyleExamples", $"{fileName}_Out.xlsx");
+ // p.SaveAs(fi);
+ //}
}
[TestMethod]
- public void ManualSystemText()
+ public void Epp_Gen_DefaultLine()
{
- ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project");
-
- string fileName = "ExcelThemeManualSystemText";
+ using (var p = OpenPackage("StyleExamples\\epplusDefaultChart_Line.xlsx", true))
+ {
+ var ws = p.Workbook.Worksheets.Add("Empty");
- CreatePathIfNotExists("StyleExamples\\");
+ ws.Cells["A1:A3"].Formula = "ROW()+COLUMN()";
- using (var p = OpenTemplatePackage($"StyleExamples\\{fileName}.xlsx"))
- {
- var ws = p.Workbook.Worksheets[0];
+ ws.Calculate();
- foreach (var d in ws.Drawings)
+ var emptyLines = ws.Drawings.AddLineChart("Chart", eLineChartType.Line);
+ foreach (ExcelDrawing d in ws.Drawings)
{
- if (d is ExcelChart c)
- {
- //var borderSetting = c.Border;
- //var borderDirectColor = borderSetting.Fill.Color;
- //var theme = p.Workbook.ThemeManager.GetOrCreateTheme();
+ var svg = d.ToSvg();
- //var defaultColorFromTheme = theme.ColorScheme.Dark1;
+ SaveTextFileToWorkbook($"svg\\epplusDefaultChart_Line{ws.Name}_{d.Name}.svg", svg);
- var svg = c.ToSvg();
- SaveTextFileToWorkbook($"svg\\{fileName}_{ws.Name}_{c.Name}.svg", svg);
- }
+ //Create expected color
+ var fill = Color.FromArgb(255, 255, 255, 255);
+ var expectedFill = ColorTranslator.ToHtml(fill).ToLower();
+ var col = Color.FromArgb(255, 137, 137, 137);
+ var expectedStroke = ColorTranslator.ToHtml(col).ToLower();
+
+ var svgSplitOnSpace = svg.Split(' ');
+ var fills = svgSplitOnSpace.Where(s => s.StartsWith("fill")).ToArray();
+
+ //Get the first fill and extract the hexCode for the expected color
+ var firstFill = fills[0];
+ var fillResult = firstFill.Substring(6, 7).ToLower();
+
+ var secondFill = fills[1];
+ var secondResult = secondFill.Substring(6, 7).ToLower();
+
+ //Get the first stroke and extract the hexCode for the expected color
+ var firstStroke = svgSplitOnSpace.First(s => s.StartsWith("stroke"));
+ var strokeResult = firstStroke.Substring(8, 7).ToLower();
+
+ //Get the resulting width
+ var strokeWidth = svgSplitOnSpace.First(s => s.StartsWith("stroke-width"));
+ var widthStr = strokeWidth.Substring(14, strokeWidth.Length - 14 - 1).ToLower();
+ var widthResult = double.Parse(widthStr, CultureInfo.InvariantCulture);
+
+ //Assert
+ Assert.AreEqual(expectedFill, fillResult);
+ Assert.AreEqual(expectedStroke, strokeResult);
+ Assert.AreEqual(1d, widthResult, 0.003);
+ //Plot area should also be white as in Excel
+ Assert.AreEqual(expectedFill, secondResult);
}
- var fi = GetOutputFile("StyleExamples", $"{fileName}_Out.xlsx");
- p.SaveAs(fi);
+
+ SaveAndCleanup(p);
}
}
-
[TestMethod]
- public void ExcelThemeLnDeleted()
+ public void Epp_Gen_DefaultShape()
{
- ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project");
+ string fileName = "epplusShape";
- CreatePathIfNotExists("StyleExamples\\");
+ using (var p = OpenPackage($"StyleExamples\\{fileName}.xlsx", true))
+ {
+ var ws = p.Workbook.Worksheets.Add("ws1");
- string fileName = "ExcelThemeLnDeleted";
+ var defaultRect = ws.Drawings.AddShape("Default", OfficeOpenXml.Drawing.eShapeStyle.Round1Rect);
+ var gradientRect = ws.Drawings.AddShape("GradRect", OfficeOpenXml.Drawing.eShapeStyle.Round1Rect);
- using (var p = OpenTemplatePackage($"StyleExamples\\{fileName}.xlsx"))
- {
- var ws = p.Workbook.Worksheets[0];
+ var aThemeStyle = defaultRect.ThemeStyles.BorderReference;
- foreach (var d in ws.Drawings)
- {
- if (d is ExcelChart c)
- {
- //var borderSetting = c.Border;
- //var borderDirectColor = borderSetting.Fill.Color;
- //var theme = p.Workbook.ThemeManager.GetOrCreateTheme();
+ defaultRect.SetPosition(300, 1);
+ gradientRect.SetPosition(300, 1000);
- //var defaultColorFromTheme = theme.ColorScheme.Dark1;
+ var svgDefault = defaultRect.ToSvg();
+ SaveTextFileToWorkbook($"svg\\{fileName}_{ws.Name}_{defaultRect.Name}.svg", svgDefault);
- var svg = c.ToSvg();
- SaveTextFileToWorkbook($"svg\\{fileName}_{ws.Name}_{c.Name}.svg", svg);
- }
- }
- var fi = GetOutputFile("StyleExamples", $"{fileName}_Out.xlsx");
- p.SaveAs(fi);
+ //Create expected color
+ var fill = Color.FromArgb(255, 21, 96, 130);
+ var expectedFill = ColorTranslator.ToHtml(fill).ToLower();
+ var col = Color.FromArgb(255, 4, 36, 51);
+ var expectedStroke = ColorTranslator.ToHtml(col).ToLower();
+
+ var svgSplitOnSpace = svgDefault.Split(' ');
+
+ var fills = svgSplitOnSpace.Where(s => s.StartsWith("fill")).ToArray();
+
+ //Get the first fill and extract the hexCode for the expected color
+ var firstFill = fills[0];
+ var fillResult = firstFill.Substring(6, 7).ToLower();
+
+ //Get the first stroke and extract the hexCode for the expected color
+ var firstStroke = svgSplitOnSpace.First(s => s.StartsWith("stroke"));
+ var strokeResult = firstStroke.Substring(8, 7).ToLower();
+
+ //Get the resulting width
+ var strokeWidth = svgSplitOnSpace.First(s => s.StartsWith("stroke-width"));
+ var widthStr = strokeWidth.Substring(14, strokeWidth.Length - 14 - 1).ToLower();
+ var widthResult = double.Parse(widthStr, CultureInfo.InvariantCulture);
+
+ //Assert
+ Assert.AreEqual(expectedFill, fillResult);
+ Assert.AreEqual(expectedStroke, strokeResult);
+ Assert.AreEqual(1d, widthResult, 0.003);
+
+ gradientRect.Fill.Style = OfficeOpenXml.Drawing.eFillStyle.GradientFill;
+
+ var svgGradient = gradientRect.ToSvg();
+ SaveTextFileToWorkbook($"svg\\{fileName}_{ws.Name}_{gradientRect.Name}.svg", svgGradient);
+
+ SaveAndCleanup(p);
}
}
-
[TestMethod]
- public void PureExcelTheme()
+ public void EpplusGeneratedChart()
{
- ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project");
+ using (var p = OpenPackage("StyleExamples\\epplusDefaultTest.xlsx", true))
+ {
+ var ws = p.Workbook.Worksheets.Add("EpplusGeneratedChart");
- string fileName = "PureExcelTheme";
+ //ws.Workbook.ThemeManager.GetOrCreateTheme();
- CreatePathIfNotExists("StyleExamples\\");
+ //ws.Workbook.ThemeManager.GetOrCreateTheme().FormatScheme.BackgroundFillStyle[0] = true;
+ ws.Cells["A1:A3"].Formula = "ROW()+COLUMN()";
- using (var p = OpenTemplatePackage($"StyleExamples\\{fileName}.xlsx"))
- {
- var ws = p.Workbook.Worksheets[0];
+ ws.Calculate();
- foreach (var d in ws.Drawings)
- {
- if (d is ExcelChart c)
- {
- var borderSetting = c.Border;
- var borderDirectColor = borderSetting.Fill.Color;
- var theme = p.Workbook.ThemeManager.GetOrCreateTheme();
+ var emptyLines = ws.Drawings.AddLineChart("EmptyLineChart", eLineChartType.Line);
+ var generatedBar = ws.Drawings.AddBarChart("EpplusBarChart", eBarChartType.ColumnClustered);
- var defaultColorFromTheme = theme.ColorScheme.Dark1;
+ generatedBar.SetPosition(1, 1000);
- var svg = c.ToSvg();
- SaveTextFileToWorkbook($"svg\\{fileName}_{ws.Name}_{c.Name}.svg", svg);
- }
+ var defaultRect = ws.Drawings.AddShape("MyDefaultShape", OfficeOpenXml.Drawing.eShapeStyle.Round1Rect);
+ var gradientRect = ws.Drawings.AddShape("GradRect", OfficeOpenXml.Drawing.eShapeStyle.Round1Rect);
+
+ defaultRect.SetPosition(300, 1);
+ gradientRect.SetPosition(300, 1000);
+
+ //defaultRect.Fill.Style = OfficeOpenXml.Drawing.eFillStyle.SolidFill;
+ gradientRect.Fill.Style = OfficeOpenXml.Drawing.eFillStyle.GradientFill;
+ generatedBar.Series.Add(ws.Cells["A1:A3"]);
+
+ List outputSvgs = new List();
+
+ foreach (ExcelDrawing d in ws.Drawings)
+ {
+ var svg = d.ToSvg();
+ outputSvgs.Add(svg);
+ SaveTextFileToWorkbook($"svg\\epplusDefault{ws.Name}_{d.Name}.svg", svg);
}
- var fi = GetOutputFile("StyleExamples", $"{fileName}_Out.xlsx");
- p.SaveAs(fi);
+ //GetOutputFile("StyleExamples", "");
+ SaveAndCleanup(p);
}
}
-
[TestMethod]
- public void ChartWithChartStyle()
+ public void GenerateSimpleChart()
{
- ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project");
+ string fileName = "EpplusSimpleChart";
- string fileName = "ChartWithChartStyleMEdit";
+ using (var p = OpenPackage($"{fileName}.xlsx", true))
+ {
+ var ws = p.Workbook.Worksheets.Add("s1");
+ ws.Drawings.AddBarChart("simpleChart", eBarChartType.ColumnClustered);
- CreatePathIfNotExists("StyleExamples\\");
+ SaveAndCleanup(p);
+ }
+ }
- using (var p = OpenTemplatePackage($"StyleExamples\\{fileName}.xlsx"))
+ [TestMethod]
+ public void ReadObjectDefaults()
+ {
+ string fileName = "ObjectDefaultsChanged";
+
+ using (var p = OpenTemplatePackage($"{fileName}.xlsx"))
{
- var ws = p.Workbook.Worksheets[0];
+ var theme = p.Workbook.ThemeManager.GetOrCreateTheme();
+ var shapeStyle = theme.ObjectDefaults.ShapeDefinition.Style;
+ var fillRef = shapeStyle.FillReference;
- foreach (var d in ws.Drawings)
- {
- if (d is ExcelChart c)
- {
- var svg = c.ToSvg();
- SaveTextFileToWorkbook($"svg\\{fileName}_{ws.Name}_{c.Name}.svg", svg);
- }
- }
- var fi = GetOutputFile("StyleExamples", $"{fileName}_Out.xlsx");
- p.SaveAs(fi);
+ var lnRef = shapeStyle.BorderReference;
+
+ Assert.AreEqual(eSchemeColor.Accent2, lnRef.ShapeColor.SchemeColor.Color);
+ Assert.IsTrue(lnRef.ShapeColor.Transforms.Count > 0);
+ Assert.AreEqual(eColorTransformType.Shade, lnRef.ShapeColor.Transforms[0].Type);
+ Assert.AreEqual(15, lnRef.ShapeColor.Transforms[0].Value);
+ Assert.AreEqual(2, lnRef.Index);
+
+ Assert.IsTrue(fillRef.HasColor);
+ var schemeClr = fillRef.ShapeColor.SchemeColor;
+ var col = schemeClr.Color;
+ Assert.AreEqual(eSchemeColor.Accent2, col);
+ Assert.AreEqual(1, fillRef.Index);
+
+ var fontRef = theme.ObjectDefaults.ShapeDefinition.Style.FontReference;
+ Assert.AreEqual(eSchemeColor.Light1, fontRef.Color.SchemeColor.Color);
+ Assert.AreEqual(eThemeFontCollectionType.Minor, fontRef.Index);
+
+ SaveAndCleanup(p);
}
}
}
diff --git a/src/EPPlus.DrawingRenderer.Tests/Chart/ColumnChartTests.cs b/src/EPPlus.DrawingRenderer.Tests/Chart/ColumnChartTests.cs
index 6d6002687e..0f69cf48d4 100644
--- a/src/EPPlus.DrawingRenderer.Tests/Chart/ColumnChartTests.cs
+++ b/src/EPPlus.DrawingRenderer.Tests/Chart/ColumnChartTests.cs
@@ -53,5 +53,48 @@ public void GenerateSvgForColumnCharts2()
}
}
}
+ [TestMethod]
+ public void GenerateColumnChartFromBlazorSample()
+ {
+ ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project");
+ using (var p = OpenTemplatePackage("BlazorSample1-Column.xlsx"))
+ {
+ var ws = p.Workbook.Worksheets[1];
+
+ //var ix = 2;
+ //var c = ws.Drawings[ix];
+ //var svg = renderer.RenderDrawingToSvg(c);
+ //SaveTextFileToWorkbook($"svg\\ChartForSvg_ind{ix++}.svg", svg);
+
+ var ix = 0;
+ foreach (ExcelChart c in ws.Drawings)
+ {
+ var svg = c.ToSvg();
+ SaveTextFileToWorkbook($"svg\\BlazorSample_Column_sheet2_{ix++}.svg", svg);
+ }
+ }
+ }
+ [TestMethod]
+ public void GenerateBarChartFromBlazorSample()
+ {
+ ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project");
+ using (var p = OpenTemplatePackage("BlazorSample1-BarChart.xlsx"))
+ {
+ var ws = p.Workbook.Worksheets[1];
+
+ //var ix = 2;
+ //var c = ws.Drawings[ix];
+ //var svg = renderer.RenderDrawingToSvg(c);
+ //SaveTextFileToWorkbook($"svg\\ChartForSvg_ind{ix++}.svg", svg);
+
+ var ix = 0;
+ foreach (ExcelChart c in ws.Drawings)
+ {
+ var svg = c.ToSvg();
+ SaveTextFileToWorkbook($"svg\\BlazorSample_Bar_sheet2_{ix++}.svg", svg);
+ }
+ }
+ }
+
}
}
diff --git a/src/EPPlus.DrawingRenderer.Tests/Chart/LineChartToSvgTests.cs b/src/EPPlus.DrawingRenderer.Tests/Chart/LineChartToSvgTests.cs
index 6d55224e92..02d264ddae 100644
--- a/src/EPPlus.DrawingRenderer.Tests/Chart/LineChartToSvgTests.cs
+++ b/src/EPPlus.DrawingRenderer.Tests/Chart/LineChartToSvgTests.cs
@@ -1,7 +1,12 @@
using OfficeOpenXml;
using OfficeOpenXml.Drawing.Chart;
+using OfficeOpenXml.Drawing.Chart.Style;
+using OfficeOpenXml.Export.HtmlExport;
+using OfficeOpenXml.Style;
+using OfficeOpenXml.Table;
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -322,6 +327,88 @@ public void GenerateLineChartWithDropLine()
}
}
}
+ [TestMethod]
+ public void GenerateBlazorSample1()
+ {
+ ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project");
+ using (var p = OpenTemplatePackage("BlazorSample1.xlsx"))
+ {
+ var ws = p.Workbook.Worksheets[1];
+
+ //var ix = 1;
+ //var c = ws.Drawings[ix];
+ //var svg = c.ToSvg();
+ //SaveTextFileToWorkbook($"svg\\5.3-SampleLines{ix}.svg", svg);
+
+ for (int i = 0; i < ws.Drawings.Count; i++)
+ {
+ var c = ws.Drawings[i];
+ var svg = c.ToSvg();
+ SaveTextFileToWorkbook($"svg\\BlazorSample1{i}.svg", svg);
+ }
+ }
+ }
+ [TestMethod]
+ public async Task HtmlExportWithLineChart()
+ {
+ using (var package = new ExcelPackage())
+ {
+ var style = TableStyles.Dark3;
+ var sheet = package.Workbook.Worksheets.Add("Html export sample 8");
+ var csvFileInfo = new FileInfo(Path.Combine(_dataPath, $"currencies2011weekly.csv"));
+ if (csvFileInfo.Exists == false) return;
+ var format = new ExcelTextFormat
+ {
+ Delimiter = ';',
+ Culture = CultureInfo.InvariantCulture,
+ DataTypes = new eDataTypes[] { eDataTypes.DateTime, eDataTypes.Number, eDataTypes.Number, eDataTypes.Number, eDataTypes.Number }
+ };
+ var tableRange = sheet.Cells["A15"].LoadFromText(csvFileInfo, format, style, true);
+
+ sheet.Cells["B1:E1"].Style.HorizontalAlignment = ExcelHorizontalAlignment.Right;
+ sheet.Cells[tableRange.Start.Row, 1, tableRange.End.Row, 1].Style.Numberformat.Format = "yyyy-MM-dd";
+ sheet.Cells[tableRange.Start.Row, 2, tableRange.End.Row, 5].Style.Numberformat.Format = "#,##0.0000";
+ tableRange.AutoFitColumns();
+
+ var table = sheet.Tables.GetFromRange(tableRange);
+ table.ShowFirstColumn = true;
+ var chart = sheet.Drawings.AddLineChart("LineChart1", eLineChartType.Line);
+
+ var serie1 = chart.Series.Add(tableRange.TakeColumnsBetween(1,1).SkipRows(1), tableRange.TakeColumns(1).SkipRows(1));
+ serie1.HeaderAddress = sheet.Cells["B15"];
+
+ var serie2 = chart.Series.Add(tableRange.TakeColumnsBetween(2, 1).SkipRows(1), tableRange.TakeColumns(1).SkipRows(1));
+ serie2.HeaderAddress = sheet.Cells["C15"];
+
+ var serie3 = chart.Series.Add(tableRange.TakeColumnsBetween(3, 1).SkipRows(1), tableRange.TakeColumns(1).SkipRows(1));
+ serie3.HeaderAddress = sheet.Cells["D15"];
+
+ chart.SetPosition(0, 0);
+ chart.To.Row = 14;
+ chart.To.Column = 10;
+ chart.StyleManager.SetChartStyle(ePresetChartStyle.LineChartStyle5);
+
+ var exporter = sheet.Cells.CreateHtmlExporter();
+ var settings = exporter.Settings;
+ settings.Drawings.Include = eDrawingInclude.Include;
+ settings.Culture = CultureInfo.InvariantCulture;
+ settings.SetRowHeight = true;
+ settings.SetColumnWidth = true;
+ settings.TableId = "currency-table";
+ settings.AdditionalTableClassNames.Add("table");
+ settings.AdditionalTableClassNames.Add("table-sm");
+ settings.AdditionalTableClassNames.Add("table-borderless");
+ settings.Drawings.Position = eDrawingPosition.Absolute;
+ SaveWorkbook("HtmlExportWithLineChart.xlsx", package);
+ // export css and html
+ //var css = exporter.GetCssString();
+ //var html = exporter.GetHtmlString();
+ var html = await exporter.GetSinglePageAsync();
+
+ SaveSvg("HtmlExportWithLineChart.html", html);
+ }
+
+ }
//2.4-CreateAFileSystemReport.xlsx
//3.3-FxReportFromDatabase.xlsx
}
diff --git a/src/EPPlus.DrawingRenderer.Tests/Shape/ShapeWrappingTests.cs b/src/EPPlus.DrawingRenderer.Tests/Shape/ShapeWrappingTests.cs
new file mode 100644
index 0000000000..16c45c9749
--- /dev/null
+++ b/src/EPPlus.DrawingRenderer.Tests/Shape/ShapeWrappingTests.cs
@@ -0,0 +1,75 @@
+using OfficeOpenXml;
+using OfficeOpenXml.Drawing;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EPPlus.DrawingRenderer.Tests.Shape
+{
+ [TestClass]
+ public class ShapeWrappingTests : TestBase
+ {
+ [TestMethod]
+ public void WrapEveryLetter()
+ {
+ using(var p = OpenPackage("WrapEveryLetterSvg.xlsx",true))
+ {
+ var ws = p.Workbook.Worksheets.Add("wrapShapes");
+
+ var txtBox = ws.Drawings.AddTextbox("txtBox1", "MY WORLD");
+ txtBox.As.Shape.SetSize(30, 150);
+
+ var svg = txtBox.ToSvg();
+ File.WriteAllText(GetOutputFile("svg\\", "WrapEveryLetter.svg").FullName, svg);
+ SaveAndCleanup(p);
+ }
+ }
+
+ [TestMethod]
+ public void WrapAndColor()
+ {
+ using (var p = OpenPackage("WrapInRect.xlsx", true))
+ {
+ var ws = p.Workbook.Worksheets.Add("wrap");
+
+ var _currentShape = ws.Drawings.AddShape("MyShape", OfficeOpenXml.Drawing.eShapeStyle.Rect);
+ _currentShape.SetSize(36, 200);
+
+ _currentShape.Font.Color = System.Drawing.Color.Goldenrod;
+ _currentShape.TextBody.LeftInsert = 0;
+ _currentShape.TextBody.RightInsert = 0;
+ _currentShape.TextBody.TopInsert = 0;
+ _currentShape.TextBody.BottomInsert = 0;
+
+ var rt1 = _currentShape.RichText.Add("M", true);
+ var rt2 = _currentShape.RichText.Add("Y ", false);
+ var rt3 = _currentShape.RichText.Add("W", false);
+ var rt4 = _currentShape.RichText.Add("O", false);
+ var rt5 = _currentShape.RichText.Add("R", false);
+ var rt6 = _currentShape.RichText.Add("L", false);
+ var rt7 = _currentShape.RichText.Add("D", false);
+ _currentShape.RichText.Add("MY WORLD", true);
+
+ var startColor = KnownColor.Plum;
+
+ _currentShape.RichText.Add("Default world", true);
+
+ foreach (var item in _currentShape.RichText)
+ {
+ item.Color = System.Drawing.Color.FromKnownColor(startColor);
+ startColor += 1;
+ }
+
+ _currentShape.TextBody.Anchor = eTextAnchoringType.Top;
+
+ var svg = _currentShape.ToSvg();
+ File.WriteAllText(GetOutputFile("svg\\", "WrapAndColor.svg").FullName, svg);
+
+ SaveAndCleanup(p);
+ }
+ }
+ }
+}
diff --git a/src/EPPlus.DrawingRenderer.Tests/TestBase.cs b/src/EPPlus.DrawingRenderer.Tests/TestBase.cs
index 6c4f4dff6f..d22ac7a764 100644
--- a/src/EPPlus.DrawingRenderer.Tests/TestBase.cs
+++ b/src/EPPlus.DrawingRenderer.Tests/TestBase.cs
@@ -83,6 +83,7 @@ private class GeoData
protected static string _testInputPathOptional = @"c:\epplusTest\workbooks\"; //Team shared workbooks for tests
protected static string _testInputLocalPathOptional = @"c:\epplusTest\workbooks\"; //Local workboks for tests
protected static string _imagePath = @"c:\epplusTest\images\";
+ protected static string _dataPath = @"c:\epplusTest\data\";
///
///Gets or sets the test context which provides
///information about and functionality for the current test run.
diff --git a/src/EPPlus.DrawingRenderer.Tests/TextRenderTests.cs b/src/EPPlus.DrawingRenderer.Tests/TextRenderTests.cs
index d04da9f575..895f286aec 100644
--- a/src/EPPlus.DrawingRenderer.Tests/TextRenderTests.cs
+++ b/src/EPPlus.DrawingRenderer.Tests/TextRenderTests.cs
@@ -39,7 +39,7 @@
// var c = System.Drawing.Color.FromName(color);
// if (c.IsEmpty)
// {
-// var sc = Enum.Parse(color);
+// var sc = (eSchemeColor)Enum.Parse(typeof(eSchemeColor), color);
// fill.SolidFill.Color.SetSchemeColor(sc);
// }
// else
@@ -49,7 +49,7 @@
// }
// catch
// {
-// var sc = Enum.Parse(color);
+// var sc = (eSchemeColor)Enum.Parse(typeof(eSchemeColor), color);
// fill.SolidFill.Color.SetSchemeColor(sc);
// }
// }
@@ -76,13 +76,13 @@
// // fontSizes.Add(runFont.Size);
// // }
-
+
// // return new TextFragmentCollectionSimple(fonts, runContents);
// //}
// //List GetWrappedText(ExcelDrawingTextRunCollection runs, TextFragmentCollectionSimple fragments)
// //{
-
+
// // List fonts = new List();
// // for (int i = 0; i < runs.Count(); i++)
@@ -146,7 +146,7 @@
// Style = MeasurementFontStyles.Regular
// };
-// List fonts = new() { /*font1,*/ font2, font3, font4, font5, font6};
+// List fonts = new() { /*font1,*/ font2, font3, font4, font5, font6 };
// var maxSizePoints = Math.Round(300d, 0, MidpointRounding.AwayFromZero).PixelToPoint();
// var ttMeasurer = OpenTypeFonts.GetTextLayoutEngineForFont(font2);
@@ -224,14 +224,14 @@
// var txtRuns2 = tbItem.Paragraphs[1].Runs;
-// Assert.AreEqual(53.20963541666667d, txtRuns2[0].Bounds.Width.PointToPixel(),0.2);
+// Assert.AreEqual(53.20963541666667d, txtRuns2[0].Bounds.Width.PointToPixel(), 0.2);
// var currentLineWidth = txtRuns2[0].Bounds.Width.PointToPixel();
-// Assert.AreEqual(currentLineWidth, txtRuns2[1].Bounds.Left.PointToPixel(),0.2);
-// Assert.AreEqual(69.55924479166667d, txtRuns2[1].Bounds.Width.PointToPixel(),0.2);
+// Assert.AreEqual(currentLineWidth, txtRuns2[1].Bounds.Left.PointToPixel(), 0.2);
+// Assert.AreEqual(69.55924479166667d, txtRuns2[1].Bounds.Width.PointToPixel(), 0.2);
// currentLineWidth += txtRuns2[1].Bounds.Width.PointToPixel();
-// Assert.AreEqual(currentLineWidth, txtRuns2[2].Bounds.Left.PointToPixel(),0.0001);
+// Assert.AreEqual(currentLineWidth, txtRuns2[2].Bounds.Left.PointToPixel(), 0.0001);
// Assert.AreEqual(49.89388020833334, txtRuns2[2].Bounds.Width.PointToPixel(), 0.0001);
// currentLineWidth += txtRuns2[2].Bounds.Width.PointToPixel();
@@ -328,7 +328,7 @@
// //Appears off by 1-2 px bc of border width
-// Assert.AreEqual(190d, tbItem.Bounds.GlobalTop.PointToPixel() , 1.0);
+// Assert.AreEqual(190d, tbItem.Bounds.GlobalTop.PointToPixel(), 1.0);
// }
@@ -379,7 +379,7 @@
// //var trItem = new SvgTextRunItem(svgShape, lastpara.Bounds, font, "MyText");
// //lastpara.Runs.Add(trItem);
-// lastpara.AddOwnText(new TextFragment() {Text = "my new text", Font = font });
+// lastpara.AddOwnText(new TextFragment() { Text = "my new text", Font = font });
// svgShape.Render(sb);
// var str = sb.ToString();
diff --git a/src/EPPlus.DrawingRenderer/EPPlus.DrawingRenderer.csproj b/src/EPPlus.DrawingRenderer/EPPlus.DrawingRenderer.csproj
index a8ab2f7e66..5d22f79446 100644
--- a/src/EPPlus.DrawingRenderer/EPPlus.DrawingRenderer.csproj
+++ b/src/EPPlus.DrawingRenderer/EPPlus.DrawingRenderer.csproj
@@ -11,6 +11,7 @@
latest
True
EPPlus.DrawingRenderer.snk
+ false
diff --git a/src/EPPlus.DrawingRenderer/Svg/Core/SvgShapeRenderer.cs b/src/EPPlus.DrawingRenderer/Svg/Core/SvgShapeRenderer.cs
index 786a22ba98..f3f5e993c2 100644
--- a/src/EPPlus.DrawingRenderer/Svg/Core/SvgShapeRenderer.cs
+++ b/src/EPPlus.DrawingRenderer/Svg/Core/SvgShapeRenderer.cs
@@ -622,8 +622,46 @@ private string GetXy(RenderItem item, UserSpaceSettings userSpace, double? angle
double x1 = cx - halfX, y1 = cy - halfY;
double x2 = cx + halfX, y2 = cy + halfY;
+ if(item.DefId == "xGridLine")
+ {
+ //If global, has to stretch the whole length.
+ //This case is special because it stretches in the same direction as the attempted gradient
+ x1 = item.Bounds.Left;
+ x2 = w - x1;
+ }
+
return $" x1=\"{(x1).PointToPixelString("0.00")}\" x2=\"{(x2).PointToPixelString("0.00")}\" y1=\"{y1.PointToPixelString("0.00")}\" y2=\"{y2.PointToPixelString("0.00")}\"";
}
+ else if (angle.HasValue && angle != 0)
+ {
+ var x1 = 0D;
+ var x2 = 0D;
+ var y1 = 0D;
+ var y2 = 0D;
+ angle %= 360;
+ if (angle <= 90)
+ {
+ x2 = 1D - Math.Sin(MathHelper.Radians(angle.Value));
+ y2 = Math.Sin(MathHelper.Radians(angle.Value));
+ }
+ else if (angle <= 180)
+ {
+ y2 = Math.Sin(MathHelper.Radians(angle.Value));
+ x1 = 1D - Math.Sin(MathHelper.Radians(angle.Value));
+ }
+ else if (angle <= 270)
+ {
+ y1 = Math.Sin(MathHelper.Radians(angle.Value - 180));
+ x1 = 1D - Math.Sin(MathHelper.Radians(angle.Value - 180));
+ }
+ else
+ {
+ y1 = Math.Sin(MathHelper.Radians(angle.Value - 180));
+ x2 = 1D - Math.Sin(MathHelper.Radians(angle.Value - 180));
+ }
+
+ return $" x1=\"{(x1).ToString("0.00%", CultureInfo.InvariantCulture)}\" x2=\"{(x2).ToString("0.00%", CultureInfo.InvariantCulture)}\" y1=\"{y1.ToString("0.00%", CultureInfo.InvariantCulture)}\" y2=\"{y2.ToString("0.00%", CultureInfo.InvariantCulture)}\"";
+ }
return "";
}
diff --git a/src/EPPlus.Export.Pdf/DocumentObjects/Fonts/PdfFontDescriptor.cs b/src/EPPlus.Export.Pdf/DocumentObjects/Fonts/PdfFontDescriptor.cs
index a1833327b2..8111423791 100644
--- a/src/EPPlus.Export.Pdf/DocumentObjects/Fonts/PdfFontDescriptor.cs
+++ b/src/EPPlus.Export.Pdf/DocumentObjects/Fonts/PdfFontDescriptor.cs
@@ -10,6 +10,7 @@ Date Author Change
*************************************************************************************************
27/11/2025 EPPlus Software AB EPPlus 9
*************************************************************************************************/
+using EPPlus.Export.Pdf.Helpers;
using EPPlus.Graphics;
using System;
using System.IO;
@@ -69,7 +70,7 @@ internal override string RenderDictionary()
sb.AppendFormat($"<< /Type /FontDescriptor\n" +
$" /FontName /{fontName.Replace(" ", "")}\n" +
$" /Flags {flags}\n" +
- $" /FontBBox [{fontBBox.X} {fontBBox.Y} {fontBBox.Width} {fontBBox.Height}]\n" +
+ $" /FontBBox [{fontBBox.X.ToPdfStringF0()} {fontBBox.Y.ToPdfStringF0()} {fontBBox.Width.ToPdfStringF0()} {fontBBox.Height.ToPdfStringF0()}]\n" +
$" /Ascent {ascent}\n" +
$" /Descent {descent}\n" +
$" /CapHeight {capheight}\n" +
@@ -93,12 +94,12 @@ internal override void RenderDictionary(BinaryWriter bw)
sb.AppendFormat($"<< /Type /FontDescriptor\n" +
$" /FontName /{fontName.Replace(" ", "")}\n" +
$" /Flags {flags}\n" +
- $" /FontBBox [{fontBBox.X} {fontBBox.Y} {fontBBox.Width} {fontBBox.Height}]\n" +
- $" /Ascent {ascent}\n" +
- $" /Descent {descent}\n" +
- $" /CapHeight {capheight}\n" +
- $" /ItalicAngle {(int)italicAngle}\n" +
- $" /StemV {(int)stemV}");
+ $" /FontBBox [{fontBBox.X.ToPdfStringF0()} {fontBBox.Y.ToPdfStringF0()} {fontBBox.Width.ToPdfStringF0()} {fontBBox.Height.ToPdfStringF0()}]\n" +
+ $" /Ascent {ascent.ToPdfStringF0()}\n" +
+ $" /Descent {descent.ToPdfStringF0()}\n" +
+ $" /CapHeight {capheight.ToPdfStringF0()}\n" +
+ $" /ItalicAngle {((int)italicAngle).ToPdfStringF0()}\n" +
+ $" /StemV {((int)stemV).ToPdfStringF0()}");
if (FontFile2ObjectNumber > 0)
{
sb.AppendFormat($"\n /FontFile2 {FontFile2ObjectNumber} 0 R");
diff --git a/src/EPPlus.Export.Pdf/EPPlus.Export.Pdf.csproj b/src/EPPlus.Export.Pdf/EPPlus.Export.Pdf.csproj
index 87beb9760a..eb14a2dee8 100644
--- a/src/EPPlus.Export.Pdf/EPPlus.Export.Pdf.csproj
+++ b/src/EPPlus.Export.Pdf/EPPlus.Export.Pdf.csproj
@@ -7,7 +7,7 @@
false
true
EPPlus.Export.Pdf.snk
- latest
+ latest
diff --git a/src/EPPlus.Export.Pdf/Helpers/PdfString.cs b/src/EPPlus.Export.Pdf/Helpers/PdfString.cs
index d26aea06f6..6c49ff4d14 100644
--- a/src/EPPlus.Export.Pdf/Helpers/PdfString.cs
+++ b/src/EPPlus.Export.Pdf/Helpers/PdfString.cs
@@ -45,5 +45,14 @@ internal static string ToPdfStringF0(this double val)
{
return val.ToString("F0", CultureInfo.InvariantCulture);
}
+ ///
+ /// Returns the value formated for use in pdf document.
+ ///
+ /// Value to turn into a string.
+ /// The value repsented as a string with no decimals.
+ internal static string ToPdfStringF0(this int val)
+ {
+ return val.ToString("F0", CultureInfo.InvariantCulture);
+ }
}
}
diff --git a/src/EPPlus.Export.Pdf/Settings/PdfPageSettings.cs b/src/EPPlus.Export.Pdf/Settings/PdfPageSettings.cs
index 76287e5a12..fe20194ede 100644
--- a/src/EPPlus.Export.Pdf/Settings/PdfPageSettings.cs
+++ b/src/EPPlus.Export.Pdf/Settings/PdfPageSettings.cs
@@ -200,8 +200,8 @@ public PdfScaling Scaling
internal string defaultFontName = "";
//DEBUG
- internal bool Debug = false;
- internal bool PrintAsText = false;
+ internal bool Debug = true;
+ internal bool PrintAsText = true;
public PdfPageSettings(OpenTypeFontEngine fontEngine)
{
diff --git a/src/EPPlus.Fonts.OpenType/Integration/DataHolders/TextLineSimple.cs b/src/EPPlus.Fonts.OpenType/Integration/DataHolders/TextLineSimple.cs
index cb578c0e66..214963a2a0 100644
--- a/src/EPPlus.Fonts.OpenType/Integration/DataHolders/TextLineSimple.cs
+++ b/src/EPPlus.Fonts.OpenType/Integration/DataHolders/TextLineSimple.cs
@@ -57,7 +57,7 @@ public class TextLineSimple
public double GetWidthWithoutTrailingSpaces()
{
var trailingSpaceCount = 0;
-
+ if (string.IsNullOrEmpty(Text)) return 0D;
for (int i = Text.Count() - 1; i > 0; i--)
{
if (Text[i] != ' ')
diff --git a/src/EPPlus.Fonts.OpenType/Integration/RichText/LayoutSystem.cs b/src/EPPlus.Fonts.OpenType/Integration/RichText/LayoutSystem.cs
index ce14476ae4..df5e09b552 100644
--- a/src/EPPlus.Fonts.OpenType/Integration/RichText/LayoutSystem.cs
+++ b/src/EPPlus.Fonts.OpenType/Integration/RichText/LayoutSystem.cs
@@ -261,7 +261,7 @@ public TextLineCollection Wrap(double maxWidth)
for (int i = 1; i < wrappedLines.Count-1; i++)
{
var startIdx = wrappedLines[i].InternalLineFragments[0].StartOriginal;
- var len = wrappedLines[i].Text.Length;
+ var len = wrappedLines[i].Text?.Length??0;
for(int j = startIdx; j< (startIdx + len); j++)
{
AllChars[j].Line = i;
diff --git a/src/EPPlus.Interfaces/EPPlus.Interfaces.csproj b/src/EPPlus.Interfaces/EPPlus.Interfaces.csproj
index 6ae5284ef3..346b81e4e2 100644
--- a/src/EPPlus.Interfaces/EPPlus.Interfaces.csproj
+++ b/src/EPPlus.Interfaces/EPPlus.Interfaces.csproj
@@ -1,9 +1,9 @@
net10.0;net9.0;net8.0;netstandard2.1;netstandard2.0;net462
- 8.6.2.0
- 8.6.2.0
- 8.6.2
+ 9.0.0.0
+ 9.0.0.0
+ 9.0.0-preview
true
license.md
git
diff --git a/src/EPPlus.System.Drawing/EPPlus.System.Drawing.csproj b/src/EPPlus.System.Drawing/EPPlus.System.Drawing.csproj
index 2560da378d..356ea70064 100644
--- a/src/EPPlus.System.Drawing/EPPlus.System.Drawing.csproj
+++ b/src/EPPlus.System.Drawing/EPPlus.System.Drawing.csproj
@@ -2,9 +2,9 @@
net10.0;net9.0;net8.0;netstandard2.1;netstandard2.0;net462
- 8.6.2.0
- 8.6.2.0
- 8.6.2
+ 9.0.0.0
+ 9.0.0.0
+ 9.0.0-preview
true
license.md
true
diff --git a/src/EPPlus/Core/AutofitHelper.cs b/src/EPPlus/Core/AutofitHelper.cs
index e1db3e54a0..52a271741d 100644
--- a/src/EPPlus/Core/AutofitHelper.cs
+++ b/src/EPPlus/Core/AutofitHelper.cs
@@ -28,7 +28,7 @@ namespace OfficeOpenXml.Core
internal class AutofitHelper
{
// Approximate width in pixels (at 96 DPI) of the autofilter dropdown arrow rendered by Excel.
- private const double AutoFilterArrowWidthPixels = 15d;
+ private const double AutoFilterArrowWidthPixels = 19d;
private ExcelRangeBase _range;
ITextMeasurer _genericMeasurer = new GenericFontMetricsTextMeasurer();
MeasurementFont _nonExistingFont = new MeasurementFont() { FontFamily = FontSize.NonExistingFont };
diff --git a/src/EPPlus/Drawing/Chart/ExcelChart.cs b/src/EPPlus/Drawing/Chart/ExcelChart.cs
index a0a94f4968..1990f59f39 100644
--- a/src/EPPlus/Drawing/Chart/ExcelChart.cs
+++ b/src/EPPlus/Drawing/Chart/ExcelChart.cs
@@ -73,6 +73,23 @@ private void Init(ExcelDrawings drawings, XmlDocument chartXml)
#endregion
internal ExcelChartStyleManager _styleManager = null;
+ internal readonly static HashSet _svgSupportedChartTypes = new HashSet()
+ {
+ eChartType.Line,
+ eChartType.LineMarkers,
+ eChartType.LineMarkersStacked,
+ eChartType.LineStacked,
+ eChartType.LineStacked100,
+ eChartType.LineMarkersStacked100,
+ eChartType.ColumnClustered,
+ eChartType.ColumnStacked,
+ eChartType.ColumnStacked100,
+ eChartType.BarClustered,
+ eChartType.BarStacked,
+ eChartType.BarStacked100,
+ eChartType.Pie,
+ eChartType.PieExploded,
+ };
///
/// Manage style settings for the chart
///
@@ -1240,5 +1257,9 @@ internal override void SaveDrawing(bool hasLoadedPivotTables)
cs.Drawings.DrawingXml.Save(xrd);
}
}
+ ///
+ /// Returns true if the chart supports svg export via the .
+ ///
+ public override bool SupportsSvgExport => _svgSupportedChartTypes.Contains(ChartType);
}
}
diff --git a/src/EPPlus/Drawing/Chart/ExcelChartAxis.cs b/src/EPPlus/Drawing/Chart/ExcelChartAxis.cs
index e5fec2f806..89c895e1e2 100644
--- a/src/EPPlus/Drawing/Chart/ExcelChartAxis.cs
+++ b/src/EPPlus/Drawing/Chart/ExcelChartAxis.cs
@@ -132,6 +132,9 @@ public string Format
}
}
}
+ ///
+ /// Returns the number format code for the axis. If the axis has no format code, it will return the format code of the first series in the chart that uses this axis. If no series has a format code, it will return an empty string.
+ ///
public string FormatOrFirstValueFormat
{
get
@@ -144,19 +147,11 @@ public string FormatOrFirstValueFormat
{
if(ct.XAxis.Id == Id)
{
- foreach (var serie in ct.Series)
- {
- if(string.IsNullOrEmpty(serie.XSeries))
- {
- continue;
- }
- var adr = new ExcelAddressBase(serie.XSeries);
- var ws = wb.Worksheets[adr.WorkSheetName];
- if(ws!=null)
- {
- return ws.Cells[adr.Address].Style.Numberformat.Format;
- }
- }
+ return GetFormatFromSeries(ct, true);
+ }
+ else if (ct.YAxis.Id == Id)
+ {
+ return GetFormatFromSeries(ct, false);
}
}
return "";
@@ -165,6 +160,26 @@ public string FormatOrFirstValueFormat
return f;
}
}
+
+ private string GetFormatFromSeries(ExcelChart ct, bool isXAxis)
+ {
+ foreach (var serie in ct.Series)
+ {
+ var address = isXAxis ? serie.XSeries : serie.Series;
+ if (string.IsNullOrEmpty(address))
+ {
+ continue;
+ }
+ var adr = new ExcelAddressBase(address);
+ var ws = _chart.WorkSheet.Workbook.Worksheets[adr.WorkSheetName];
+ if (ws != null)
+ {
+ return ws.Cells[adr.Address].FirstOrDefault(x=>string.IsNullOrEmpty(x.Style.Numberformat.Format)==false).Style.Numberformat.Format ?? "";
+ }
+ }
+ return "";
+ }
+
///
/// The Numberformats are linked to the source data.
///
diff --git a/src/EPPlus/Drawing/Chart/ExcelChartAxisStandard.cs b/src/EPPlus/Drawing/Chart/ExcelChartAxisStandard.cs
index 67345c5f20..7de2abf586 100644
--- a/src/EPPlus/Drawing/Chart/ExcelChartAxisStandard.cs
+++ b/src/EPPlus/Drawing/Chart/ExcelChartAxisStandard.cs
@@ -15,6 +15,7 @@ Date Author Change
using OfficeOpenXml.FormulaParsing.Excel.Functions.Information;
using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions;
using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;
+using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup.Sorting;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Text;
using OfficeOpenXml.FormulaParsing.Utilities;
using OfficeOpenXml.Style.XmlAccess;
@@ -919,7 +920,7 @@ internal override List
public new ExcelChartSeries Series { get; } = new ExcelChartSeries();
-
}
}
diff --git a/src/EPPlus/Drawing/Chart/Style/ExcelChartStyleReference.cs b/src/EPPlus/Drawing/Chart/Style/ExcelChartStyleReference.cs
index 194869e0f3..6f90f72312 100644
--- a/src/EPPlus/Drawing/Chart/Style/ExcelChartStyleReference.cs
+++ b/src/EPPlus/Drawing/Chart/Style/ExcelChartStyleReference.cs
@@ -10,6 +10,7 @@ Date Author Change
*************************************************************************************************
01/27/2020 EPPlus Software AB Initial release EPPlus 5
*************************************************************************************************/
+using OfficeOpenXml.Drawing.Shape.Style;
using System;
using System.Globalization;
using System.Xml;
@@ -19,29 +20,14 @@ namespace OfficeOpenXml.Drawing.Chart.Style
///
/// A reference from a chart style to the theme collection
///
- public class ExcelChartStyleReference : XmlHelper
+ public class ExcelChartStyleReference : ShapeStyleReference
{
string _path;
- internal ExcelChartStyleReference(XmlNamespaceManager nsm, XmlNode topNode, string path) : base(nsm, topNode)
+ internal ExcelChartStyleReference(XmlNamespaceManager nsm, XmlNode topNode, string path) : base(nsm, topNode, path)
{
_path = path;
}
- ///
- /// The index to the theme style matrix.
- ///
- ///
- public int Index
- {
- get
- {
- return GetXmlNodeInt($"{_path}/@idx");
- }
- set
- {
- if (value < 0) throw new ArgumentOutOfRangeException("Index", "Can't be negative");
- SetXmlNodeString($"{_path}/@idx", value.ToString(CultureInfo.InvariantCulture));
- }
- }
+
ExcelChartStyleColorManager _color = null;
///
/// The color to be used for the reference.
@@ -59,16 +45,5 @@ public ExcelChartStyleColorManager Color
return _color;
}
}
- ///
- /// If the reference has a color
- ///
- public bool HasColor
- {
- get
- {
- var node = GetNode(_path);
- return node!=null && node.HasChildNodes;
- }
- }
}
}
\ No newline at end of file
diff --git a/src/EPPlus/Drawing/ExcelDrawing.cs b/src/EPPlus/Drawing/ExcelDrawing.cs
index 7ba71d3f5f..1b5d38fa86 100644
--- a/src/EPPlus/Drawing/ExcelDrawing.cs
+++ b/src/EPPlus/Drawing/ExcelDrawing.cs
@@ -20,6 +20,7 @@ Date Author Change
using OfficeOpenXml.Drawing.Controls;
using OfficeOpenXml.Drawing.OleObject;
using OfficeOpenXml.Drawing.Slicer;
+using OfficeOpenXml.Export.HtmlExport;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Text;
using OfficeOpenXml.Packaging;
using OfficeOpenXml.Utils.Drawings;
@@ -1764,7 +1765,11 @@ public ExcelGroupShape ParentGroup
}
internal ExcelDrawingCustomGeometry CustomGeom { get; private set; }
-
+ ///
+ /// Returns true if the drawing supports svg export via the .
+ ///
+ public virtual bool SupportsSvgExport { get => false; }
+ internal eDrawingInclude? IncludeInHtmlExport { get; set; } = eDrawingInclude.Include;
internal virtual void DeleteMe()
{
TopNode.ParentNode.RemoveChild(TopNode);
@@ -2595,6 +2600,7 @@ internal virtual void SaveDrawing(bool hasLoadedPivotTables)
///
/// Converts the drawing to a SVG image.
/// This is currently only supported for shapes, line-, column-, bar- and pie- charts.
+ /// Please use to verify svg export is supported.
///
/// The svg image.
/// If the drawing type is not supported
diff --git a/src/EPPlus/Drawing/ExcelPicture.cs b/src/EPPlus/Drawing/ExcelPicture.cs
index aeecc7c3b0..aeee3e88ac 100644
--- a/src/EPPlus/Drawing/ExcelPicture.cs
+++ b/src/EPPlus/Drawing/ExcelPicture.cs
@@ -628,5 +628,9 @@ public bool VerticalFlip
internal PictureLocation LocationType = PictureLocation.None;
internal ZipPackageRelationship LinkedImageRel = null;
+ ///
+ /// Returns true if the drawing supports svg export via the .
+ ///
+ public override bool SupportsSvgExport => true;
}
}
\ No newline at end of file
diff --git a/src/EPPlus/Drawing/ExcelShape.cs b/src/EPPlus/Drawing/ExcelShape.cs
index 7af5c06558..6467461daa 100644
--- a/src/EPPlus/Drawing/ExcelShape.cs
+++ b/src/EPPlus/Drawing/ExcelShape.cs
@@ -104,6 +104,9 @@ public string ToSvg(SvgRenderOptions options)
svg.Render(sr.RenderItems);
return sb.ToString();
}
-
+ ///
+ /// Returns true if the drawing supports svg export via the .
+ ///
+ public override bool SupportsSvgExport => true;
}
}
diff --git a/src/EPPlus/Drawing/Renderer/Chart/Axis/DateAxisScaleCalculator.cs b/src/EPPlus/Drawing/Renderer/Chart/Axis/DateAxisScaleCalculator.cs
index 30551fb198..e0e3c9802f 100644
--- a/src/EPPlus/Drawing/Renderer/Chart/Axis/DateAxisScaleCalculator.cs
+++ b/src/EPPlus/Drawing/Renderer/Chart/Axis/DateAxisScaleCalculator.cs
@@ -36,7 +36,8 @@ internal static AxisScale Calculate(double dataMin, double dataMax, AxisOptions
Max = axisMax,
MajorInterval = majorValue,
MajorDateUnit = majorUnit,
- MinorDateUnit = majorUnit
+ MinorDateUnit = majorUnit,
+ TextOrientation = eTextOrientation.Horizontal
};
}
// Excel's date serial epoch: Dec 30, 1899
@@ -315,46 +316,89 @@ internal static AxisScale CalculateByWidthAllowDiagonal(List
-
-
-
-
-
-
-
-
-
-
-
-
@@ -794,7 +798,7 @@
-
+
@@ -841,7 +845,7 @@
- Never
+ Never
@@ -869,10 +873,10 @@
Never
- PreserveNewest
+ PreserveNewest
PreserveNewest
-
+
\ No newline at end of file
diff --git a/src/EPPlus/ExcelHeaderFooter.cs b/src/EPPlus/ExcelHeaderFooter.cs
index c5e349a7a8..a7ac0f6cda 100644
--- a/src/EPPlus/ExcelHeaderFooter.cs
+++ b/src/EPPlus/ExcelHeaderFooter.cs
@@ -65,14 +65,14 @@ internal ExcelHeaderFooterText(XmlNode TextNode, ExcelWorksheet ws, string hf)
string text = TextNode.InnerText;
string code = text.Substring(0, 2);
int startPos = 2;
- for (int pos = startPos; pos < text.Length - 2; pos++)
+ for (int pos = startPos; pos < text.Length - 1; pos++)
{
string newCode = text.Substring(pos, 2);
- if (newCode == "&C" || newCode == "&R")
+ if (newCode == "&L" || newCode == "&C" || newCode == "&R")
{
SetText(code, text.Substring(startPos, pos - startPos));
startPos = pos + 2;
- pos = startPos;
+ pos = startPos - 1;
code = newCode;
}
}
diff --git a/src/EPPlus/ExcelRange.cs b/src/EPPlus/ExcelRange.cs
index f253b1d5ca..d5676cb4fb 100644
--- a/src/EPPlus/ExcelRange.cs
+++ b/src/EPPlus/ExcelRange.cs
@@ -187,5 +187,6 @@ public void SetFormula(string formula, bool asSharedFormula = true)
}
}
}
+
}
}
diff --git a/src/EPPlus/ExcelRangeBase.cs b/src/EPPlus/ExcelRangeBase.cs
index 5e55aebba4..ecd4ffa0b6 100644
--- a/src/EPPlus/ExcelRangeBase.cs
+++ b/src/EPPlus/ExcelRangeBase.cs
@@ -35,6 +35,7 @@ Date Author Change
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -774,7 +775,7 @@ internal ExcelAddressBase GetAddressDimension()
{
GetAddressDimensionFullRowAndColumn(out int dimFromRow, out int dimFromCol, out int dimToRow, out int dimToCol);
//If the range is only full column or full row the dimension of the worksheet, return null.
- if (dimFromCol==0 || dimFromRow>dimToCol || dimFromCol > dimToCol)
+ if (dimFromCol==0 || dimFromRow>dimToRow || dimFromCol > dimToCol)
{
return null;
}
@@ -2943,5 +2944,13 @@ public bool UseImplicitItersection
}
}
}
+ internal ExcelTable GetIntersectingTable()
+ {
+ if (_worksheet == null)
+ {
+ return null;
+ }
+ return _worksheet.Tables.GetIntersectingRanges(this).Select(x => x.Value).FirstOrDefault();
+ }
}
}
diff --git a/src/EPPlus/Export/HtmlExport/CssCollections/CssTableRuleCollection.cs b/src/EPPlus/Export/HtmlExport/CssCollections/CssTableRuleCollection.cs
index 762981b769..334aa31644 100644
--- a/src/EPPlus/Export/HtmlExport/CssCollections/CssTableRuleCollection.cs
+++ b/src/EPPlus/Export/HtmlExport/CssCollections/CssTableRuleCollection.cs
@@ -19,6 +19,8 @@ Date Author Change
using OfficeOpenXml.Style.Table;
using OfficeOpenXml.Table;
using System.Collections.Generic;
+using System.Data;
+using System.Text;
using static OfficeOpenXml.Export.HtmlExport.ColumnDataTypeManager;
namespace OfficeOpenXml.Export.HtmlExport.CssCollections
@@ -104,13 +106,22 @@ internal void AddAlignment(string name, List dataTypes)
}
}
- internal void AddToCollection(string name, ExcelTableStyleElement element, string htmlElement)
+ internal void AddToCollection(string name, ExcelTableStyleElement element, string htmlElement, bool isRangeName=false)
{
if (element.Style.HasValue == false) return; //Dont add empty elements
var s = element.Style;
- var styleClass = new CssRule($"table.{name}{htmlElement}",int.MaxValue);
+ string rule;
+ if (isRangeName)
+ {
+ rule = $".{name}";
+ }
+ else
+ {
+ rule = $"table.{name}{htmlElement}";
+ }
+ var styleClass = new CssRule(rule,int.MaxValue);
var translators = new List();
@@ -202,7 +213,45 @@ internal void AddTableToCollection(ExcelTable table, List datatypes, str
var tableClassFC = $"{tableClass}-first-column";
AddToCollection($"{tableClassFC}", tblStyle.FirstColumn, " tbody tr td:first-child");
}
+ internal void AddRangeTableToCollection(ExcelTable table, List datatypes, string tableClassPreset)
+ {
+ var tblStyle = table.GetTableNamedStyle();
+
+ var tableClass = HtmlExportTableUtil.GetTableBaseClassName(table, true);
+
+ //AddHyperlink($"{tableClass}-default", tblStyle.WholeTable);
+ //AddAlignment($"{tableClass}", datatypes);
+
+ AddToCollection($"{tableClass}-default", tblStyle.WholeTable,"", true);
+ // AddToCollectionVH($"{tableClass}", tblStyle.WholeTable, "");
+
+ //Header
+ AddToCollection($"{tableClass}-header", tblStyle.HeaderRow, "", true);
+ //AddToCollectionVH($"{tableClass}", tblStyle.HeaderRow, "");
+ AddToCollection($"{tableClass}-last-header-cell", tblStyle.LastHeaderCell, "", true);
+ AddToCollection($"{tableClass}-first-header-cell", tblStyle.FirstHeaderCell, "", true);
+
+ //Total
+ AddToCollection($"{tableClass}-total", tblStyle.TotalRow, "", true);
+ //AddToCollectionVH($"{tableClass}", tblStyle.TotalRow, "");
+ AddToCollection($"{tableClass}-last-total-cell", tblStyle.LastTotalCell, "", true);
+ AddToCollection($"{tableClass}-first-total-cell", tblStyle.FirstTotalCell, "", true);
+
+ //Columns stripes
+ AddToCollection($"{tableClass}-first-col-stripe", tblStyle.FirstColumnStripe, "", true);
+ AddToCollection($"{tableClass}-second-col-stripe", tblStyle.SecondColumnStripe, "", true);
+
+ //Row stripes
+ AddToCollection($"{tableClass}-first-row-stripe", tblStyle.FirstRowStripe, "", true);
+ AddToCollection($"{tableClass}-second-row-stripe", tblStyle.SecondRowStripe, "", true);
+
+ //Last column
+ AddToCollection($"{tableClass}-last-col", tblStyle.LastColumn, "", true);
+
+ //First column
+ AddToCollection($"{tableClass}-first-col", tblStyle.FirstColumn, "", true);
+ }
internal void AddOtherCollectionToThisCollection(CssRuleCollection otherCollection)
{
foreach (var otherRule in otherCollection)
diff --git a/src/EPPlus/Export/HtmlExport/Enums/eDrawingInclude.cs b/src/EPPlus/Export/HtmlExport/Enums/eDrawingInclude.cs
index d092f66c83..a27c8617ae 100644
--- a/src/EPPlus/Export/HtmlExport/Enums/eDrawingInclude.cs
+++ b/src/EPPlus/Export/HtmlExport/Enums/eDrawingInclude.cs
@@ -6,28 +6,25 @@
namespace OfficeOpenXml.Export.HtmlExport
{
///
- /// What drawings to include in html export
+ /// How to include picture drawings in the html
///
- [Flags]
public enum eDrawingInclude
{
///
- /// Include no drawings
+ /// Do not include supported drawing objects in the html export. Default
///
- None = 0,
+ Exclude,
///
- /// Include Shapes
+ /// Include in css only, so they drawing images can be added manually.
///
- Shapes = 2,
+ IncludeInCssOnly,
///
- /// Include Charts
+ /// Include the drawings as images in the html export.
///
- Charts = 4,
-
- //TODO: This is already handled by image enum. We may need restructure here
+ Include,
///
- /// Include Images ?
+ /// Include the drawings as images in the HTML only .
///
- Images = 8,
+ IncludeInHtmlOnly
}
}
diff --git a/src/EPPlus/Export/HtmlExport/Enums/ePictureInclude.cs b/src/EPPlus/Export/HtmlExport/Enums/ePictureInclude.cs
index b5e8f7bb97..171532fa04 100644
--- a/src/EPPlus/Export/HtmlExport/Enums/ePictureInclude.cs
+++ b/src/EPPlus/Export/HtmlExport/Enums/ePictureInclude.cs
@@ -11,11 +11,15 @@ Date Author Change
05/11/2021 EPPlus Software AB ExcelTable Html Export
*************************************************************************************************/
+using System;
+
namespace OfficeOpenXml.Export.HtmlExport
{
///
- /// How to include picture drawings in the html
+ /// Obsolete: How to include picture drawings in the html
+ ///
///
+ [Obsolete("Use general Drawings.Include property to set drawing behaviour.")]
public enum ePictureInclude
{
///
diff --git a/src/EPPlus/Export/HtmlExport/Enums/ePicturePosition.cs b/src/EPPlus/Export/HtmlExport/Enums/ePicturePosition.cs
index 4252a49e49..979d16983a 100644
--- a/src/EPPlus/Export/HtmlExport/Enums/ePicturePosition.cs
+++ b/src/EPPlus/Export/HtmlExport/Enums/ePicturePosition.cs
@@ -11,11 +11,14 @@ Date Author Change
05/11/2021 EPPlus Software AB ExcelTable Html Export
*************************************************************************************************/
+using System;
+
namespace OfficeOpenXml.Export.HtmlExport
{
///
/// If the Blip is absolut or relative to the table cell
///
+ [Obsolete("Use eDrawingPosition on the Drawing.Position property instead.")]
public enum ePicturePosition
{
///
@@ -31,4 +34,23 @@ public enum ePicturePosition
///
Relative
}
+ ///
+ /// If the drawing image is absolut or relative to the table cell
+ ///
+ public enum eDrawingPosition
+ {
+ ///
+ /// No CSS is added for Position
+ ///
+ DontSet,
+ ///
+ /// Position is Absolute in the CSS
+ ///
+ Absolute,
+ ///
+ /// Position is Relative in the CSS
+ ///
+ Relative
+ }
+
}
diff --git a/src/EPPlus/Export/HtmlExport/Exporters/ExcelHtmlExporterBase.cs b/src/EPPlus/Export/HtmlExport/Exporters/ExcelHtmlExporterBase.cs
index ced1a36c9f..8297c38a79 100644
--- a/src/EPPlus/Export/HtmlExport/Exporters/ExcelHtmlExporterBase.cs
+++ b/src/EPPlus/Export/HtmlExport/Exporters/ExcelHtmlExporterBase.cs
@@ -15,6 +15,7 @@ Date Author Change
using System.Linq;
using System.Text;
using OfficeOpenXml.Core;
+using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;
namespace OfficeOpenXml.Export.HtmlExport.Exporters
{
@@ -73,14 +74,7 @@ public EPPlusReadOnlyList Ranges
private void AddRange(ExcelRangeBase range)
{
- if (range.IsFullColumn && range.IsFullRow)
- {
- _ranges.Add(new ExcelRangeBase(range.Worksheet, range.Worksheet.Dimension.Address));
- }
- else
- {
- _ranges.Add(range);
- }
+ _ranges.Add(range);
}
}
}
diff --git a/src/EPPlus/Export/HtmlExport/Exporters/Internal/AbstractRangeExporter.cs b/src/EPPlus/Export/HtmlExport/Exporters/Internal/AbstractRangeExporter.cs
index ca8d63f8bf..283ad78e9f 100644
--- a/src/EPPlus/Export/HtmlExport/Exporters/Internal/AbstractRangeExporter.cs
+++ b/src/EPPlus/Export/HtmlExport/Exporters/Internal/AbstractRangeExporter.cs
@@ -15,6 +15,7 @@ Date Author Change
using OfficeOpenXml.FormulaParsing.Excel.Functions.Text;
using OfficeOpenXml.Table;
using OfficeOpenXml.Utils.String;
+using System;
using System.Collections.Generic;
using System.Linq;
@@ -29,6 +30,7 @@ public AbstractHtmlExporter()
internal const string TableClass = "epplus-table";
internal List _rangePictures = null;
internal List _rangeDrawings = null;
+ protected bool _hasIntersectingTables = false; //Intersecting tables;
protected List _dataTypes = new List();
protected ExporterContext _exporterContext;
@@ -55,11 +57,17 @@ internal void LoadRangeDrawings(List ranges)
}
_rangePictures = new List();
_rangeDrawings = new List();
+ var processedDrawings = new HashSet();
//Render in-cell images.
- foreach (var worksheet in ranges.Select(x => x.Worksheet).Distinct())
+ foreach (var range in ranges)
{
+ var worksheet = range.Worksheet;
foreach (var d in worksheet.Drawings)
{
+ if (processedDrawings.Contains(d)) continue;
+ processedDrawings.Add(d);
+ var drawingAddress = d.GetAddress();
+ if (drawingAddress.Collide(range) == ExcelAddressBase.eAddressCollition.No) continue;
if (d is ExcelPicture p)
{
p.GetFromBounds(out int fromRow, out int fromRowOff, out int fromCol, out int fromColOff);
@@ -79,26 +87,7 @@ internal void LoadRangeDrawings(List ranges)
ToColumnOff = toColOff
});
}
- else if(d is ExcelShape s)
- {
- s.GetFromBounds(out int fromRow, out int fromRowOff, out int fromCol, out int fromColOff);
- s.GetToBounds(out int toRow, out int toRowOff, out int toCol, out int toColOff);
-
- _rangeDrawings.Add(new HtmlSvgDrawing()
- {
- WorksheetId = worksheet.PositionId,
- Drawing = s,
- FromRow = fromRow,
- FromRowOff = fromRowOff,
- FromColumn = fromCol,
- FromColumnOff = fromColOff,
- ToRow = toRow,
- ToRowOff = toRowOff,
- ToColumn = toCol,
- ToColumnOff = toColOff
- });
- }
- else if(d is ExcelChart)
+ else if(d.SupportsSvgExport && (d is ExcelShape || d is ExcelChart))
{
d.GetFromBounds(out int fromRow, out int fromRowOff, out int fromCol, out int fromColOff);
d.GetToBounds(out int toRow, out int toRowOff, out int toCol, out int toColOff);
@@ -168,5 +157,71 @@ protected HtmlSvgDrawing GetDrawing(int worksheetId, int row, int col)
}
return null;
}
+ ///
+ /// Adjust all drawings for the worksheets dimension and include any draings that are outside the dimension.
+ ///
+ ///
+ ///
+ protected void AdjustRangeForDimensionAndDrawings(List ranges, bool includeDrawings)
+ {
+ for(int i=0;i drawMinRow)
+ {
+ if (toRowOff > 0) toRow++;
+ if (toColOff > 0) toCol++;
+ }
+
+ if (range.Collide(fromRow, fromCol, toRow, toCol) != ExcelAddressBase.eAddressCollition.Inside)
+ {
+ if (fromRow < drawMinRow) drawMinRow = fromRow;
+ if (fromCol < drawMinCol) drawMinCol = fromCol;
+ if (toRow > drawMaxRow) drawMaxRow = toRow;
+ if (toCol > drawMaxCol) drawMaxCol = toCol;
+ }
+ }
+ }
+
+ if (newRange != null &&
+ newRange._fromRow > drawMinRow ||
+ newRange._fromCol > drawMinCol ||
+ newRange._toRow > drawMinRow ||
+ newRange._toCol > drawMinCol)
+ {
+ return range.Worksheet.Cells[drawMinRow < newRange._fromRow ? Math.Max(drawMinRow, range._fromRow) : newRange._fromRow,
+ drawMinCol < newRange._fromCol ? Math.Max(drawMinCol, range._fromCol) : newRange._fromCol,
+ drawMaxRow > newRange._toRow ? Math.Min(drawMaxRow, range._toRow) : newRange._toRow,
+ drawMaxCol > newRange._toCol ? Math.Min(drawMaxCol, range._toCol) : newRange._toCol];
+ }
+ }
+ return range.Worksheet.Cells[newRange.Address];
+ }
}
}
diff --git a/src/EPPlus/Export/HtmlExport/Exporters/Internal/CssExporterBase.cs b/src/EPPlus/Export/HtmlExport/Exporters/Internal/CssExporterBase.cs
index 74dada4db1..e472642575 100644
--- a/src/EPPlus/Export/HtmlExport/Exporters/Internal/CssExporterBase.cs
+++ b/src/EPPlus/Export/HtmlExport/Exporters/Internal/CssExporterBase.cs
@@ -51,13 +51,13 @@ public CssExporterBase(HtmlExportSettings settings, ExcelRangeBase range)
if (range.Addresses == null)
{
- AddRange(range);
+ AddRange(range, settings.Drawings.Include!=eDrawingInclude.Exclude);
}
else
{
foreach (var address in range.Addresses)
{
- AddRange(range.Worksheet.Cells[address.Address]);
+ AddRange(range.Worksheet.Cells[address.Address], settings.Drawings.Include != eDrawingInclude.Exclude);
}
}
}
@@ -66,6 +66,7 @@ public CssExporterBase(HtmlRangeExportSettings settings, EPPlusReadOnlyList _ranges = new EPPlusReadOnlyList();
internal const string TableStyleClassPrefix = "ts-";
- private void AddRange(ExcelRangeBase range)
+ private void AddRange(ExcelRangeBase range, bool includeDrawings)
{
if (range.IsFullColumn && range.IsFullRow)
{
- _ranges.Add(new ExcelRangeBase(range.Worksheet, range.Worksheet.Dimension.Address));
+ _ranges.Add(AdjustRangeForDimensionAndDrawings(range, includeDrawings));
}
else
{
- _ranges.Add(range);
+ _ranges.Add(range);
}
}
@@ -134,10 +135,30 @@ protected void AddCssRulesToCollection(CssRangeRuleCollection cssTranslator, Htm
);
addedTableStyles.Add(table.TableStyle);
}
+ else
+ {
+ var tables = range.Worksheet.Tables.GetIntersectingRanges(range).Select(x=>x.Value).ToList();
+ if(tables.Count>0)
+ {
+ if (tableSettings == null)
+ {
+ tableSettings = new HtmlTableExportSettings() { Minify = Settings.Minify };
+ }
+
+ foreach (var t in tables)
+ {
+ cssTranslator.AddOtherCollectionToThisCollection
+ (
+ CreateRangeTableCssRules(t, tableSettings, _dataTypes).RuleCollection
+ );
+ addedTableStyles.Add(t.TableStyle);
+ }
+ }
+ }
}
}
- if (Settings.Pictures.Include == ePictureInclude.Include || Settings.Pictures.Include == ePictureInclude.IncludeInCssOnly)
+ if (Settings.Drawings.Include == eDrawingInclude.Include || Settings.Drawings.Include == eDrawingInclude.IncludeInCssOnly)
{
LoadRangeDrawings(_ranges._list);
foreach (var p in _rangePictures)
@@ -146,7 +167,7 @@ protected void AddCssRulesToCollection(CssRangeRuleCollection cssTranslator, Htm
}
}
- if(Settings.Drawings.Include == ePictureInclude.Include || Settings.Drawings.Include == ePictureInclude.IncludeInCssOnly)
+ if(Settings.Drawings.Include == eDrawingInclude.Include || Settings.Drawings.Include == eDrawingInclude.IncludeInCssOnly)
{
LoadRangeDrawings(_ranges._list);
foreach(var d in _rangeDrawings)
@@ -308,6 +329,14 @@ internal static CssTableRuleCollection CreateTableCssRules(ExcelTable table, Htm
return tableRules;
}
+ internal static CssTableRuleCollection CreateRangeTableCssRules(ExcelTable table, HtmlTableExportSettings settings, List datatypes)
+ {
+ var tableRules = new CssTableRuleCollection(table, settings);
+ var tableClass = HtmlExportTableUtil.GetTableBaseClassName(table, true);
+ tableRules.AddRangeTableToCollection(table, datatypes, tableClass);
+
+ return tableRules;
+ }
internal CssWriter GetTableCssWriter(Stream stream, ExcelTable table, HtmlTableExportSettings tableSettings)
{
diff --git a/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlExportTableUtil.cs b/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlExportTableUtil.cs
index e6cf4ec662..95e04416ed 100644
--- a/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlExportTableUtil.cs
+++ b/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlExportTableUtil.cs
@@ -12,9 +12,14 @@ Date Author Change
*************************************************************************************************/
using OfficeOpenXml.Export.HtmlExport.HtmlCollections;
using OfficeOpenXml.Export.HtmlExport.Settings;
+using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions;
using OfficeOpenXml.Table;
+using System;
+
+
#if !NET35 && !NET40
using System.Threading.Tasks;
+using static OfficeOpenXml.RichData.Structures.Constants.SpecialKeyNames;
#endif
namespace OfficeOpenXml.Export.HtmlExport.Exporters.Internal
@@ -68,15 +73,7 @@ internal static string GetWorksheetClassName(string styleClassPrefix, string nam
internal static string GetTableClasses(ExcelTable table)
{
- string styleClass;
- if (table.TableStyle == TableStyles.Custom)
- {
- styleClass = TableStyleClassPrefix + GetClassName(table.StyleName, $"tablestyle{table.Id}");
- }
- else
- {
- styleClass = TableStyleClassPrefix + table.TableStyle.ToString().ToLowerInvariant();
- }
+ string styleClass = GetTableBaseClassName(table, false);
var tblClasses = $"{styleClass}";
if (table.ShowHeader)
@@ -112,6 +109,25 @@ internal static string GetTableClasses(ExcelTable table)
return tblClasses;
}
+ internal static string GetTableBaseClassName(ExcelTable table, bool inRangeName)
+ {
+ string styleClass;
+ if (table.TableStyle == TableStyles.Custom)
+ {
+ styleClass = TableStyleClassPrefix + GetClassName(table.StyleName, $"tablestyle{table.Id}");
+ }
+ else
+ {
+ styleClass = TableStyleClassPrefix + table.TableStyle.ToString().ToLowerInvariant();
+ if(inRangeName)
+ {
+ return styleClass + $"-tbl{table.Id}";
+ }
+ }
+
+ return styleClass;
+ }
+
internal static void AddClassesAttributes(HTMLElement element, ExcelTable table, HtmlTableExportSettings settings)
{
if (table.TableStyle == TableStyles.None)
@@ -137,5 +153,66 @@ internal static void AddClassesAttributes(HTMLElement element, ExcelTable table,
element.AddAttribute(HtmlAttributes.Id, settings.TableId);
}
}
+
+ internal static string GetInRangeTableClass(ExcelRangeBase cell, ExcelTable tbl)
+ {
+
+ var tblStyle = tbl.GetTableNamedStyle();
+
+ var styleClass = GetTableBaseClassName(tbl, true);
+ var classes = styleClass + "-default";
+ var tblAdr = tbl.Address;
+
+ if (tbl.ShowHeader && tblAdr._fromRow == cell._fromRow && tblAdr._fromCol == cell._fromCol && tblStyle.FirstHeaderCell.Style.HasValue)
+ {
+ classes += " " + styleClass + "-first-header-cell";
+ }
+ if (tbl.ShowHeader && tblAdr._fromRow == cell._fromRow && tblAdr._toCol == cell._fromCol && tblStyle.LastHeaderCell.Style.HasValue)
+ {
+ classes += " " + styleClass + "-last-header-cell";
+ }
+ else if (tbl.ShowHeader && tblAdr._fromRow == cell._fromRow && tblStyle.HeaderRow.Style.HasValue)
+ {
+ classes += " " + styleClass + "-header";
+ }
+ else if (tbl.ShowTotal && tblAdr._toRow == cell._fromRow && tblAdr._fromRow == cell._fromRow && tblStyle.FirstTotalCell.Style.HasValue)
+ {
+ classes += " " + styleClass + "-first-total-cell";
+ }
+ else if (tbl.ShowTotal && tblAdr._toRow == cell._fromRow && tblAdr._toRow == cell._fromRow && tblStyle.LastTotalCell.Style.HasValue)
+ {
+ classes += " " + styleClass + "-last-total-cell";
+ }
+ else if (tbl.ShowTotal && tblAdr._toRow == cell._fromRow && tblStyle.TotalRow.Style.HasValue)
+ {
+ classes += " " + styleClass + "-total";
+ }
+ else if (tbl.ShowFirstColumn && tblAdr._fromCol == cell._fromCol && tblStyle.FirstColumn.Style.HasValue)
+ {
+ classes += " " + styleClass + "-first-col";
+ }
+ else if (tbl.ShowLastColumn && tblAdr._toCol == cell._fromCol && tblStyle.LastColumn.Style.HasValue)
+ {
+ classes += " " + styleClass + "-last-col";
+ }
+ else if (tbl.ShowRowStripes && ((cell._fromRow - (tbl.ShowHeader ? tbl.Address._fromRow + 1 : tbl.Address._fromRow)) % 2) == 0 && (tblStyle.FirstRowStripe.Style.HasValue))
+ {
+ classes += " " + styleClass + "-first-row-stripe";
+ }
+ else if (tbl.ShowRowStripes && ((cell._fromRow - (tbl.ShowHeader ? tbl.Address._fromRow + 1 : tbl.Address._fromRow)) % 2) == 1 && (tblStyle.SecondRowStripe.Style.HasValue))
+ {
+ classes += " " + styleClass + "-second-row-stripe";
+ }
+ else if (tbl.ShowColumnStripes && ((cell._fromCol - tbl.Address._fromCol) % 2) == 0)
+ {
+ classes += " " + styleClass + "-first-col-stripe";
+ }
+ else if (tbl.ShowColumnStripes && ((cell._fromCol - tbl.Address._fromCol) % 2) == 1)
+ {
+ classes += " " + styleClass + "-second-col-stripe";
+ }
+
+ return classes;
+ }
}
}
diff --git a/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlExporterBaseInternal.cs b/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlExporterBaseInternal.cs
index 997be280a4..f083fbe4fd 100644
--- a/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlExporterBaseInternal.cs
+++ b/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlExporterBaseInternal.cs
@@ -10,6 +10,7 @@ Date Author Change
*************************************************************************************************
6/4/2022 EPPlus Software AB ExcelTable Html Export
*************************************************************************************************/
+using Microsoft.VisualBasic;
using OfficeOpenXml.Core;
using OfficeOpenXml.Drawing;
using OfficeOpenXml.Drawing.Interfaces;
@@ -41,13 +42,13 @@ public HtmlExporterBaseInternal(HtmlExportSettings settings, ExcelRangeBase rang
if (range.Addresses == null)
{
- AddRange(range);
+ AddRange(range, settings.Drawings.Include!=eDrawingInclude.Exclude);
}
else
{
foreach (var address in range.Addresses)
{
- AddRange(range.Worksheet.Cells[address.Address]);
+ AddRange(range.Worksheet.Cells[address.Address], settings.Drawings.Include != eDrawingInclude.Exclude);
}
}
@@ -58,6 +59,7 @@ public HtmlExporterBaseInternal(HtmlExportSettings settings, EPPlusReadOnlyList<
{
Settings = settings;
Require.Argument(ranges).IsNotNull("ranges");
+ AdjustRangeForDimensionAndDrawings(ranges._list, settings.Drawings.Include!=eDrawingInclude.Exclude);
_ranges = ranges;
//TODO: Fix support for all ranges
LoadRangeDrawings(_ranges._list);
@@ -112,7 +114,7 @@ protected HTMLElement GetThead(ExcelRangeBase range, List headers = null
{
table = range.GetTable();
}
-
+ ExcelTable inRangeTable = null;
int headerRows = GetHeaderRows(table);
for (int i = 0; i < headerRows; i++)
@@ -138,24 +140,28 @@ protected HTMLElement GetThead(ExcelRangeBase range, List headers = null
HTMLElement contentElement;
+ if (_hasIntersectingTables && Settings.TableStyle == eHtmlRangeTableInclude.Include)
+ {
+ inRangeTable = cell.GetIntersectingTable();
+ }
+
if (Settings.IncludeCssClassNames)
{
- GetClassData(th, true, image, cell, Settings, _exporterContext, out contentElement, true);
+ GetClassData(th, true, image, cell, Settings, _exporterContext, inRangeTable, out contentElement, true);
}
else
{
contentElement = th;
}
-
AddTableData(table, contentElement, col);
- if ((Settings.Pictures.Include == ePictureInclude.Include) || (Settings.Pictures.Include == ePictureInclude.IncludeInHtmlOnly))
+ if ((Settings.Drawings.Include == eDrawingInclude.Include) || (Settings.Drawings.Include == eDrawingInclude.IncludeInHtmlOnly))
{
image = GetImage(cell.Worksheet.PositionId, cell._fromRow, cell._fromCol);
}
- if ((Settings.Drawings.Include == ePictureInclude.Include) || (Settings.Drawings.Include == ePictureInclude.IncludeInHtmlOnly))
+ if ((Settings.Drawings.Include == eDrawingInclude.Include) || (Settings.Drawings.Include == eDrawingInclude.IncludeInHtmlOnly))
{
drawing = GetDrawing(cell.Worksheet.PositionId, cell._fromRow, cell._fromCol);
}
@@ -229,7 +235,7 @@ protected HTMLElement GetTableBody(ExcelRangeBase range, int row, int endRow)
}
var table = range.GetTable();
-
+ ExcelTable inRangeTable=null;
var ws = range.Worksheet;
HtmlImage image = null;
HtmlDrawing drawing = null;
@@ -271,31 +277,35 @@ protected HTMLElement GetTableBody(ExcelRangeBase range, int row, int endRow)
if (InMergeCellSpan(row, col)) continue;
var colIx = col - range._fromCol;
var cell = ws.Cells[row, col];
-
+ if(Settings.TableStyle== eHtmlRangeTableInclude.Include && _hasIntersectingTables)
+ {
+ inRangeTable = cell.GetIntersectingTable();
+ }
var dataType = HtmlRawDataProvider.GetHtmlDataTypeFromValue(cell.Value);
var tblData = new HTMLElement(HtmlElements.TableData);
SetColRowSpan(range, tblData, cell);
- if ((Settings.Pictures.Include == ePictureInclude.Include) || (Settings.Pictures.Include == ePictureInclude.IncludeInHtmlOnly))
+ if ((Settings.Drawings.Include == eDrawingInclude.Include) || (Settings.Drawings.Include == eDrawingInclude.IncludeInHtmlOnly))
{
image = GetImage(cell.Worksheet.PositionId, cell._fromRow, cell._fromCol);
}
- if (Settings.Drawings.Include == (ePictureInclude.Include | ePictureInclude.IncludeInHtmlOnly))
+ if (Settings.Drawings.Include == (eDrawingInclude.Include | eDrawingInclude.IncludeInHtmlOnly))
{
drawing = GetDrawing(cell.Worksheet.PositionId, cell._fromRow, cell._fromCol);
}
if (cell.Hyperlink == null)
{
- var addRowScope = table == null ? false : table.ShowFirstColumn && col == table.Address._fromCol || table.ShowLastColumn && col == table.Address._toCol;
- AddTableDataFromCell(cell, dataType, tblData, Settings, addRowScope, image, _exporterContext);
+ var t = table ?? inRangeTable;
+ var addRowScope = t == null ? false : t.ShowFirstColumn && col == t.Address._fromCol || t.ShowLastColumn && col == t.Address._toCol;
+ AddTableDataFromCell(cell, dataType, tblData, Settings, addRowScope, image, _exporterContext, inRangeTable);
}
else
{
- GetClassData(tblData, table != null, image, cell, Settings, _exporterContext, out HTMLElement contentElement);
+ GetClassData(tblData, table != null, image, cell, Settings, _exporterContext, inRangeTable, out HTMLElement contentElement);
AddImage(contentElement, Settings, image, cell.Value);
AddHyperlink(contentElement, cell, Settings);
@@ -423,12 +433,12 @@ protected void AddImage(HTMLElement parent, HtmlExportSettings settings, HtmlIma
var name = GetPictureName(image);
string imageName = HtmlExportTableUtil.GetClassName(image.Picture.Name, ((IPictureContainer)image.Picture).ImageHash);
child.AddAttribute("alt", image.Picture.Name);
- if (settings.Pictures.AddNameAsId)
+ if (settings.Drawings.AddNameAsId)
{
child.AddAttribute("id", imageName);
}
- if(settings.Pictures.Include == ePictureInclude.IncludeInHtmlOnly)
+ if(settings.Drawings.Include == eDrawingInclude.IncludeInHtmlOnly)
{
ePictureType? type;
var _encodedImage = ImageEncoder.EncodeImage(image, out type);
@@ -450,12 +460,12 @@ protected void AddDrawing(HTMLElement parent, HtmlExportSettings settings, HtmlS
var child = new HTMLElement(HtmlElements.Img);
string drawingName = HtmlExportTableUtil.GetClassName(d.Drawing.Name, $"drawing{d.Drawing.Id}");
child.AddAttribute("alt", d.Drawing.Name);
- if (settings.Pictures.AddNameAsId)
+ if (settings.Drawings.AddNameAsId)
{
child.AddAttribute("id", drawingName);
}
- if (settings.Drawings.Include == ePictureInclude.IncludeInHtmlOnly)
+ if (settings.Drawings.Include == eDrawingInclude.IncludeInHtmlOnly)
{
child = new HTMLElement(HtmlElements.Svg);
child.ElementName = "div";
@@ -492,18 +502,17 @@ protected void LoadVisibleColumns(ExcelRangeBase range)
protected EPPlusReadOnlyList _ranges = new EPPlusReadOnlyList();
- private void AddRange(ExcelRangeBase range)
+ private void AddRange(ExcelRangeBase range, bool includeDrawings)
{
if (range.IsFullColumn && range.IsFullRow)
{
- _ranges.Add(new ExcelRangeBase(range.Worksheet, range.Worksheet.Dimension.Address));
+ _ranges.Add(AdjustRangeForDimensionAndDrawings(new ExcelRangeBase(range.Worksheet, range.Worksheet.Dimension.Address), includeDrawings));
}
else
{
_ranges.Add(range);
}
}
-
protected void ValidateRangeIndex(int rangeIndex)
{
if (rangeIndex < 0 || rangeIndex >= _ranges.Count)
@@ -721,12 +730,19 @@ protected void AddClassesAttributes(HTMLElement element, ExcelTable table, strin
}
}
- internal void GetClassData(HTMLElement element, bool isTable, HtmlImage image, ExcelRangeBase cell, HtmlExportSettings settings, ExporterContext content, out HTMLElement valueElement, bool isHeader = false)
+ internal void GetClassData(HTMLElement element, bool isTable, HtmlImage image, ExcelRangeBase cell, HtmlExportSettings settings, ExporterContext content, ExcelTable inRangeTable, out HTMLElement valueElement, bool isHeader = false)
{
- var imageCellClassName = GetImageCellClassName(image, Settings, isTable);
- var classString = AttributeTranslator.GetClassAttributeFromStyle(cell, isHeader, settings, imageCellClassName, content);
+ var additionalCellClassName = GetImageCellClassName(image, Settings, isTable);
+
+ if (inRangeTable != null)
+ {
+ additionalCellClassName += (string.IsNullOrEmpty(additionalCellClassName) ? "" : " ") + HtmlExportTableUtil.GetInRangeTableClass(cell, inRangeTable);
+ }
+
+ var classString = AttributeTranslator.GetClassAttributeFromStyle(cell, isHeader, settings, additionalCellClassName, content);
var stylesAndExtras = AttributeTranslator.GetConditionalFormattings(cell, settings, content, ref classString);
+
if (cell.Style.Checkbox)
{
if (cell.Value == null || HtmlRawDataProvider.GetHtmlDataTypeFromValue(cell.Value) == HtmlDataTypes.Boolean)
@@ -818,7 +834,7 @@ private static bool IsTextRotationExcluded(HtmlExportSettings settings, bool isH
}
- public void AddTableDataFromCell(ExcelRangeBase cell, string dataType, HTMLElement element, HtmlExportSettings settings, bool addRowScope, HtmlImage image, ExporterContext content)
+ public void AddTableDataFromCell(ExcelRangeBase cell, string dataType, HTMLElement element, HtmlExportSettings settings, bool addRowScope, HtmlImage image, ExporterContext content, ExcelTable inRangeTable)
{
if (dataType != ColumnDataTypeManager.HtmlDataTypes.String && settings.RenderDataAttributes)
{
@@ -837,7 +853,7 @@ public void AddTableDataFromCell(ExcelRangeBase cell, string dataType, HTMLEleme
}
}
- GetClassData(element, true, image, cell, settings, content, out HTMLElement contentElement);
+ GetClassData(element, true, image, cell, settings, content, inRangeTable, out HTMLElement contentElement);
AddImage(contentElement, settings, image, cell.Value);
diff --git a/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlRangeExporterBase.cs b/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlRangeExporterBase.cs
index ab8b9d5614..a8db72b601 100644
--- a/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlRangeExporterBase.cs
+++ b/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlRangeExporterBase.cs
@@ -13,7 +13,9 @@ Date Author Change
using OfficeOpenXml.Core;
using OfficeOpenXml.Export.HtmlExport.HtmlCollections;
using OfficeOpenXml.Table;
+using System;
using System.Collections.Generic;
+using System.Linq;
namespace OfficeOpenXml.Export.HtmlExport.Exporters.Internal
{
@@ -42,6 +44,10 @@ protected HTMLElement GenerateHTML(int rangeIndex, ExcelHtmlOverrideExportSettin
if (Settings.TableStyle != eHtmlRangeTableInclude.Exclude)
{
table = range.GetTable();
+ if(table==null)
+ {
+ _hasIntersectingTables = HasIntersctingTables(range);
+ }
}
var tableId = GetTableId(rangeIndex, overrideSettings);
@@ -65,12 +71,18 @@ protected HTMLElement GenerateHTML(int rangeIndex, ExcelHtmlOverrideExportSettin
{
AddHeaderRow(range, htmlTable, table, headers);
}
+
// table rows
AddTableRows(htmlTable, range);
return htmlTable;
}
+ private bool HasIntersctingTables(ExcelRangeBase range)
+ {
+ return range.Worksheet.Tables.GetIntersectingRanges(range).Count>0;
+ }
+
private void AddTableRows(HTMLElement htmlTable, ExcelRangeBase range)
{
var row = range._fromRow + _settings.HeaderRows;
diff --git a/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlTableExporterBase.cs b/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlTableExporterBase.cs
index 64633caec8..0920d1539d 100644
--- a/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlTableExporterBase.cs
+++ b/src/EPPlus/Export/HtmlExport/Exporters/Internal/HtmlTableExporterBase.cs
@@ -146,6 +146,7 @@ private void AddTotalRow(HTMLElement table)
var address = _table.Address;
HtmlImage image = null;
+ ExcelTable inRangeTable = null;
foreach (var col in _columns)
{
var tblData = new HTMLElement(HtmlElements.TableData);
@@ -155,7 +156,12 @@ private void AddTotalRow(HTMLElement table)
{
tblData.AddAttribute("role", "cell");
}
- GetClassData(tblData, true, image, cell, Settings, _exporterContext, out HTMLElement contentElement);
+ if (Settings.TableStyle == eHtmlRangeTableInclude.Include && _hasIntersectingTables)
+ {
+ inRangeTable = cell.GetIntersectingTable();
+ }
+
+ GetClassData(tblData, true, image, cell, Settings, _exporterContext, inRangeTable, out HTMLElement contentElement);
AddImage(contentElement, Settings, image, cell.Value);
diff --git a/src/EPPlus/Export/HtmlExport/Settings/HtmlDrawingSettings.cs b/src/EPPlus/Export/HtmlExport/Settings/HtmlDrawingSettings.cs
index d94bfbb869..71d14561b0 100644
--- a/src/EPPlus/Export/HtmlExport/Settings/HtmlDrawingSettings.cs
+++ b/src/EPPlus/Export/HtmlExport/Settings/HtmlDrawingSettings.cs
@@ -1,32 +1,91 @@
-using System;
+using OfficeOpenXml.Drawing;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OfficeOpenXml.Export.HtmlExport
{
+ ///
+ /// Drawing handler
+ ///
public class HtmlDrawingSettings
{
internal HtmlDrawingSettings()
{
}
-
- //Use picture for now. Possibly re-name
///
- /// If how drawings should be included in the html. Default is
+ /// Optional handle to set individual settings for a drawing. Returning null will use the default settings.
///
- public ePictureInclude Include = ePictureInclude.Exclude;
-
+ public Func IndividualDrawingHandler { get; set; } = null;
///
- /// Which type of drawing should be included
+ /// Option to handle if a drawing should be excluded or not.
///
- public eDrawingInclude DrawTypeInclude = eDrawingInclude.None;
-
+ public Func ExcludeDrawingHandler { get; set; } = null;
///
- /// Is absolute by default for charts
+ /// If a drawing should be included in the export or not. Only pictures and drawings with set to true will be included.
///
- public ePicturePosition Position = ePicturePosition.DontSet;
-
+ public eDrawingInclude Include = eDrawingInclude.Exclude;
+ ///
+ /// If the drawing image should be added as absolut or relative in the css.
+ ///
+ public eDrawingPosition Position { get; set; } = eDrawingPosition.Relative;
+ ///
+ /// If the margin in pixels from the top corner should be used.
+ /// If this property is set to true, the cells vertical alignment will be set to 'top',
+ /// otherwise alignment will be set to middle.
+ ///
+ public bool AddMarginTop { get; set; } = false;
+ ///
+ /// If the margin in pixels from the left corner should be used.
+ /// If this property is set to true, the cells text alignment will be set to 'left',
+ /// otherwise alignment will be set to center.
+ ///
+ public bool AddMarginLeft { get; set; } = false;
+ ///
+ /// If set to true the original size of the image is used,
+ /// otherwise the size in the workbook is used. Default is false.
+ ///
+ public bool KeepOriginalSizeOnPictures { get; set; } = false;
+ ///
+ /// Exclude settings
+ ///
+ public PictureCssExclude PictureCssExclude { get; } = new PictureCssExclude();
+ ///
+ /// Adds the Blip name as Id for the img element in the HTML.
+ /// Characters [A-Z][0-9]-_ are allowed. The first character allows [A-Z]_.
+ /// Other characters will be replaced with an hyphen (-).
+ ///
+ public bool AddNameAsId
+ {
+ get;
+ set;
+ } = true;
+ ///
+ /// Reset the setting to it's default values.
+ ///
+ public void ResetToDefault()
+ {
+ Include = eDrawingInclude.Exclude;
+ Position = eDrawingPosition.Relative;
+ AddMarginLeft = false;
+ AddMarginTop = false;
+ KeepOriginalSizeOnPictures = false;
+ PictureCssExclude.ResetToDefault();
+ }
+ ///
+ /// Copy the values from another settings object.
+ ///
+ /// The object to copy.
+ public void Copy(HtmlDrawingSettings copy)
+ {
+ Include = copy.Include;
+ Position = copy.Position;
+ AddMarginLeft = copy.AddMarginLeft;
+ AddMarginTop = copy.AddMarginTop;
+ KeepOriginalSizeOnPictures = copy.KeepOriginalSizeOnPictures;
+ PictureCssExclude.Copy(copy.PictureCssExclude);
+ }
}
}
diff --git a/src/EPPlus/Export/HtmlExport/Settings/HtmlExportSettings.cs b/src/EPPlus/Export/HtmlExport/Settings/HtmlExportSettings.cs
index 5cde7470e7..d1c1b8d671 100644
--- a/src/EPPlus/Export/HtmlExport/Settings/HtmlExportSettings.cs
+++ b/src/EPPlus/Export/HtmlExport/Settings/HtmlExportSettings.cs
@@ -11,6 +11,7 @@ Date Author Change
05/11/2021 EPPlus Software AB ExcelTable Html Export
*************************************************************************************************/
using OfficeOpenXml.Export.HtmlExport.Accessibility;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
@@ -129,15 +130,24 @@ public string DataValueAttributeName
///
public string IconPrefix { get; set; } = "ic";
+ HtmlPictureSettings _legacyPicturesSettings;
///
/// If picture drawings will be included. Default is true.
///
+ [Obsolete("Use the Drawings.Pictures property instead")]
public HtmlPictureSettings Pictures
{
- get;
- } = new HtmlPictureSettings();
- ///
- /// If and which Charts and/or Shapes will be included
+ get
+ {
+ if(_legacyPicturesSettings==null)
+ {
+ _legacyPicturesSettings = new HtmlPictureSettings(Drawings);
+ }
+ return _legacyPicturesSettings;
+ }
+ }
+ ///
+ /// If and which drawing objects like Charts, Pictures and/or Shapes will be included
///
public HtmlDrawingSettings Drawings
{
diff --git a/src/EPPlus/Export/HtmlExport/Settings/HtmlPictureSettings.cs b/src/EPPlus/Export/HtmlExport/Settings/HtmlPictureSettings.cs
index 366f25e081..afc2d18d9d 100644
--- a/src/EPPlus/Export/HtmlExport/Settings/HtmlPictureSettings.cs
+++ b/src/EPPlus/Export/HtmlExport/Settings/HtmlPictureSettings.cs
@@ -11,47 +11,106 @@ Date Author Change
05/11/2021 EPPlus Software AB ExcelTable Html Export
*************************************************************************************************/
+using System;
+
namespace OfficeOpenXml.Export.HtmlExport
{
///
/// Setting for rendering of picture drawings
///
+ [Obsolete("Use HtmlDrawingSettings instead (Settings.Drawings).")]
public class HtmlPictureSettings
{
- internal HtmlPictureSettings()
+ HtmlDrawingSettings _drawingsSettings;
+ internal HtmlPictureSettings(HtmlDrawingSettings drawingsSettings)
{
-
+ _drawingsSettings = drawingsSettings;
}
///
/// If picture drawings should be included in the html. Default is
///
- public ePictureInclude Include { get; set; } = ePictureInclude.Exclude;
+ public ePictureInclude Include
+ {
+ get
+ {
+ return (ePictureInclude)_drawingsSettings.Include;
+ }
+ set
+ {
+ _drawingsSettings.Include = (eDrawingInclude)value;
+ }
+ }
///
/// If the image should be added as absolut or relative in the css.
///
- public ePicturePosition Position { get; set; } = ePicturePosition.Relative;
+ public ePicturePosition Position
+ {
+ get
+ {
+ return (ePicturePosition)_drawingsSettings.Position;
+ }
+ set
+ {
+ _drawingsSettings.Position = (eDrawingPosition)value;
+ }
+ }
///
/// If the margin in pixels from the top corner should be used.
/// If this property is set to true, the cells vertical alignment will be set to 'top',
/// otherwise alignment will be set to middle.
///
- public bool AddMarginTop { get; set; } = false;
+ public bool AddMarginTop
+ {
+ get
+ {
+ return _drawingsSettings.AddMarginTop;
+ }
+ set
+ {
+ _drawingsSettings.AddMarginTop = value;
+ }
+ }
///
/// If the margin in pixels from the left corner should be used.
/// If this property is set to true, the cells text alignment will be set to 'left',
/// otherwise alignment will be set to center.
///
- public bool AddMarginLeft { get; set; } = false;
- ///
- /// If set to true the original size of the image is used,
- /// otherwise the size in the workbook is used. Default is false.
- ///
- public bool KeepOriginalSize { get; set; } = false;
+ public bool AddMarginLeft
+ {
+ get
+ {
+ return _drawingsSettings.AddMarginLeft;
+ }
+ set
+ {
+ _drawingsSettings.AddMarginLeft = value;
+ }
+ } ///
+ /// If set to true the original size of the image is used,
+ /// otherwise the size in the workbook is used. Default is false.
+ ///
+ public bool KeepOriginalSize
+ {
+ get
+ {
+ return _drawingsSettings.KeepOriginalSizeOnPictures;
+ }
+ set
+ {
+ _drawingsSettings.KeepOriginalSizeOnPictures = value;
+ }
+ }
///
/// Exclude settings
///
- public PictureCssExclude CssExclude { get; } = new PictureCssExclude();
+ public PictureCssExclude CssExclude
+ {
+ get
+ {
+ return _drawingsSettings.PictureCssExclude;
+ }
+ }
///
/// Adds the Blip name as Id for the img element in the HTML.
/// Characters [A-Z][0-9]-_ are allowed. The first character allows [A-Z]_.
@@ -59,9 +118,15 @@ internal HtmlPictureSettings()
///
public bool AddNameAsId
{
- get;
- set;
- } = true;
+ get
+ {
+ return _drawingsSettings.AddNameAsId;
+ }
+ set
+ {
+ _drawingsSettings.AddNameAsId = value;
+ }
+ }
///
/// Reset the setting to it's default values.
///
diff --git a/src/EPPlus/Export/HtmlExport/Translators/AttributeTranslator.cs b/src/EPPlus/Export/HtmlExport/Translators/AttributeTranslator.cs
index de7595f503..b2d1964551 100644
--- a/src/EPPlus/Export/HtmlExport/Translators/AttributeTranslator.cs
+++ b/src/EPPlus/Export/HtmlExport/Translators/AttributeTranslator.cs
@@ -49,11 +49,11 @@ internal static string GetClassAttributeFromStyle(ExcelRangeBase cell, bool isHe
{
if (ConvertUtil.IsNumericOrDate(cell.Value))
{
- cls = $"{styleClassPrefix}ar";
+ cls += $" {styleClassPrefix}ar";
}
else if (isHeader)
{
- cls = $"{styleClassPrefix}al";
+ cls += $" {styleClassPrefix}al";
}
}
@@ -93,7 +93,7 @@ internal static string GetClassAttributeFromStyle(ExcelRangeBase cell, bool isHe
}
}
- return cls;
+ return cls.Trim();
}
internal static List GetConditionalFormattings(ExcelRangeBase cell, HtmlExportSettings settings, ExporterContext context, ref string cls)
diff --git a/src/EPPlus/Export/HtmlExport/Translators/CssDrawingPropertiesTranslator.cs b/src/EPPlus/Export/HtmlExport/Translators/CssDrawingPropertiesTranslator.cs
index dab122d097..88e8e80590 100644
--- a/src/EPPlus/Export/HtmlExport/Translators/CssDrawingPropertiesTranslator.cs
+++ b/src/EPPlus/Export/HtmlExport/Translators/CssDrawingPropertiesTranslator.cs
@@ -39,7 +39,7 @@ internal CssDrawingPropertiesTranslator(HtmlSvgDrawing d)
internal override List GenerateDeclarationList(TranslatorContext context)
{
- if (context.Drawings.Position == ePicturePosition.Relative)
+ if (context.Drawings.Position == eDrawingPosition.Relative)
{
if (_bounds.Left != 0)
{
@@ -50,19 +50,14 @@ internal override List GenerateDeclarationList(TranslatorContext co
AddDeclaration("top", $"{_bounds.Top.PointToPixel():F0}px");
}
}
- else if (context.Drawings.Position == ePicturePosition.Absolute)
+ else if (context.Drawings.Position == eDrawingPosition.Absolute)
{
- if (_bounds.Left != 0)
- {
- AddDeclaration("left", $"{_bounds.GlobalLeft.PointToPixel():F0}px");
- }
- if (_bounds.Top != 0)
- {
- AddDeclaration("top", $"{_bounds.GlobalTop.PointToPixel():F0}px");
- }
+ AddDeclaration("position", $"{context.Drawings.Position.ToString().ToLower()}");
+ AddDeclaration("left", $"{_bounds.GlobalLeft.PointToPixel():F0}px");
+ AddDeclaration("top", $"{_bounds.GlobalTop.PointToPixel():F0}px");
}
- if (context.Pictures.KeepOriginalSize == false)
+ if (context.Drawings.KeepOriginalSizeOnPictures == false)
{
if (_width != _bounds.Width)
{
@@ -74,7 +69,7 @@ internal override List GenerateDeclarationList(TranslatorContext co
}
}
- if (_border.LineStyle != null && context.Pictures.CssExclude.Border == false)
+ if (_border.LineStyle != null && context.Drawings.PictureCssExclude.Border == false)
{
var border = GetDrawingBorder();
AddDeclaration("border", border);
diff --git a/src/EPPlus/Export/HtmlExport/Translators/CssImageAlignmentTranslator.cs b/src/EPPlus/Export/HtmlExport/Translators/CssImageAlignmentTranslator.cs
index 46dc89bf32..f62228a5a2 100644
--- a/src/EPPlus/Export/HtmlExport/Translators/CssImageAlignmentTranslator.cs
+++ b/src/EPPlus/Export/HtmlExport/Translators/CssImageAlignmentTranslator.cs
@@ -17,17 +17,17 @@ namespace OfficeOpenXml.Export.HtmlExport.Translators
{
internal class CssImageAlignmentTranslator : TranslatorBase
{
- HtmlPictureSettings _picSettings;
+ HtmlDrawingSettings _drawingsSettings;
- internal CssImageAlignmentTranslator(HtmlPictureSettings picSettings)
+ internal CssImageAlignmentTranslator(HtmlDrawingSettings drawingsSettings)
{
- _picSettings = picSettings;
+ _drawingsSettings = drawingsSettings;
}
internal override List GenerateDeclarationList(TranslatorContext context)
{
- AddDeclaration("vertical-align", _picSettings.AddMarginTop ? "top" : "middle");
- AddDeclaration("text-align", _picSettings.AddMarginLeft ? "left" : "center");
+ AddDeclaration("vertical-align", _drawingsSettings.AddMarginTop ? "top" : "middle");
+ AddDeclaration("text-align", _drawingsSettings.AddMarginLeft ? "left" : "center");
return declarations;
}
diff --git a/src/EPPlus/Export/HtmlExport/Translators/CssImagePropertiesTranslator.cs b/src/EPPlus/Export/HtmlExport/Translators/CssImagePropertiesTranslator.cs
index 1d3ee325fe..e1a44b77a0 100644
--- a/src/EPPlus/Export/HtmlExport/Translators/CssImagePropertiesTranslator.cs
+++ b/src/EPPlus/Export/HtmlExport/Translators/CssImagePropertiesTranslator.cs
@@ -34,7 +34,7 @@ internal CssImagePropertiesTranslator(HtmlImage image)
internal override List GenerateDeclarationList(TranslatorContext context)
{
- if (context.Pictures.KeepOriginalSize == false)
+ if (context.Drawings.KeepOriginalSizeOnPictures == false)
{
if (_width != _bounds.Width)
{
@@ -46,7 +46,7 @@ internal override List GenerateDeclarationList(TranslatorContext co
}
}
- if (_border.LineStyle != null && context.Pictures.CssExclude.Border == false)
+ if (_border.LineStyle != null && context.Drawings.PictureCssExclude.Border == false)
{
var border = GetDrawingBorder();
AddDeclaration("border", border);
diff --git a/src/EPPlus/Export/HtmlExport/Translators/CssImageTranslator.cs b/src/EPPlus/Export/HtmlExport/Translators/CssImageTranslator.cs
index e3648b2a8c..c02f2f4d17 100644
--- a/src/EPPlus/Export/HtmlExport/Translators/CssImageTranslator.cs
+++ b/src/EPPlus/Export/HtmlExport/Translators/CssImageTranslator.cs
@@ -47,23 +47,23 @@ internal override List GenerateDeclarationList(TranslatorContext co
{
AddDeclaration("content", $"url('data:{GetContentType(type.Value)};base64,{_encodedImage}')");
- if (context.Pictures.Position != ePicturePosition.DontSet)
- {
- AddDeclaration("position", $"{context.Pictures.Position.ToString().ToLower()}");
- }
+ //if (context.Drawings.Position != eDrawingPosition.DontSet)
+ //{
+ // AddDeclaration("position", $"{context.Drawings.Position.ToString().ToLower()}");
+ //}
- if(isDrawing && context.Drawings.Position != ePicturePosition.DontSet)
- {
- AddDeclaration("position", $"{context.Drawings.Position.ToString().ToLower()}");
- }
+ //if(isDrawing && context.Drawings.Position != eDrawingPosition.DontSet)
+ //{
+ // AddDeclaration("position", $"{context.Drawings.Position.ToString().ToLower()}");
+ //}
- if (_p.FromColumnOff != 0 && context.Pictures.AddMarginLeft)
+ if (_p.FromColumnOff != 0 && context.Drawings.AddMarginLeft)
{
var leftOffset = _p.FromColumnOff / ExcelPicture.EMU_PER_PIXEL;
AddDeclaration("margin-left", $"{leftOffset}px");
}
- if (_p.FromRowOff != 0 && context.Pictures.AddMarginTop)
+ if (_p.FromRowOff != 0 && context.Drawings.AddMarginTop)
{
var topOffset = _p.FromRowOff / ExcelPicture.EMU_PER_PIXEL;
AddDeclaration("margin-top", $"{topOffset}px");
diff --git a/src/EPPlus/Export/HtmlExport/Translators/TranslatorContext.cs b/src/EPPlus/Export/HtmlExport/Translators/TranslatorContext.cs
index 92e90b0b6b..ee45883c06 100644
--- a/src/EPPlus/Export/HtmlExport/Translators/TranslatorContext.cs
+++ b/src/EPPlus/Export/HtmlExport/Translators/TranslatorContext.cs
@@ -33,7 +33,6 @@ internal class TranslatorContext
internal CssExclude Exclude;
internal CssExportSettings Settings;
- internal HtmlPictureSettings Pictures;
internal HtmlDrawingSettings Drawings;
private TranslatorBase strategy;
@@ -53,14 +52,12 @@ public TranslatorContext(HtmlRangeExportSettings settings)
{
Exclude = settings.Css.CssExclude;
Settings = settings.Css;
- Pictures = settings.Pictures;
Drawings = settings.Drawings;
}
public TranslatorContext(HtmlTableExportSettings settings, CssExclude exclude)
{
Settings = settings.Css;
- Pictures = settings.Pictures;
Drawings = settings.Drawings;
Exclude = exclude;
}
diff --git a/src/EPPlus/Style/Dxf/DxfStyleHandler.cs b/src/EPPlus/Style/Dxf/DxfStyleHandler.cs
index e830f44400..5da4198c15 100644
--- a/src/EPPlus/Style/Dxf/DxfStyleHandler.cs
+++ b/src/EPPlus/Style/Dxf/DxfStyleHandler.cs
@@ -210,7 +210,9 @@ private static void UpdateConditionalFormatting(ExcelWorksheet ws, ExcelStyleCol
{
foreach (var cf in ws.ConditionalFormatting)
{
- if (cf.Style.HasValue)
+ //If at least one border exists then a dxf style for the border must be added even if the value for that border is empty
+ //(Thus meaning HasValue is false)
+ if (cf.Style.HasValue || cf.Style.Border != null && cf.Style.Border.AtLeastOneBorderExists())
{
var standardDxfStyle = cf.Style.ToDxfStyle();
@@ -228,11 +230,9 @@ private static void UpdateConditionalFormatting(ExcelWorksheet ws, ExcelStyleCol
{
((ExcelConditionalFormattingRule)cf).DxfId = ix;
cf.Style.DxfId = ix;
- //cf.Style.DxfId = ix;
}
}
}
- //var num = dxfs._list[129];
}
internal static void CopyDxfStylesTable(ExcelTable tblFrom, ExcelTable tblTo)
{
diff --git a/src/EPPlus/Style/Dxf/ExcelDxfBorder.cs b/src/EPPlus/Style/Dxf/ExcelDxfBorder.cs
index 5b51e8a3c7..2b369f1883 100644
--- a/src/EPPlus/Style/Dxf/ExcelDxfBorder.cs
+++ b/src/EPPlus/Style/Dxf/ExcelDxfBorder.cs
@@ -205,14 +205,36 @@ internal override DxfStyleBase Clone()
Horizontal = (ExcelDxfBorderItem)Horizontal.Clone(),
};
}
+
+ private bool BorderItemExists(ExcelDxfBorderItem bi)
+ {
+ if (bi.Style != null)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ internal bool AtLeastOneBorderExists()
+ {
+ if (BorderItemExists(Left)) return true;
+ if (BorderItemExists(Right)) return true;
+ if (BorderItemExists(Bottom)) return true;
+ if (BorderItemExists(Top)) return true;
+ if (BorderItemExists(Vertical)) return true;
+ if (BorderItemExists(Horizontal)) return true;
+
+ return false;
+ }
+
internal override void SetValuesFromXml(XmlHelper helper)
{
if (helper.ExistsNode("d:border"))
{
Left = GetBorderItem(helper, "d:border/d:left", eStyleClass.BorderLeft);
- Right = GetBorderItem(helper, "d:border/d:right", eStyleClass.BorderLeft);
- Bottom = GetBorderItem(helper, "d:border/d:bottom", eStyleClass.BorderLeft);
- Top = GetBorderItem(helper, "d:border/d:top", eStyleClass.BorderLeft);
+ Right = GetBorderItem(helper, "d:border/d:right", eStyleClass.BorderRight);
+ Bottom = GetBorderItem(helper, "d:border/d:bottom", eStyleClass.BorderBottom);
+ Top = GetBorderItem(helper, "d:border/d:top", eStyleClass.BorderTop);
Vertical = GetBorderItem(helper, "d:border/d:vertical", eStyleClass.Border);
Horizontal = GetBorderItem(helper, "d:border/d:horizontal", eStyleClass.Border);
}
@@ -224,7 +246,8 @@ private ExcelDxfBorderItem GetBorderItem(XmlHelper helper, string path, eStyleCl
if (exists)
{
var style = helper.GetXmlNodeString(path + "/@style");
- bi.Style = GetBorderStyleEnum(style);
+ //When exists and border has no style the CT_BorderPr\ST_BorderStyle node defaults to BorderNone if the node exists even when empty
+ bi.Style = GetBorderStyleEnum(style) ?? ExcelBorderStyle.None;
bi.Color = GetColor(helper, path + "/d:color", styleClass);
}
return bi;
diff --git a/src/EPPlus/Style/Dxf/ExcelDxfStyleLimitedFont.cs b/src/EPPlus/Style/Dxf/ExcelDxfStyleLimitedFont.cs
index 4e5a15ae73..08a4d370e6 100644
--- a/src/EPPlus/Style/Dxf/ExcelDxfStyleLimitedFont.cs
+++ b/src/EPPlus/Style/Dxf/ExcelDxfStyleLimitedFont.cs
@@ -52,7 +52,7 @@ internal override void CreateNodes(XmlHelper helper, string path)
{
if (Font.HasValue) Font.CreateNodes(helper, "d:font");
if (Fill.HasValue) Fill.CreateNodes(helper, "d:fill");
- if (Border.HasValue) Border.CreateNodes(helper, "d:border");
+ if (Border.HasValue || Border.AtLeastOneBorderExists()) Border.CreateNodes(helper, "d:border");
}
///
/// If the object has any properties set
diff --git a/src/EPPlus/Table/ExcelTable.cs b/src/EPPlus/Table/ExcelTable.cs
index b4f4b07da7..ed270f97c8 100644
--- a/src/EPPlus/Table/ExcelTable.cs
+++ b/src/EPPlus/Table/ExcelTable.cs
@@ -33,6 +33,8 @@ Date Author Change
using OfficeOpenXml.Data.QueryTable;
using OfficeOpenXml.Data.Connection.IOHandlers;
using OfficeOpenXml.Utils.EnumUtils;
+using OfficeOpenXml.Style.Table;
+
@@ -1522,5 +1524,23 @@ public ExcelTable Copy(ExcelRangeBase range)
Range.Copy(range);
return WorkSheet.Tables.FirstOrDefault(x => x.Address.Collide(range) != ExcelAddressBase.eAddressCollition.No);
}
+
+ internal ExcelTableNamedStyle GetTableNamedStyle()
+ {
+ ExcelTableNamedStyle tblStyle;
+ if (TableStyle == TableStyles.Custom)
+ {
+ tblStyle = WorkSheet.Workbook.Styles.TableStyles[StyleName].As.TableStyle;
+ }
+ else
+ {
+ var tmpNode = WorkSheet.Workbook.StylesXml.CreateElement("c:tableStyle");
+ tblStyle = new ExcelTableNamedStyle(WorkSheet.Workbook.Styles.NameSpaceManager, tmpNode, WorkSheet.Workbook.Styles);
+ tblStyle.SetFromTemplate(TableStyle);
+ }
+
+ return tblStyle;
+ }
}
}
+
diff --git a/src/EPPlus/XmlHelper.cs b/src/EPPlus/XmlHelper.cs
index 9c6ef4d656..37148b1e43 100644
--- a/src/EPPlus/XmlHelper.cs
+++ b/src/EPPlus/XmlHelper.cs
@@ -1141,7 +1141,7 @@ internal int GetXmlNodeInt(string path, int defaultValue = int.MinValue)
internal double GetXmlNodeAngle(string path, double defaultValue = 0)
{
int a = GetXmlNodeInt(path);
- if (a < 0) return defaultValue;
+ if (a == int.MinValue) return defaultValue;
return a / 60000D;
}
internal double GetXmlNodeEmuToPt(string path, double defaultValue = 0)
diff --git a/src/EPPlusTest/Core/Worksheet/HeaderFooterSectionOrderTests.cs b/src/EPPlusTest/Core/Worksheet/HeaderFooterSectionOrderTests.cs
new file mode 100644
index 0000000000..58be5d0ae2
--- /dev/null
+++ b/src/EPPlusTest/Core/Worksheet/HeaderFooterSectionOrderTests.cs
@@ -0,0 +1,367 @@
+/*******************************************************************************
+ * You may amend and distribute as you like, but don't remove this header!
+ *
+ * Required Notice: Copyright (C) EPPlus Software AB.
+ * https://epplussoftware.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
+ * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
+ *
+ * All code and executables are provided "" as is "" with no warranty either express or implied.
+ * The author accepts no liability for any damage or loss of business that this product may cause.
+ *
+ * Code change notes:
+ *
+ Date Author Change
+ *******************************************************************************
+ 08/25/2026 EPPlus Software AB Regression tests: header/footer sections
+ must parse regardless of their order
+ *******************************************************************************/
+using System;
+using System.IO;
+using System.Xml;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using OfficeOpenXml;
+
+namespace EPPlusTest.Core.Worksheet
+{
+ ///
+ /// Regression tests for header/footer sections being lost when they are not stored in
+ /// Left, Center, Right order.
+ ///
+ /// The ExcelHeaderFooterText constructor takes the first section code from the first two
+ /// characters of the raw string, then scans for further section codes. Before the fix its
+ /// scan only recognized "&C" and "&R" - never "&L" - so a "&L" appearing
+ /// anywhere other than at position 0 was not treated as the start of a new section. Its
+ /// content was swallowed into the preceding section, and then discarded when that section
+ /// was normalized by ReadHeaderFooterFormat/WriteHeaderFooterFormat, taking any "&G"
+ /// picture placeholder with it. The picture survived in the VML collection, so
+ /// HeaderFooter.Pictures.Count was unchanged, but nothing referenced it any more and it
+ /// stopped rendering in Excel.
+ ///
+ /// Excel writes the sections in the order the user created them, which is frequently not
+ /// Left, Center, Right - so this affected ordinary files produced by Excel itself.
+ ///
+ /// Reported case (oddfooter-left-corruption-template.xlsx), oddFooter raw value:
+ /// &C&"-,Bold"&12{FORMID}&"-,Regular"&11\n&L&G&R&G
+ /// which before the fix was persisted as:
+ /// &C&"-,Bold"&12{FORMID}&"-,Regular"&11\n&R&G
+ ///
+ /// The same file's oddHeader ("&L&G&R&G") was unaffected, both because it
+ /// starts with "&L" and because Save() only rewrites a node whose backing field is
+ /// non-null - and the reported repro only ever touched OddFooter.
+ ///
+ [TestClass]
+ public class HeaderFooterSectionOrderTests : TestBase
+ {
+ /// The customer's exact oddFooter value. Section order: Center, Left, Right.
+ private const string CustomerOddFooter = "&C&\"-,Bold\"&12{FORMID}&\"-,Regular\"&11\n&L&G&R&G";
+
+ /// The customer's exact oddHeader value. Section order: Left, Right.
+ private const string CustomerOddHeader = "&L&G&R&G";
+
+ [ClassInitialize]
+ public static void Init(TestContext testContext)
+ {
+ InitBase();
+ }
+
+ [TestMethod]
+ public void CustomerOddFooter_SurvivesRoundTrip_WhenOddFooterIsTouched()
+ {
+ // The exact scenario from the support ticket: a footer whose sections are stored
+ // in Center, Left, Right order, with a single read of a header/footer property as
+ // the only interaction before saving.
+ using (var pkg = new ExcelPackage())
+ {
+ var ws = pkg.Workbook.Worksheets.Add("Sheet1");
+ SetRawHeaderFooterNode(ws, "oddFooter", CustomerOddFooter);
+ SetRawHeaderFooterNode(ws, "oddHeader", CustomerOddHeader);
+
+ // The single interaction from the customer's repro script.
+ var _ = ws.HeaderFooter.OddFooter.LeftAlignedText;
+
+ var persistedFooter = SaveAndReadRawNode(pkg, "oddFooter");
+ var persistedHeader = SaveAndReadRawNode(pkg, "oddHeader");
+
+ StringAssert.Contains(persistedFooter, "&L" + ExcelHeaderFooter.Image,
+ "The Left section's picture placeholder must survive the round trip. " +
+ $"Persisted footer was: \"{Escape(persistedFooter)}\".");
+
+ Assert.AreEqual(CustomerOddHeader, persistedHeader,
+ "The untouched oddHeader must be unchanged by the save.");
+ }
+ }
+
+ [TestMethod]
+ public void LeftSection_IsParsed_RegardlessOfSectionOrder()
+ {
+ // Every one of these holds the same three logical sections, only reordered. The
+ // Left section always contains a picture placeholder, and must always come back.
+ var variants = new[]
+ {
+ new { Name = "L,C,R", Raw = "&L&G&CCenterText&R&G" },
+ new { Name = "C,L,R", Raw = "&CCenterText&L&G&R&G" },
+ new { Name = "R,L,C", Raw = "&R&G&L&G&CCenterText" },
+ new { Name = "C,R,L", Raw = "&CCenterText&R&G&L&G" },
+ new { Name = "R,C,L", Raw = "&R&G&CCenterText&L&G" },
+ new { Name = "L,R", Raw = "&L&G&R&G" },
+ new { Name = "C,L", Raw = "&CCenterText&L&G" },
+ new { Name = "R,L", Raw = "&R&G&L&G" },
+ };
+
+ foreach (var v in variants)
+ {
+ using (var pkg = new ExcelPackage())
+ {
+ var ws = pkg.Workbook.Worksheets.Add("Sheet1");
+ SetRawHeaderFooterNode(ws, "oddFooter", v.Raw);
+
+ var oddFooter = ws.HeaderFooter.OddFooter;
+
+ StringAssert.Contains(oddFooter.LeftAlignedText, ExcelHeaderFooter.Image,
+ $"[{v.Name}] The Left section's '&G' placeholder was not parsed from " +
+ $"\"{Escape(v.Raw)}\". Left parsed as \"{Escape(oddFooter.LeftAlignedText)}\".");
+
+ // Guard against the content merely being relocated into another section.
+ Assert.IsFalse(Contains(oddFooter.CenteredText, "&L"),
+ $"[{v.Name}] The Center section swallowed a stray '&L': " +
+ $"\"{Escape(oddFooter.CenteredText)}\".");
+ Assert.IsFalse(Contains(oddFooter.RightAlignedText, "&L"),
+ $"[{v.Name}] The Right section swallowed a stray '&L': " +
+ $"\"{Escape(oddFooter.RightAlignedText)}\".");
+ }
+ }
+ }
+
+ [TestMethod]
+ public void AllSections_SurviveRoundTrip_RegardlessOfSectionOrder()
+ {
+ // As above, but verifying what actually reaches the file. Section order is not
+ // required to be preserved - only the content of each section.
+ var variants = new[]
+ {
+ new { Name = "L,C,R", Raw = "&L&G&CCenterText&R&G" },
+ new { Name = "C,L,R", Raw = "&CCenterText&L&G&R&G" },
+ new { Name = "R,L,C", Raw = "&R&G&L&G&CCenterText" },
+ new { Name = "C,R,L", Raw = "&CCenterText&R&G&L&G" },
+ new { Name = "R,C,L", Raw = "&R&G&CCenterText&L&G" },
+ };
+
+ foreach (var v in variants)
+ {
+ using (var pkg = new ExcelPackage())
+ {
+ var ws = pkg.Workbook.Worksheets.Add("Sheet1");
+ SetRawHeaderFooterNode(ws, "oddFooter", v.Raw);
+
+ // Touch the object so Save() rewrites the node.
+ var _ = ws.HeaderFooter.OddFooter.CenteredText;
+
+ var persisted = SaveAndReadRawNode(pkg, "oddFooter");
+
+ StringAssert.Contains(persisted, "&L" + ExcelHeaderFooter.Image,
+ $"[{v.Name}] Left section lost. Raw was \"{Escape(v.Raw)}\", " +
+ $"persisted \"{Escape(persisted)}\".");
+ StringAssert.Contains(persisted, "&CCenterText",
+ $"[{v.Name}] Center section lost. Persisted \"{Escape(persisted)}\".");
+ StringAssert.Contains(persisted, "&R" + ExcelHeaderFooter.Image,
+ $"[{v.Name}] Right section lost. Persisted \"{Escape(persisted)}\".");
+ }
+ }
+ }
+
+ [TestMethod]
+ public void EmptySectionBetweenTwoSections_DoesNotConsumeFollowingSectionCode()
+ {
+ // Covers the "pos = startPos - 1" part of the fix. With the previous
+ // "pos = startPos" the loop's pos++ skipped the character at startPos, so a
+ // section code starting immediately after a consumed code - i.e. an empty
+ // section - was missed, and the following section was swallowed by it.
+ //
+ // The empty section must sit in the MIDDLE for this to bite: the first section
+ // code is taken outside the loop, so no skip has happened yet at that point.
+ // "&C&L&G&R&G" therefore parses correctly even without the fix and would not
+ // catch a regression here - the empty Center has to follow a match made inside
+ // the loop, as below.
+ const string raw = "&L&G&C&R&G";
+
+ using (var pkg = new ExcelPackage())
+ {
+ var ws = pkg.Workbook.Worksheets.Add("Sheet1");
+ SetRawHeaderFooterNode(ws, "oddFooter", raw);
+
+ var oddFooter = ws.HeaderFooter.OddFooter;
+
+ Console.WriteLine($"Raw : \"{Escape(raw)}\"");
+ Console.WriteLine($"Left : \"{Escape(oddFooter.LeftAlignedText)}\"");
+ Console.WriteLine($"Center : \"{Escape(oddFooter.CenteredText)}\"");
+ Console.WriteLine($"Right : \"{Escape(oddFooter.RightAlignedText)}\"");
+
+ StringAssert.Contains(oddFooter.LeftAlignedText, ExcelHeaderFooter.Image,
+ $"Left section not parsed from \"{Escape(raw)}\".");
+
+ // The empty Center must not swallow the Right section's code.
+ Assert.IsFalse(Contains(oddFooter.CenteredText, "&R"),
+ $"The empty Center section swallowed the following '&R': " +
+ $"\"{Escape(oddFooter.CenteredText)}\".");
+
+ // This is the assertion that should fail if "pos = startPos - 1" is reverted:
+ // Right is never set, so RightAlignedText comes back as just "&R".
+ StringAssert.Contains(oddFooter.RightAlignedText, ExcelHeaderFooter.Image,
+ $"Right section not parsed from \"{Escape(raw)}\" - it was most likely " +
+ $"consumed by the empty Center section. Right parsed as " +
+ $"\"{Escape(oddFooter.RightAlignedText)}\".");
+ }
+ }
+
+ [TestMethod]
+ public void SectionCodeAtEndOfString_IsRecognized()
+ {
+ // Covers the "text.Length - 1" part of the fix. The previous "text.Length - 2"
+ // bound meant a section code occupying the final two characters was never seen,
+ // so it was swallowed into the preceding section instead of starting an empty one.
+ const string raw = "&L&G&R";
+
+ using (var pkg = new ExcelPackage())
+ {
+ var ws = pkg.Workbook.Worksheets.Add("Sheet1");
+ SetRawHeaderFooterNode(ws, "oddFooter", raw);
+
+ var oddFooter = ws.HeaderFooter.OddFooter;
+
+ Assert.IsFalse(Contains(oddFooter.LeftAlignedText, "&R"),
+ $"The trailing '&R' was swallowed into the Left section: " +
+ $"\"{Escape(oddFooter.LeftAlignedText)}\".");
+ StringAssert.Contains(oddFooter.LeftAlignedText, ExcelHeaderFooter.Image,
+ "The Left section's picture placeholder should still be parsed.");
+ }
+ }
+
+ [TestMethod]
+ public void HeaderSections_AreParsed_RegardlessOfSectionOrder()
+ {
+ // The same parsing path backs OddHeader, so it needs the same coverage - the
+ // reported case simply never touched the header.
+ const string raw = "&CHeaderCenter&L&G&R&G";
+
+ using (var pkg = new ExcelPackage())
+ {
+ var ws = pkg.Workbook.Worksheets.Add("Sheet1");
+ SetRawHeaderFooterNode(ws, "oddHeader", raw);
+
+ var oddHeader = ws.HeaderFooter.OddHeader;
+
+ StringAssert.Contains(oddHeader.LeftAlignedText, ExcelHeaderFooter.Image,
+ $"Left header section not parsed from \"{Escape(raw)}\". " +
+ $"Left parsed as \"{Escape(oddHeader.LeftAlignedText)}\".");
+ }
+ }
+
+ [TestMethod]
+ public void PictureCount_IsUnchanged_ByHeaderFooterTextRoundTrip()
+ {
+ // The reported symptom that made this hard to spot: the picture object itself was
+ // never lost, only the text reference to it, so Pictures.Count kept reporting the
+ // original value. This pins that behavior down so a future change cannot start
+ // silently dropping the VML pictures instead.
+ using (var pkg = new ExcelPackage())
+ {
+ var ws = pkg.Workbook.Worksheets.Add("Sheet1");
+ SetRawHeaderFooterNode(ws, "oddFooter", CustomerOddFooter);
+
+ var countBefore = ws.HeaderFooter.Pictures.Count;
+ var _ = ws.HeaderFooter.OddFooter.LeftAlignedText;
+
+ using (var stream = new MemoryStream())
+ {
+ pkg.SaveAs(stream);
+ using (var reloaded = new ExcelPackage(stream))
+ {
+ Assert.AreEqual(countBefore,
+ reloaded.Workbook.Worksheets[0].HeaderFooter.Pictures.Count,
+ "HeaderFooter.Pictures.Count changed across the round trip.");
+ }
+ }
+ }
+ }
+
+ #region Helpers
+
+ private static string Escape(string s)
+ {
+ return s == null ? "" : s.Replace("\n", "\\n").Replace("\r", "\\r");
+ }
+
+ private static bool Contains(string haystack, string needle)
+ {
+ return haystack != null && haystack.Contains(needle);
+ }
+
+ private static XmlNamespaceManager GetNsm(ExcelWorksheet ws)
+ {
+ var nsm = new XmlNamespaceManager(ws.WorksheetXml.NameTable);
+ nsm.AddNamespace("d", ExcelPackage.schemaMain);
+ return nsm;
+ }
+
+ ///
+ /// Writes a raw string straight into d:headerFooter/d:{nodeName}, so section ordering
+ /// is under the test's control instead of EPPlus's own always-Left-Center-Right
+ /// authoring order - which is what makes these orderings reachable at all.
+ ///
+ private static void SetRawHeaderFooterNode(ExcelWorksheet ws, string nodeName, string rawText)
+ {
+ var nsm = GetNsm(ws);
+ var wsNode = ws.WorksheetXml.SelectSingleNode("d:worksheet", nsm);
+
+ var hfNode = wsNode.SelectSingleNode("d:headerFooter", nsm);
+ if (hfNode == null)
+ {
+ hfNode = ws.WorksheetXml.CreateElement("headerFooter", ExcelPackage.schemaMain);
+ wsNode.AppendChild(hfNode);
+ }
+
+ var node = hfNode.SelectSingleNode("d:" + nodeName, nsm);
+ if (node == null)
+ {
+ node = ws.WorksheetXml.CreateElement(nodeName, ExcelPackage.schemaMain);
+ hfNode.AppendChild(node);
+ }
+ node.InnerText = rawText;
+ }
+
+ ///
+ /// Saves the package to a stream, reloads it, and returns the raw text of the
+ /// requested header/footer node as persisted - i.e. what Excel would read.
+ ///
+ private static string SaveAndReadRawNode(ExcelPackage pkg, string nodeName)
+ {
+ using (var stream = new MemoryStream())
+ {
+ pkg.SaveAs(stream);
+
+ using (var reloaded = new ExcelPackage(stream))
+ {
+ var ws = reloaded.Workbook.Worksheets[0];
+ var nsm = GetNsm(ws);
+ var node = ws.WorksheetXml.SelectSingleNode(
+ "d:worksheet/d:headerFooter/d:" + nodeName, nsm);
+ return node == null ? null : node.InnerText;
+ }
+ }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/EPPlusTest/Drawing/Chart/DataPointsTest.cs b/src/EPPlusTest/Drawing/Chart/DataPointsTest.cs
index 6964621938..9052f04051 100644
--- a/src/EPPlusTest/Drawing/Chart/DataPointsTest.cs
+++ b/src/EPPlusTest/Drawing/Chart/DataPointsTest.cs
@@ -87,9 +87,8 @@ public void PieChart()
var svg = chart.ToSvg();
+ File.WriteAllText($"{_worksheetPath}svg\\EPPlusPieChart1.svg", svg);
//SaveAndCleanup(_pck);
-
- //File.WriteAllText($"{_worksheetPath}svg\\EPPlusPieChart1.svg", svg);
}
[TestMethod]
public void BarChart()
@@ -137,7 +136,7 @@ public void GradientPieChart()
}
}
- [TestMethod]
+ [TestMethod]
public void DataLabelsMultipleOneSeriesExport()
{
using (var pck = OpenPackage("DataLabelsMultipleOneSeriesExport.xlsx", true))
diff --git a/src/EPPlusTest/Export/HtmlExport/RangeExporterTests.cs b/src/EPPlusTest/Export/HtmlExport/RangeExporterTests.cs
index 7ec061fb6a..9a27e3c33d 100644
--- a/src/EPPlusTest/Export/HtmlExport/RangeExporterTests.cs
+++ b/src/EPPlusTest/Export/HtmlExport/RangeExporterTests.cs
@@ -222,9 +222,8 @@ public async Task TaskWriteChartAndShape()
var setting = exporter.Settings.Drawings;
- setting.DrawTypeInclude = eDrawingInclude.Shapes & eDrawingInclude.Shapes;
- setting.Include = ePictureInclude.Include;
- setting.Position = ePicturePosition.Absolute;
+ setting.Include = eDrawingInclude.Include;
+ setting.Position = eDrawingPosition.Absolute;
exporter.Settings.Minify = false;
exporter.Settings.Encoding = Encoding.UTF8;
@@ -253,9 +252,8 @@ public async Task TaskWriteChartSimple()
var setting = exporter.Settings.Drawings;
- setting.DrawTypeInclude = eDrawingInclude.Charts;
- setting.Include = ePictureInclude.Include;
- setting.Position = ePicturePosition.Relative;
+ setting.Include = eDrawingInclude.Include;
+ setting.Position = eDrawingPosition.Relative;
exporter.Settings.Minify = false;
exporter.Settings.Encoding = Encoding.UTF8;
@@ -282,7 +280,7 @@ public async Task WriteImagesAsync()
exporter.Settings.SetColumnWidth = true;
exporter.Settings.SetRowHeight = true;
- exporter.Settings.Pictures.Include = ePictureInclude.Include;
+ exporter.Settings.Drawings.Include = eDrawingInclude.Include;
exporter.Settings.Minify = false;
exporter.Settings.Encoding = Encoding.UTF8;
var html = exporter.GetSinglePage();
@@ -303,7 +301,7 @@ public async Task WriteImagesAsyncHTMLOnlyEmbed()
exporter.Settings.SetColumnWidth = true;
exporter.Settings.SetRowHeight = true;
- exporter.Settings.Pictures.Include = ePictureInclude.IncludeInHtmlOnly;
+ exporter.Settings.Drawings.Include = eDrawingInclude.IncludeInHtmlOnly;
exporter.Settings.Minify = false;
exporter.Settings.Encoding = Encoding.UTF8;
diff --git a/src/EPPlusTest/Export/HtmlExport/SvgShapeExportTests.cs b/src/EPPlusTest/Export/HtmlExport/SvgShapeExportTests.cs
index 9ae2dfb929..74d731f5ed 100644
--- a/src/EPPlusTest/Export/HtmlExport/SvgShapeExportTests.cs
+++ b/src/EPPlusTest/Export/HtmlExport/SvgShapeExportTests.cs
@@ -40,8 +40,7 @@ public void ExportBasicShapeWorksheet()
var exporter = ws.Cells["A1:C20"].CreateHtmlExporter();
- exporter.Settings.Drawings.Include = ePictureInclude.IncludeInHtmlOnly;
- exporter.Settings.Drawings.DrawTypeInclude = eDrawingInclude.Shapes;
+ exporter.Settings.Drawings.Include = eDrawingInclude.IncludeInHtmlOnly;
var htmlPage = exporter.GetSinglePage();
@@ -147,8 +146,7 @@ public void ExportBarChartWithCategories()
var exporter = ws.Cells["A1:C20"].CreateHtmlExporter();
- exporter.Settings.Drawings.Include = ePictureInclude.IncludeInHtmlOnly;
- exporter.Settings.Drawings.DrawTypeInclude = eDrawingInclude.Charts;
+ exporter.Settings.Drawings.Include = eDrawingInclude.IncludeInHtmlOnly;
var htmlPage = exporter.GetSinglePage();
diff --git a/src/EPPlusTest/Export/HtmlExport/TableExporterTests.cs b/src/EPPlusTest/Export/HtmlExport/TableExporterTests.cs
index bf4e961317..83f704699a 100644
--- a/src/EPPlusTest/Export/HtmlExport/TableExporterTests.cs
+++ b/src/EPPlusTest/Export/HtmlExport/TableExporterTests.cs
@@ -497,7 +497,7 @@ public async Task WriteMultipleRangeWithTableAndRange()
exporterRange.Settings.SetRowHeight = true;
exporterRange.Settings.Minify = false;
exporterRange.Settings.TableStyle = eHtmlRangeTableInclude.Include;
- exporterRange.Settings.Pictures.Include = ePictureInclude.Include;
+ exporterRange.Settings.Drawings.Include = eDrawingInclude.Include;
var html1 = exporterRange.GetHtmlString(0);
var html2 = exporterRange.GetHtmlString(1);
@@ -509,7 +509,7 @@ public async Task WriteMultipleRangeWithTableAndRange()
var outputHtml = string.Format("\r\n\r\n\r\n\r\n\r\n{0}
{1}
{2}
{3}
\r\n", html1, html2, html3, html4, css);
- File.WriteAllText("${_htmlOutput}RangeAndThreeTables.html", outputHtml);
+ File.WriteAllText($"{_htmlOutput}RangeAndThreeTables.html", outputHtml);
Assert.AreEqual(css, cssAsync);
}
diff --git a/src/EPPlusTest/Issues/ConditionalFormattingIssues.cs b/src/EPPlusTest/Issues/ConditionalFormattingIssues.cs
index 1130ea6626..6fa34c1bdf 100644
--- a/src/EPPlusTest/Issues/ConditionalFormattingIssues.cs
+++ b/src/EPPlusTest/Issues/ConditionalFormattingIssues.cs
@@ -8,6 +8,8 @@
using System.Drawing;
using System.Globalization;
using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
using System.Threading;
using ColorTranslator = System.Drawing.ColorTranslator;
@@ -264,6 +266,127 @@ public void RoundTrip_ExtIconSetWithNumericFormulaCfvo_DoesNotThrow()
}
}
+
+ [TestMethod]
+ public void ReadEppGeneratedCFBorderStylesCorrectly()
+ {
+ using (var p = OpenPackage("i2488_EppGen.xlsx", true))
+ {
+ var ws = p.Workbook.Worksheets.Add("readBorderNone");
+
+ var notEqual = ws.Cells["C1:C5"].ConditionalFormatting.AddNotEqual();
+
+ notEqual.Formula = "1";
+
+ ws.Cells["D5"].Style.Border.Left.Style = ExcelBorderStyle.None;
+
+ //Set a borderstyle to thick so that Conditional Formatting can then Set it to None
+ ws.Cells["C2"].Style.Border.Left.Style = ExcelBorderStyle.Thick;
+ ws.Cells["C2"].Style.Border.Top.Style = ExcelBorderStyle.Thick;
+ ws.Cells["C2"].Style.Border.Right.Style = ExcelBorderStyle.Thick;
+ ws.Cells["C2"].Style.Border.Bottom.Style = ExcelBorderStyle.Thick;
+
+ notEqual.Style.Border.Left.Style = ExcelBorderStyle.None;
+ notEqual.Style.Border.Top.Style = ExcelBorderStyle.None;
+ notEqual.Style.Border.Right.Style = ExcelBorderStyle.None;
+ notEqual.Style.Border.Bottom.Style = ExcelBorderStyle.None;
+
+ SaveAndCleanup(p);
+ }
+ using (var p = OpenPackage("i2488_EppGen.xlsx", false))
+ {
+ var ws = p.Workbook.Worksheets[0];
+ var cfs = ws.Cells["C1:C5"].ConditionalFormatting.GetConditionalFormattings();
+
+ var rightStyle = cfs[0].Style.Border.Right.Style;
+ Assert.AreEqual(ExcelBorderStyle.None, ws.Cells["D5"].Style.Border.Left.Style);
+ //Assert that we can read borderStyle None. This is different from NoNode and the same as an Empty border node
+ Assert.AreEqual(ExcelBorderStyle.None, cfs[0].Style.Border.Right.Style);
+ Assert.AreEqual(ExcelBorderStyle.None, cfs[0].Style.Border.Left.Style);
+ Assert.AreEqual(ExcelBorderStyle.None, cfs[0].Style.Border.Top.Style);
+ Assert.AreEqual(ExcelBorderStyle.None, cfs[0].Style.Border.Bottom.Style);
+ }
+ }
+
+ ///
+ /// Epplus did not write out ExcelBorderStyle.None correctly into DXF styles
+ /// Causing Cell-Styling to be applied instead of the conditionallyFormatted "None" style
+ ///
+ [TestMethod]
+ public void CopyingIssue2488_ActualCase()
+ {
+ string expectedId = "";
+ var fi = GetOutputFile("", "i2488_out.xlsx");
+ var copiedName = "";
+
+ using (var p = OpenTemplatePackage("i2488.xlsx"))
+ {
+ var wkbook = p.Workbook;
+ var wkSheet = wkbook.Worksheets[0];
+
+ var conditionalFormatting = wkSheet.Cells["S15"].ConditionalFormatting.GetConditionalFormattings().First(cf => cf.Address.Address == "S7:Y22");
+ var dxfId = conditionalFormatting.DxfId;
+ expectedId = conditionalFormatting.Style.ToDxfStyle().Id;
+ Assert.AreEqual(expectedId, wkbook.Styles.Dxfs[dxfId].Id);
+ var sheetName = wkSheet.Name;
+ var copyCount = 1;
+
+ copiedName = $"{sheetName}_{1}";
+
+ for (int i = 1; i <= copyCount; i++)
+ {
+ p.Workbook.Worksheets.Copy(
+ wkSheet.Name, $"{sheetName}_{i}");
+ }
+
+ wkSheet = wkbook.Worksheets[1];
+ sheetName = wkbook.Worksheets[1].Name;
+
+ for (int i = 1; i <= copyCount; i++)
+ {
+ p.Workbook.Worksheets.Copy(
+ wkSheet.Name, $"{sheetName}_{i}");
+ }
+ p.SaveAs(fi);
+ }
+ using (var p = OpenPackage(fi.Name, false))
+ {
+ var wkbook = p.Workbook;
+ var wkSheet = wkbook.Worksheets[copiedName];
+
+ var conditionalFormatting = wkSheet.Cells["S15"].ConditionalFormatting.GetConditionalFormattings().First(cf => cf.Address.Address == "S7:Y22");
+ Assert.AreEqual(expectedId, conditionalFormatting.Style.ToDxfStyle().Id);
+ }
+ }
+
+ [TestMethod]
+ public void CopyingIssue2488_StyleRead()
+ {
+ string expectedId = "";
+ var fi = GetOutputFile("", "i2488_Read_out.xlsx");
+
+ using (var p = OpenTemplatePackage("i2488.xlsx"))
+ {
+ var wkbook = p.Workbook;
+ var wkSheet = wkbook.Worksheets[0];
+
+ var conditionalFormatting = wkSheet.Cells["S15"].ConditionalFormatting.GetConditionalFormattings().First(cf=> cf.Address.Address == "S7:Y22");
+ var dxfId = conditionalFormatting.DxfId;
+ expectedId = conditionalFormatting.Style.ToDxfStyle().Id;
+ Assert.AreEqual(expectedId, wkbook.Styles.Dxfs[dxfId].Id);
+ p.SaveAs(fi);
+ }
+
+ using (var p = OpenPackage(fi.Name, false))
+ {
+ var wkbook = p.Workbook;
+ var wkSheet = wkbook.Worksheets[0];
+
+ var conditionalFormatting = wkSheet.Cells["S15"].ConditionalFormatting.GetConditionalFormattings().First(cf => cf.Address.Address == "S7:Y22");
+ Assert.AreEqual(expectedId, conditionalFormatting.Style.ToDxfStyle().Id);
+ }
+ }
+
[TestMethod]
public void RoundTrip_RegularIconSetWithNumericFormulaCfvo_DoesNotThrow()
{